From f94152de7dd4fa368ba5b32e3e42850e1688e6fe Mon Sep 17 00:00:00 2001 From: dim Date: Sat, 14 May 2016 10:18:27 +0000 Subject: [PATCH] MFC r275385 (by bapt): Sync the svn template with the one from ports MFC r289180 (by peter): Update from svn-1.8.14 to 1.9.2. Formal release notes are available: https://subversion.apache.org/docs/release-notes/1.9.html Of particular note, the client checkout format has *not* changed so upgrades should *not* be required. When reading a repository (file:// or running as a local server), an improved fsfs version 7 is available with significant performance improvements. An optional upgrade is possible to use the new features. Without the upgrade, this is fully read/write compatible with the version 6 fsfs as in svn-1.8. MFC r298845: Update from subversion 1.9.2 to 1.9.4. This contains only bug fixes, no new features. The repository format is also unchanged from 1.9.2. Full list of changes between 1.9.4 and earlier versions: https://svn.apache.org/repos/asf/subversion/tags/1.9.4/CHANGES Note that the two security issues fixed in 1.9.4 (CVE-2016-2167 and CVE-2016-2168) do not affect the version of Subversion in the FreeBSD base system, since neither SASL nor Apache modules are enabled. MFC r298996: Re-sync the FreeBSD-specific Subversion template with the one from ports. Relnotes: yes git-svn-id: svn://svn.freebsd.org/base/stable/10@299742 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- contrib/subversion/.ycm_extra_conf.py | 88 + contrib/subversion/CHANGES | 944 +- contrib/subversion/COMMITTERS | 13 +- contrib/subversion/INSTALL | 333 +- contrib/subversion/LICENSE | 98 + contrib/subversion/Makefile.in | 208 +- contrib/subversion/NOTICE | 7 +- contrib/subversion/autogen.sh | 11 +- contrib/subversion/build-outputs.mk | 1704 +- contrib/subversion/build.conf | 439 +- contrib/subversion/configure | 1604 +- contrib/subversion/configure.ac | 213 +- .../subversion/doc/programmer/gtest-guide.txt | 30 + .../doc/user/svn-best-practices.html | 48 +- contrib/subversion/gen-make.py | 51 +- contrib/subversion/get-deps.sh | 21 +- .../subversion/include/mod_dav_svn.h | 37 +- .../subversion/include/private/svn_atomic.h | 25 - .../include/private/svn_auth_private.h | 10 + .../subversion/include/private/svn_cache.h | 83 +- .../include/private/svn_client_mtcc.h | 226 + .../include/private/svn_client_private.h | 84 +- .../include/private/svn_cmdline_private.h | 18 + .../include/private/svn_delta_private.h | 23 +- .../include/private/svn_dep_compat.h | 88 +- .../include/private/svn_diff_private.h | 6 + .../include/private/svn_diff_tree.h | 11 +- .../subversion/include/private/svn_editor.h | 81 +- .../include/private/svn_error_private.h | 2 +- .../include/private/svn_fs_fs_private.h | 355 + .../include/private/svn_fs_private.h | 17 + .../subversion/include/private/svn_fs_util.h | 25 + .../include/private/svn_io_private.h | 99 +- .../subversion/include/private/svn_log.h | 4 +- .../subversion/include/private/svn_magic.h | 6 +- .../include/private/svn_mergeinfo_private.h | 8 +- .../subversion/include/private/svn_mutex.h | 16 +- .../include/private/svn_named_atomic.h | 162 - .../include/private/svn_object_pool.h | 154 + .../include/private/svn_opt_private.h | 4 - .../include/private/svn_packed_data.h | 255 + .../include/private/svn_pseudo_md5.h | 83 - .../include/private/svn_ra_private.h | 27 + .../include/private/svn_ra_svn_private.h | 142 +- .../include/private/svn_repos_private.h | 278 +- .../include/private/svn_sorts_private.h | 227 + .../subversion/include/private/svn_sqlite.h | 25 +- .../include/private/svn_string_private.h | 130 +- .../include/private/svn_subr_private.h | 389 +- .../include/private/svn_temp_serializer.h | 16 + .../include/private/svn_utf_private.h | 176 +- .../include/private/svn_wc_private.h | 189 +- .../subversion/subversion/include/svn_auth.h | 40 + .../subversion/include/svn_cache_config.h | 5 + .../subversion/include/svn_checksum.h | 12 +- .../subversion/include/svn_client.h | 356 +- .../subversion/include/svn_cmdline.h | 47 +- .../subversion/include/svn_compat.h | 12 + .../subversion/include/svn_config.h | 102 +- .../subversion/subversion/include/svn_delta.h | 25 +- .../subversion/subversion/include/svn_diff.h | 189 +- .../subversion/include/svn_dirent_uri.h | 17 +- .../subversion/subversion/include/svn_error.h | 61 +- .../subversion/include/svn_error_codes.h | 208 +- .../subversion/subversion/include/svn_fs.h | 856 +- .../subversion/subversion/include/svn_hash.h | 1 - .../subversion/subversion/include/svn_io.h | 230 +- .../subversion/subversion/include/svn_iter.h | 2 +- .../subversion/include/svn_mergeinfo.h | 103 +- .../subversion/subversion/include/svn_opt.h | 10 +- .../subversion/subversion/include/svn_path.h | 12 +- .../subversion/subversion/include/svn_props.h | 13 +- .../subversion/subversion/include/svn_ra.h | 120 +- .../subversion/include/svn_ra_svn.h | 36 +- .../subversion/subversion/include/svn_repos.h | 826 +- .../subversion/subversion/include/svn_sorts.h | 78 +- .../subversion/include/svn_string.h | 91 +- .../subversion/subversion/include/svn_types.h | 79 +- .../subversion/include/svn_version.h | 71 +- .../subversion/subversion/include/svn_wc.h | 287 +- .../subversion/subversion/include/svn_x509.h | 201 + .../subversion/subversion/include/svn_xml.h | 4 +- .../libsvn_auth_gnome_keyring.pc.in | 12 + .../libsvn_auth_kwallet/kwallet.cpp | 65 +- .../libsvn_auth_kwallet.pc.in | 12 + .../subversion/subversion/libsvn_client/add.c | 143 +- .../subversion/libsvn_client/blame.c | 307 +- .../subversion/subversion/libsvn_client/cat.c | 67 +- .../subversion/libsvn_client/checkout.c | 81 +- .../subversion/libsvn_client/cleanup.c | 228 +- .../subversion/libsvn_client/client.h | 210 +- .../subversion/libsvn_client/cmdline.c | 15 +- .../subversion/libsvn_client/commit.c | 266 +- .../subversion/libsvn_client/commit_util.c | 87 +- .../libsvn_client/compat_providers.c | 4 + .../subversion/libsvn_client/copy.c | 1035 +- .../subversion/libsvn_client/copy_foreign.c | 20 +- .../subversion/subversion/libsvn_client/ctx.c | 39 +- .../subversion/libsvn_client/delete.c | 21 +- .../subversion/libsvn_client/deprecated.c | 135 +- .../subversion/libsvn_client/diff.c | 2066 ++- .../subversion/libsvn_client/diff_local.c | 1077 +- .../subversion/libsvn_client/diff_summarize.c | 338 +- .../subversion/libsvn_client/export.c | 8 +- .../subversion/libsvn_client/externals.c | 174 +- .../subversion/libsvn_client/import.c | 191 +- .../subversion/libsvn_client/info.c | 120 +- .../subversion/libsvn_client/iprops.c | 6 +- .../libsvn_client/libsvn_client.pc.in | 12 + .../subversion/libsvn_client/list.c | 120 +- .../libsvn_client/locking_commands.c | 333 +- .../subversion/subversion/libsvn_client/log.c | 58 +- .../subversion/libsvn_client/merge.c | 596 +- .../subversion/libsvn_client/mergeinfo.c | 85 +- .../subversion/libsvn_client/mergeinfo.h | 6 +- .../subversion/libsvn_client/mtcc.c | 1429 ++ .../subversion/libsvn_client/patch.c | 320 +- .../subversion/libsvn_client/prop_commands.c | 127 +- .../subversion/subversion/libsvn_client/ra.c | 127 +- .../subversion/libsvn_client/relocate.c | 125 +- .../subversion/libsvn_client/repos_diff.c | 15 +- .../subversion/libsvn_client/resolved.c | 8 +- .../subversion/libsvn_client/revert.c | 46 +- .../subversion/libsvn_client/revisions.c | 1 + .../subversion/libsvn_client/status.c | 81 +- .../subversion/libsvn_client/switch.c | 10 +- .../subversion/libsvn_client/update.c | 168 +- .../subversion/libsvn_client/upgrade.c | 16 +- .../subversion/libsvn_client/util.c | 12 +- .../subversion/libsvn_delta/compat.c | 95 +- .../subversion/libsvn_delta/compose_delta.c | 48 +- .../subversion/libsvn_delta/debug_editor.c | 33 +- .../subversion/libsvn_delta/debug_editor.h | 7 +- .../subversion/libsvn_delta/editor.c | 77 +- .../libsvn_delta/libsvn_delta.pc.in | 12 + .../subversion/libsvn_delta/path_driver.c | 11 +- .../subversion/libsvn_delta/svndiff.c | 375 +- .../subversion/libsvn_delta/text_delta.c | 114 +- .../subversion/libsvn_delta/xdelta.c | 142 +- .../subversion/libsvn_diff/binary_diff.c | 221 + .../subversion/libsvn_diff/deprecated.c | 175 + .../subversion/subversion/libsvn_diff/diff4.c | 2 +- .../subversion/libsvn_diff/diff_file.c | 198 +- .../subversion/libsvn_diff/diff_memory.c | 204 +- .../subversion/subversion/libsvn_diff/lcs.c | 2 +- .../subversion/libsvn_diff/libsvn_diff.pc.in | 12 + .../subversion/libsvn_diff/parse-diff.c | 223 +- .../subversion/subversion/libsvn_diff/util.c | 43 +- .../subversion/subversion/libsvn_fs/access.c | 7 - .../subversion/libsvn_fs/deprecated.c | 90 + .../subversion/subversion/libsvn_fs/editor.c | 60 +- .../subversion/libsvn_fs/fs-loader.c | 735 +- .../subversion/libsvn_fs/fs-loader.h | 136 +- .../subversion/libsvn_fs/libsvn_fs.pc.in | 12 + .../libsvn_fs_base/bdb/changes-table.c | 81 +- .../libsvn_fs_base/bdb/locks-table.c | 2 +- .../libsvn_fs_base/bdb/strings-table.c | 4 +- .../subversion/libsvn_fs_base/dag.c | 29 +- .../subversion/libsvn_fs_base/dag.h | 2 +- .../subversion/subversion/libsvn_fs_base/fs.c | 149 +- .../subversion/subversion/libsvn_fs_base/fs.h | 6 +- .../subversion/subversion/libsvn_fs_base/id.c | 7 +- .../subversion/subversion/libsvn_fs_base/id.h | 6 +- .../subversion/libsvn_fs_base/key-gen.c | 36 +- .../subversion/libsvn_fs_base/key-gen.h | 7 - .../libsvn_fs_base/libsvn_fs_base.pc.in | 12 + .../subversion/libsvn_fs_base/lock.c | 148 +- .../subversion/libsvn_fs_base/lock.h | 27 +- .../subversion/libsvn_fs_base/reps-strings.c | 5 +- .../subversion/libsvn_fs_base/revs-txns.c | 45 +- .../subversion/libsvn_fs_base/tree.c | 177 +- .../subversion/libsvn_fs_fs/cached_data.c | 3387 ++++ .../subversion/libsvn_fs_fs/cached_data.h | 166 + .../subversion/libsvn_fs_fs/caching.c | 356 +- .../subversion/subversion/libsvn_fs_fs/dag.c | 207 +- .../subversion/subversion/libsvn_fs_fs/dag.h | 68 +- .../subversion/libsvn_fs_fs/dump-index.c | 90 + .../subversion/subversion/libsvn_fs_fs/fs.c | 300 +- .../subversion/subversion/libsvn_fs_fs/fs.h | 261 +- .../subversion/libsvn_fs_fs/fs_fs.c | 12240 ++------------ .../subversion/libsvn_fs_fs/fs_fs.h | 500 +- .../subversion/libsvn_fs_fs/hotcopy.c | 1097 ++ .../subversion/libsvn_fs_fs/hotcopy.h | 51 + .../subversion/subversion/libsvn_fs_fs/id.c | 620 +- .../subversion/subversion/libsvn_fs_fs/id.h | 89 +- .../subversion/libsvn_fs_fs/index.c | 3470 ++++ .../subversion/libsvn_fs_fs/index.h | 356 + .../subversion/libsvn_fs_fs/key-gen.c | 159 - .../subversion/libsvn_fs_fs/key-gen.h | 91 - .../libsvn_fs_fs/libsvn_fs_fs.pc.in | 12 + .../subversion/libsvn_fs_fs/load-index.c | 98 + .../subversion/subversion/libsvn_fs_fs/lock.c | 944 +- .../subversion/subversion/libsvn_fs_fs/lock.h | 25 +- .../subversion/libsvn_fs_fs/low_level.c | 1196 ++ .../subversion/libsvn_fs_fs/low_level.h | 230 + .../subversion/subversion/libsvn_fs_fs/pack.c | 2039 +++ .../subversion/subversion/libsvn_fs_fs/pack.h | 64 + .../subversion/libsvn_fs_fs/recovery.c | 509 + .../subversion/libsvn_fs_fs/recovery.h | 36 + .../subversion/libsvn_fs_fs/rep-cache-db.h | 10 +- .../subversion/libsvn_fs_fs/rep-cache-db.sql | 3 + .../subversion/libsvn_fs_fs/rep-cache.c | 132 +- .../subversion/libsvn_fs_fs/rep-cache.h | 22 +- .../subversion/libsvn_fs_fs/rev_file.c | 306 + .../subversion/libsvn_fs_fs/rev_file.h | 145 + .../subversion/libsvn_fs_fs/revprops.c | 1381 ++ .../subversion/libsvn_fs_fs/revprops.h | 159 + .../subversion/libsvn_fs_fs/stats.c | 1255 ++ .../subversion/libsvn_fs_fs/structure | 235 +- .../subversion/libsvn_fs_fs/structure-indexes | 352 + .../subversion/libsvn_fs_fs/temp_serializer.c | 431 +- .../subversion/libsvn_fs_fs/temp_serializer.h | 57 +- .../subversion/libsvn_fs_fs/transaction.c | 3858 +++++ .../subversion/libsvn_fs_fs/transaction.h | 294 + .../subversion/subversion/libsvn_fs_fs/tree.c | 1155 +- .../subversion/subversion/libsvn_fs_fs/tree.h | 17 +- .../subversion/subversion/libsvn_fs_fs/util.c | 694 + .../subversion/subversion/libsvn_fs_fs/util.h | 408 + .../subversion/libsvn_fs_fs/verify.c | 883 + .../subversion/libsvn_fs_fs/verify.h | 42 + .../subversion/libsvn_fs_util/fs-util.c | 117 +- .../libsvn_fs_util/libsvn_fs_util.pc.in | 12 + .../subversion/subversion/libsvn_fs_x/TODO | 270 + .../subversion/libsvn_fs_x/cached_data.c | 3355 ++++ .../subversion/libsvn_fs_x/cached_data.h | 180 + .../subversion/libsvn_fs_x/caching.c | 725 + .../subversion/libsvn_fs_x/changes.c | 536 + .../subversion/libsvn_fs_x/changes.h | 132 + .../subversion/subversion/libsvn_fs_x/dag.c | 1368 ++ .../subversion/subversion/libsvn_fs_x/dag.h | 580 + .../subversion/subversion/libsvn_fs_x/fs.c | 669 + .../subversion/subversion/libsvn_fs_x/fs.h | 574 + .../subversion/subversion/libsvn_fs_x/fs_id.c | 319 + .../subversion/subversion/libsvn_fs_x/fs_id.h | 62 + .../subversion/subversion/libsvn_fs_x/fs_x.c | 1228 ++ .../subversion/subversion/libsvn_fs_x/fs_x.h | 202 + .../subversion/libsvn_fs_x/hotcopy.c | 991 ++ .../subversion/libsvn_fs_x/hotcopy.h | 53 + .../subversion/subversion/libsvn_fs_x/id.c | 198 + .../subversion/subversion/libsvn_fs_x/id.h | 135 + .../subversion/subversion/libsvn_fs_x/index.c | 3981 +++++ .../subversion/subversion/libsvn_fs_x/index.h | 411 + .../subversion/libsvn_fs_x/libsvn_fs_x.pc.in | 12 + .../subversion/subversion/libsvn_fs_x/lock.c | 1492 ++ .../subversion/subversion/libsvn_fs_x/lock.h | 116 + .../subversion/libsvn_fs_x/low_level.c | 1123 ++ .../subversion/libsvn_fs_x/low_level.h | 214 + .../subversion/libsvn_fs_x/noderevs.c | 912 ++ .../subversion/libsvn_fs_x/noderevs.h | 142 + .../subversion/subversion/libsvn_fs_x/pack.c | 2324 +++ .../subversion/subversion/libsvn_fs_x/pack.h | 65 + .../subversion/libsvn_fs_x/recovery.c | 263 + .../subversion/libsvn_fs_x/recovery.h | 37 + .../subversion/libsvn_fs_x/rep-cache-db.h | 92 + .../subversion/libsvn_fs_x/rep-cache-db.sql | 70 + .../subversion/libsvn_fs_x/rep-cache.c | 416 + .../subversion/libsvn_fs_x/rep-cache.h | 105 + .../subversion/subversion/libsvn_fs_x/reps.c | 948 ++ .../subversion/subversion/libsvn_fs_x/reps.h | 190 + .../subversion/libsvn_fs_x/rev_file.c | 316 + .../subversion/libsvn_fs_x/rev_file.h | 154 + .../subversion/libsvn_fs_x/revprops.c | 1948 +++ .../subversion/libsvn_fs_x/revprops.h | 184 + .../subversion/libsvn_fs_x/string_table.c | 904 ++ .../subversion/libsvn_fs_x/string_table.h | 133 + .../subversion/libsvn_fs_x/structure | 336 + .../subversion/libsvn_fs_x/temp_serializer.c | 1337 ++ .../subversion/libsvn_fs_x/temp_serializer.h | 301 + .../subversion/libsvn_fs_x/transaction.c | 3782 +++++ .../subversion/libsvn_fs_x/transaction.h | 316 + .../subversion/subversion/libsvn_fs_x/tree.c | 4542 ++++++ .../subversion/subversion/libsvn_fs_x/tree.h | 112 + .../subversion/subversion/libsvn_fs_x/util.c | 777 + .../subversion/subversion/libsvn_fs_x/util.h | 476 + .../subversion/libsvn_fs_x/verify.c | 850 + .../subversion/libsvn_fs_x/verify.h | 43 + .../subversion/subversion/libsvn_ra/compat.c | 26 +- .../subversion/libsvn_ra/libsvn_ra.pc.in | 12 + .../subversion/libsvn_ra/ra_loader.c | 418 +- .../subversion/libsvn_ra/ra_loader.h | 10 +- .../subversion/libsvn_ra/wrapper_template.h | 4 +- .../libsvn_ra_local/libsvn_ra_local.pc.in | 12 + .../subversion/libsvn_ra_local/ra_local.h | 5 + .../subversion/libsvn_ra_local/ra_plugin.c | 429 +- .../subversion/libsvn_ra_local/split_url.c | 6 +- .../subversion/libsvn_ra_serf/README | 2 +- .../subversion/libsvn_ra_serf/blame.c | 50 +- .../subversion/libsvn_ra_serf/blncache.c | 6 +- .../subversion/libsvn_ra_serf/blncache.h | 4 +- .../subversion/libsvn_ra_serf/commit.c | 1309 +- .../subversion/libsvn_ra_serf/eagain_bucket.c | 122 + .../libsvn_ra_serf/get_deleted_rev.c | 18 +- .../subversion/libsvn_ra_serf/get_file.c | 425 + .../subversion/libsvn_ra_serf/get_lock.c | 337 + .../subversion/libsvn_ra_serf/getdate.c | 33 +- .../subversion/libsvn_ra_serf/getlocations.c | 32 +- .../libsvn_ra_serf/getlocationsegments.c | 36 +- .../subversion/libsvn_ra_serf/getlocks.c | 50 +- .../libsvn_ra_serf/inherited_props.c | 199 +- .../libsvn_ra_serf/libsvn_ra_serf.pc.in | 12 + .../subversion/libsvn_ra_serf/lock.c | 679 + .../subversion/libsvn_ra_serf/locks.c | 669 - .../subversion/libsvn_ra_serf/log.c | 83 +- .../subversion/libsvn_ra_serf/merge.c | 82 +- .../subversion/libsvn_ra_serf/mergeinfo.c | 27 +- .../subversion/libsvn_ra_serf/multistatus.c | 750 + .../subversion/libsvn_ra_serf/options.c | 148 +- .../subversion/libsvn_ra_serf/property.c | 760 +- .../subversion/libsvn_ra_serf/ra_serf.h | 714 +- .../subversion/libsvn_ra_serf/replay.c | 955 +- .../subversion/libsvn_ra_serf/serf.c | 883 +- .../subversion/libsvn_ra_serf/stat.c | 615 + .../subversion/libsvn_ra_serf/update.c | 4078 ++--- .../subversion/libsvn_ra_serf/util.c | 1705 +- .../subversion/libsvn_ra_serf/util_error.c | 3 +- .../subversion/libsvn_ra_serf/xml.c | 526 +- .../subversion/libsvn_ra_svn/client.c | 336 +- .../subversion/libsvn_ra_svn/cram.c | 2 +- .../subversion/libsvn_ra_svn/cyrus_auth.c | 39 +- .../subversion/libsvn_ra_svn/deprecated.c | 50 + .../subversion/libsvn_ra_svn/editorp.c | 120 +- .../subversion/libsvn_ra_svn/internal_auth.c | 2 +- .../libsvn_ra_svn/libsvn_ra_svn.pc.in | 12 + .../subversion/libsvn_ra_svn/marshal.c | 902 +- .../subversion/libsvn_ra_svn/protocol | 22 +- .../subversion/libsvn_ra_svn/ra_svn.h | 29 +- .../subversion/libsvn_ra_svn/streams.c | 149 +- .../subversion/libsvn_repos/authz.c | 81 +- .../subversion/libsvn_repos/authz_pool.c | 226 + .../subversion/libsvn_repos/commit.c | 52 +- .../subversion/libsvn_repos/config_pool.c | 531 + .../subversion/libsvn_repos/delta.c | 119 +- .../subversion/libsvn_repos/deprecated.c | 109 + .../subversion/subversion/libsvn_repos/dump.c | 1593 +- .../subversion/libsvn_repos/fs-wrap.c | 369 +- .../subversion/libsvn_repos/hooks.c | 55 +- .../libsvn_repos/libsvn_repos.pc.in | 12 + .../subversion/libsvn_repos/load-fs-vtable.c | 290 +- .../subversion/subversion/libsvn_repos/load.c | 32 +- .../subversion/subversion/libsvn_repos/log.c | 634 +- .../subversion/libsvn_repos/replay.c | 60 +- .../subversion/libsvn_repos/reporter.c | 72 +- .../subversion/libsvn_repos/repos.c | 780 +- .../subversion/libsvn_repos/repos.h | 5 + .../subversion/libsvn_repos/rev_hunt.c | 258 +- .../subversion/libsvn_subr/adler32.c | 22 +- .../subversion/subversion/libsvn_subr/auth.c | 317 +- .../subversion/subversion/libsvn_subr/auth.h | 124 + .../subversion/libsvn_subr/bit_array.c | 194 + .../subversion/libsvn_subr/cache-inprocess.c | 80 +- .../subversion/libsvn_subr/cache-membuffer.c | 1974 ++- .../subversion/libsvn_subr/cache-memcache.c | 54 +- .../subversion/subversion/libsvn_subr/cache.c | 68 +- .../subversion/subversion/libsvn_subr/cache.h | 10 + .../subversion/libsvn_subr/cache_config.c | 11 +- .../subversion/libsvn_subr/checksum.c | 411 +- .../libsvn_subr/{md5.h => checksum.h} | 35 +- .../subversion/libsvn_subr/cmdline.c | 435 +- .../subversion/libsvn_subr/compat.c | 30 +- .../subversion/libsvn_subr/compress.c | 257 + .../subversion/libsvn_subr/config.c | 150 +- .../subversion/libsvn_subr/config_auth.c | 29 +- .../subversion/libsvn_subr/config_file.c | 177 +- .../subversion/libsvn_subr/config_impl.h | 14 +- .../subversion/libsvn_subr/config_keys.inc | 77 + .../subversion/libsvn_subr/config_win.c | 95 +- .../subversion/subversion/libsvn_subr/ctype.c | 17 + .../subversion/subversion/libsvn_subr/debug.c | 6 +- .../subversion/libsvn_subr/deprecated.c | 231 +- .../subversion/libsvn_subr/dirent_uri.c | 94 +- .../subversion/subversion/libsvn_subr/dso.c | 8 + .../subversion/subversion/libsvn_subr/eol.c | 23 +- .../subversion/subversion/libsvn_subr/error.c | 120 +- .../subversion/libsvn_subr/errorcode.inc | 271 + .../subversion/subversion/libsvn_subr/fnv1a.c | 246 + .../subversion/subversion/libsvn_subr/fnv1a.h | 91 + .../subversion/libsvn_subr/gpg_agent.c | 50 +- .../subversion/subversion/libsvn_subr/hash.c | 291 +- .../libsvn_subr/internal_statements.h | 2 +- .../subversion/subversion/libsvn_subr/io.c | 907 +- .../subversion/subversion/libsvn_subr/iter.c | 15 +- .../subversion/libsvn_subr/libsvn_subr.pc.in | 12 + .../subversion/subversion/libsvn_subr/log.c | 35 +- .../subversion/libsvn_subr/macos_keychain.c | 5 +- .../subversion/subversion/libsvn_subr/magic.c | 24 +- .../subversion/subversion/libsvn_subr/md5.c | 66 +- .../subversion/libsvn_subr/mergeinfo.c | 192 +- .../subversion/subversion/libsvn_subr/mutex.c | 46 +- .../subversion/libsvn_subr/named_atomic.c | 665 - .../subversion/subversion/libsvn_subr/nls.c | 79 +- .../subversion/libsvn_subr/object_pool.c | 398 + .../subversion/subversion/libsvn_subr/opt.c | 13 +- .../subversion/libsvn_subr/packed_data.c | 1099 ++ .../subversion/subversion/libsvn_subr/path.c | 12 +- .../subversion/subversion/libsvn_subr/pool.c | 2 +- .../subversion/libsvn_subr/prefix_string.c | 315 + .../subversion/libsvn_subr/prompt.c | 17 +- .../subversion/libsvn_subr/pseudo_md5.c | 422 - .../subversion/libsvn_subr/root_pools.c | 110 + .../subversion/subversion/libsvn_subr/sha1.c | 82 - .../subversion/subversion/libsvn_subr/sha1.h | 70 - .../subversion/libsvn_subr/simple_providers.c | 23 +- .../subversion/subversion/libsvn_subr/sorts.c | 233 +- .../subversion/libsvn_subr/spillbuf.c | 136 +- .../subversion/libsvn_subr/sqlite.c | 373 +- .../subversion/libsvn_subr/sqlite3wrapper.c | 14 +- .../libsvn_subr/ssl_client_cert_providers.c | 13 +- .../ssl_client_cert_pw_providers.c | 16 +- .../libsvn_subr/ssl_server_trust_providers.c | 27 +- .../subversion/libsvn_subr/stream.c | 800 +- .../subversion/libsvn_subr/string.c | 357 +- .../subversion/subversion/libsvn_subr/subst.c | 100 +- .../subversion/libsvn_subr/sysinfo.c | 264 +- .../subversion/libsvn_subr/sysinfo.h | 11 + .../subversion/libsvn_subr/temp_serializer.c | 22 +- .../subversion/subversion/libsvn_subr/time.c | 22 +- .../subversion/subversion/libsvn_subr/types.c | 46 +- .../libsvn_subr/username_providers.c | 11 +- .../subversion/subversion/libsvn_subr/utf.c | 338 +- .../subversion/libsvn_subr/utf8proc.c | 530 + .../subversion/libsvn_subr/utf8proc/LICENSE | 64 + .../subversion/libsvn_subr/utf8proc/README | 116 + .../libsvn_subr/utf8proc/utf8proc.c | 611 + .../libsvn_subr/utf8proc/utf8proc.h | 447 + .../libsvn_subr/utf8proc/utf8proc_data.c | 13388 ++++++++++++++++ .../subversion/libsvn_subr/utf_validate.c | 79 +- .../subversion/libsvn_subr/utf_width.c | 2 +- .../subversion/libsvn_subr/version.c | 10 +- .../subversion/libsvn_subr/win32_crashrpt.c | 35 +- .../subversion/libsvn_subr/win32_crypto.c | 7 +- .../subversion/libsvn_subr/win32_xlate.c | 29 +- .../subversion/libsvn_subr/win32_xlate.h | 22 +- .../subversion/subversion/libsvn_subr/x509.h | 134 + .../subversion/libsvn_subr/x509info.c | 332 + .../subversion/libsvn_subr/x509parse.c | 1200 ++ .../subversion/subversion/libsvn_subr/xml.c | 23 + .../subversion/libsvn_wc/adm_crawler.c | 68 +- .../subversion/libsvn_wc/adm_files.c | 47 +- .../subversion/libsvn_wc/adm_files.h | 24 - .../subversion/subversion/libsvn_wc/adm_ops.c | 461 +- .../subversion/subversion/libsvn_wc/cleanup.c | 82 +- .../subversion/libsvn_wc/conflicts.c | 2054 +-- .../subversion/libsvn_wc/conflicts.h | 3 + .../subversion/subversion/libsvn_wc/copy.c | 182 +- .../subversion/subversion/libsvn_wc/crop.c | 186 +- .../subversion/subversion/libsvn_wc/delete.c | 6 +- .../subversion/libsvn_wc/deprecated.c | 304 +- .../subversion/subversion/libsvn_wc/diff.h | 4 +- .../subversion/libsvn_wc/diff_editor.c | 163 +- .../subversion/libsvn_wc/diff_local.c | 160 +- .../subversion/subversion/libsvn_wc/entries.c | 536 +- .../subversion/libsvn_wc/externals.c | 118 +- .../subversion/subversion/libsvn_wc/info.c | 163 +- .../subversion/libsvn_wc/libsvn_wc.pc.in | 12 + .../subversion/subversion/libsvn_wc/lock.c | 12 +- .../subversion/subversion/libsvn_wc/merge.c | 40 +- .../subversion/subversion/libsvn_wc/node.c | 473 +- .../subversion/libsvn_wc/old-and-busted.c | 4 +- .../subversion/subversion/libsvn_wc/props.c | 281 +- .../subversion/subversion/libsvn_wc/props.h | 8 +- .../subversion/libsvn_wc/questions.c | 195 +- .../subversion/libsvn_wc/relocate.c | 2 +- .../subversion/subversion/libsvn_wc/revert.c | 385 +- .../subversion/libsvn_wc/revision_status.c | 8 +- .../subversion/subversion/libsvn_wc/status.c | 476 +- .../subversion/libsvn_wc/token-map.h | 11 + .../subversion/libsvn_wc/translate.c | 42 +- .../subversion/libsvn_wc/tree_conflicts.c | 37 +- .../subversion/libsvn_wc/update_editor.c | 1144 +- .../subversion/subversion/libsvn_wc/upgrade.c | 323 +- .../subversion/subversion/libsvn_wc/util.c | 212 +- .../subversion/libsvn_wc/wc-checks.h | 177 +- .../subversion/libsvn_wc/wc-checks.sql | 214 + .../subversion/libsvn_wc/wc-metadata.h | 2 +- .../subversion/libsvn_wc/wc-metadata.sql | 14 +- .../subversion/libsvn_wc/wc-queries.h | 1963 ++- .../subversion/libsvn_wc/wc-queries.sql | 466 +- contrib/subversion/subversion/libsvn_wc/wc.h | 84 +- .../subversion/subversion/libsvn_wc/wc_db.c | 4792 +++--- .../subversion/subversion/libsvn_wc/wc_db.h | 446 +- .../subversion/libsvn_wc/wc_db_pristine.c | 222 +- .../subversion/libsvn_wc/wc_db_private.h | 229 +- .../subversion/libsvn_wc/wc_db_update_move.c | 2866 ++-- .../subversion/libsvn_wc/wc_db_util.c | 66 +- .../subversion/libsvn_wc/wc_db_wcroot.c | 84 +- .../subversion/libsvn_wc/workqueue.c | 93 +- .../subversion/libsvn_wc/workqueue.h | 22 +- contrib/subversion/subversion/svn/add-cmd.c | 2 +- contrib/subversion/subversion/svn/auth-cmd.c | 483 + contrib/subversion/subversion/svn/blame-cmd.c | 55 +- contrib/subversion/subversion/svn/cat-cmd.c | 7 +- .../subversion/svn/changelist-cmd.c | 24 +- .../subversion/subversion/svn/checkout-cmd.c | 12 + .../subversion/subversion/svn/cl-conflicts.c | 84 +- .../subversion/subversion/svn/cl-conflicts.h | 10 + contrib/subversion/subversion/svn/cl-log.h | 105 + contrib/subversion/subversion/svn/cl.h | 109 +- .../subversion/subversion/svn/cleanup-cmd.c | 66 +- .../subversion/subversion/svn/client_errors.h | 97 - .../subversion/subversion/svn/commit-cmd.c | 4 +- .../subversion/svn/conflict-callbacks.c | 513 +- contrib/subversion/subversion/svn/copy-cmd.c | 8 +- contrib/subversion/subversion/svn/diff-cmd.c | 58 +- .../subversion/subversion/svn/export-cmd.c | 10 +- .../subversion/subversion/svn/file-merge.c | 19 +- contrib/subversion/subversion/svn/help-cmd.c | 51 +- contrib/subversion/subversion/svn/info-cmd.c | 429 +- contrib/subversion/subversion/svn/list-cmd.c | 13 +- contrib/subversion/subversion/svn/lock-cmd.c | 39 +- contrib/subversion/subversion/svn/log-cmd.c | 158 +- contrib/subversion/subversion/svn/merge-cmd.c | 4 - .../subversion/subversion/svn/mergeinfo-cmd.c | 157 +- contrib/subversion/subversion/svn/notify.c | 783 +- .../subversion/subversion/svn/propget-cmd.c | 69 +- .../subversion/subversion/svn/proplist-cmd.c | 8 +- contrib/subversion/subversion/svn/props.c | 159 +- .../subversion/subversion/svn/resolve-cmd.c | 2 +- .../subversion/subversion/svn/revert-cmd.c | 7 +- .../subversion/subversion/svn/similarity.c | 126 + .../subversion/subversion/svn/status-cmd.c | 30 +- contrib/subversion/subversion/svn/status.c | 65 +- contrib/subversion/subversion/svn/svn.c | 796 +- .../subversion/subversion/svn/unlock-cmd.c | 40 +- contrib/subversion/subversion/svn/util.c | 72 +- .../subversion/svn_private_config.h.in | 41 +- .../subversion/svn_private_config.hw | 23 +- .../subversion/subversion/svnadmin/svnadmin.c | 873 +- contrib/subversion/subversion/svnbench/cl.h | 203 + .../subversion/subversion/svnbench/help-cmd.c | 90 + .../subversion/subversion/svnbench/notify.c | 1045 ++ .../subversion/svnbench/null-blame-cmd.c | 276 + .../subversion/svnbench/null-export-cmd.c | 346 + .../subversion/svnbench/null-info-cmd.c | 287 + .../subversion/svnbench/null-list-cmd.c | 169 + .../subversion/svnbench/null-log-cmd.c | 243 + .../subversion/subversion/svnbench/svnbench.c | 1005 ++ contrib/subversion/subversion/svnbench/util.c | 92 + .../subversion/svndumpfilter/svndumpfilter.c | 408 +- .../subversion/svnfsfs/dump-index-cmd.c | 106 + .../subversion/svnfsfs/load-index-cmd.c | 193 + .../subversion/subversion/svnfsfs/stats-cmd.c | 509 + .../subversion/subversion/svnfsfs/svnfsfs.c | 541 + .../subversion/subversion/svnfsfs/svnfsfs.h | 73 + .../subversion/subversion/svnlook/svnlook.c | 312 +- .../subversion/subversion/svnmucc/svnmucc.c | 1252 +- .../subversion/svnrdump/dump_editor.c | 532 +- .../subversion/svnrdump/load_editor.c | 309 +- .../subversion/subversion/svnrdump/svnrdump.c | 403 +- .../subversion/subversion/svnrdump/svnrdump.h | 1 + contrib/subversion/subversion/svnrdump/util.c | 4 +- .../subversion/svnserve/cyrus_auth.c | 49 +- .../subversion/subversion/svnserve/logger.c | 161 + .../subversion/subversion/svnserve/logger.h | 79 + .../subversion/subversion/svnserve/serve.c | 1497 +- .../subversion/subversion/svnserve/server.h | 145 +- .../subversion/subversion/svnserve/svnserve.c | 711 +- .../subversion/subversion/svnsync/svnsync.c | 383 +- contrib/subversion/subversion/svnsync/sync.c | 4 +- .../subversion/svnversion/svnversion.c | 168 +- contrib/subversion/win-tests.py | 481 +- usr.bin/svn/Makefile | 6 +- usr.bin/svn/Makefile.inc | 4 +- usr.bin/svn/lib/Makefile | 2 +- usr.bin/svn/lib/libsvn_client/Makefile | 2 +- usr.bin/svn/lib/libsvn_diff/Makefile | 5 +- usr.bin/svn/lib/libsvn_fs/Makefile | 2 +- usr.bin/svn/lib/libsvn_fs_fs/Makefile | 7 +- usr.bin/svn/lib/libsvn_fs_x/Makefile | 24 + usr.bin/svn/lib/libsvn_ra_serf/Makefile | 9 +- usr.bin/svn/lib/libsvn_repos/Makefile | 7 +- usr.bin/svn/lib/libsvn_subr/Makefile | 30 +- usr.bin/svn/svn/Makefile | 20 +- usr.bin/svn/svn_private_config.h | 50 +- usr.bin/svn/svnadmin/Makefile | 1 + usr.bin/svn/svnbench/Makefile | 50 + usr.bin/svn/svndumpfilter/Makefile | 1 + usr.bin/svn/svnfsfs/Makefile | 38 + usr.bin/svn/svnlook/Makefile | 1 + usr.bin/svn/svnmucc/Makefile | 3 + usr.bin/svn/svnrdump/Makefile | 1 + usr.bin/svn/svnserve/Makefile | 3 +- usr.bin/svn/svnsync/Makefile | 1 + 582 files changed, 152478 insertions(+), 48269 deletions(-) create mode 100644 contrib/subversion/.ycm_extra_conf.py create mode 100644 contrib/subversion/doc/programmer/gtest-guide.txt create mode 100644 contrib/subversion/subversion/include/private/svn_client_mtcc.h create mode 100644 contrib/subversion/subversion/include/private/svn_fs_fs_private.h delete mode 100644 contrib/subversion/subversion/include/private/svn_named_atomic.h create mode 100644 contrib/subversion/subversion/include/private/svn_object_pool.h create mode 100644 contrib/subversion/subversion/include/private/svn_packed_data.h delete mode 100644 contrib/subversion/subversion/include/private/svn_pseudo_md5.h create mode 100644 contrib/subversion/subversion/include/private/svn_sorts_private.h create mode 100644 contrib/subversion/subversion/include/svn_x509.h create mode 100644 contrib/subversion/subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring.pc.in create mode 100644 contrib/subversion/subversion/libsvn_auth_kwallet/libsvn_auth_kwallet.pc.in create mode 100644 contrib/subversion/subversion/libsvn_client/libsvn_client.pc.in create mode 100644 contrib/subversion/subversion/libsvn_client/mtcc.c create mode 100644 contrib/subversion/subversion/libsvn_delta/libsvn_delta.pc.in create mode 100644 contrib/subversion/subversion/libsvn_diff/binary_diff.c create mode 100644 contrib/subversion/subversion/libsvn_diff/libsvn_diff.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs/deprecated.c create mode 100644 contrib/subversion/subversion/libsvn_fs/libsvn_fs.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs_base/libsvn_fs_base.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/cached_data.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/cached_data.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/dump-index.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/hotcopy.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/hotcopy.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/index.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/index.h delete mode 100644 contrib/subversion/subversion/libsvn_fs_fs/key-gen.c delete mode 100644 contrib/subversion/subversion/libsvn_fs_fs/key-gen.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/libsvn_fs_fs.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/load-index.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/low_level.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/low_level.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/pack.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/pack.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/recovery.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/recovery.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/rev_file.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/rev_file.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/revprops.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/revprops.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/stats.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/structure-indexes create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/transaction.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/transaction.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/util.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/util.h create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/verify.c create mode 100644 contrib/subversion/subversion/libsvn_fs_fs/verify.h create mode 100644 contrib/subversion/subversion/libsvn_fs_util/libsvn_fs_util.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs_x/TODO create mode 100644 contrib/subversion/subversion/libsvn_fs_x/cached_data.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/cached_data.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/caching.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/changes.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/changes.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/dag.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/dag.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs_id.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs_id.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs_x.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/fs_x.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/hotcopy.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/hotcopy.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/id.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/id.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/index.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/index.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/libsvn_fs_x.pc.in create mode 100644 contrib/subversion/subversion/libsvn_fs_x/lock.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/lock.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/low_level.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/low_level.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/noderevs.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/noderevs.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/pack.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/pack.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/recovery.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/recovery.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.sql create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rep-cache.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rep-cache.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/reps.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/reps.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rev_file.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/rev_file.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/revprops.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/revprops.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/string_table.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/string_table.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/structure create mode 100644 contrib/subversion/subversion/libsvn_fs_x/temp_serializer.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/temp_serializer.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/transaction.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/transaction.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/tree.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/tree.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/util.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/util.h create mode 100644 contrib/subversion/subversion/libsvn_fs_x/verify.c create mode 100644 contrib/subversion/subversion/libsvn_fs_x/verify.h create mode 100644 contrib/subversion/subversion/libsvn_ra/libsvn_ra.pc.in create mode 100644 contrib/subversion/subversion/libsvn_ra_local/libsvn_ra_local.pc.in create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/eagain_bucket.c create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/get_file.c create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/get_lock.c create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/lock.c delete mode 100644 contrib/subversion/subversion/libsvn_ra_serf/locks.c create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/multistatus.c create mode 100644 contrib/subversion/subversion/libsvn_ra_serf/stat.c create mode 100644 contrib/subversion/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in create mode 100644 contrib/subversion/subversion/libsvn_repos/authz_pool.c create mode 100644 contrib/subversion/subversion/libsvn_repos/config_pool.c create mode 100644 contrib/subversion/subversion/libsvn_repos/libsvn_repos.pc.in create mode 100644 contrib/subversion/subversion/libsvn_subr/bit_array.c rename contrib/subversion/subversion/libsvn_subr/{md5.h => checksum.h} (61%) create mode 100644 contrib/subversion/subversion/libsvn_subr/compress.c create mode 100644 contrib/subversion/subversion/libsvn_subr/config_keys.inc create mode 100644 contrib/subversion/subversion/libsvn_subr/errorcode.inc create mode 100644 contrib/subversion/subversion/libsvn_subr/fnv1a.c create mode 100644 contrib/subversion/subversion/libsvn_subr/fnv1a.h create mode 100644 contrib/subversion/subversion/libsvn_subr/libsvn_subr.pc.in delete mode 100644 contrib/subversion/subversion/libsvn_subr/named_atomic.c create mode 100644 contrib/subversion/subversion/libsvn_subr/object_pool.c create mode 100644 contrib/subversion/subversion/libsvn_subr/packed_data.c create mode 100644 contrib/subversion/subversion/libsvn_subr/prefix_string.c delete mode 100644 contrib/subversion/subversion/libsvn_subr/pseudo_md5.c create mode 100644 contrib/subversion/subversion/libsvn_subr/root_pools.c delete mode 100644 contrib/subversion/subversion/libsvn_subr/sha1.c delete mode 100644 contrib/subversion/subversion/libsvn_subr/sha1.h create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc.c create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc/LICENSE create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc/README create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.c create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.h create mode 100644 contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc_data.c create mode 100644 contrib/subversion/subversion/libsvn_subr/x509.h create mode 100644 contrib/subversion/subversion/libsvn_subr/x509info.c create mode 100644 contrib/subversion/subversion/libsvn_subr/x509parse.c create mode 100644 contrib/subversion/subversion/libsvn_wc/libsvn_wc.pc.in create mode 100644 contrib/subversion/subversion/svn/auth-cmd.c create mode 100644 contrib/subversion/subversion/svn/cl-log.h delete mode 100644 contrib/subversion/subversion/svn/client_errors.h create mode 100644 contrib/subversion/subversion/svn/similarity.c create mode 100644 contrib/subversion/subversion/svnbench/cl.h create mode 100644 contrib/subversion/subversion/svnbench/help-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/notify.c create mode 100644 contrib/subversion/subversion/svnbench/null-blame-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/null-export-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/null-info-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/null-list-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/null-log-cmd.c create mode 100644 contrib/subversion/subversion/svnbench/svnbench.c create mode 100644 contrib/subversion/subversion/svnbench/util.c create mode 100644 contrib/subversion/subversion/svnfsfs/dump-index-cmd.c create mode 100644 contrib/subversion/subversion/svnfsfs/load-index-cmd.c create mode 100644 contrib/subversion/subversion/svnfsfs/stats-cmd.c create mode 100644 contrib/subversion/subversion/svnfsfs/svnfsfs.c create mode 100644 contrib/subversion/subversion/svnfsfs/svnfsfs.h create mode 100644 contrib/subversion/subversion/svnserve/logger.c create mode 100644 contrib/subversion/subversion/svnserve/logger.h create mode 100644 usr.bin/svn/lib/libsvn_fs_x/Makefile create mode 100644 usr.bin/svn/svnbench/Makefile create mode 100644 usr.bin/svn/svnfsfs/Makefile diff --git a/contrib/subversion/.ycm_extra_conf.py b/contrib/subversion/.ycm_extra_conf.py new file mode 100644 index 000000000..4108bec5e --- /dev/null +++ b/contrib/subversion/.ycm_extra_conf.py @@ -0,0 +1,88 @@ +# Configuration file for YouCompleteMe vim plugin to allow the plugin +# to determine the compile flags. This file is based on: +# https://github.com/Valloric/YouCompleteMe/blob/master/cpp/ycm/.ycm_extra_conf.py +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + +import os +import ycm_core +from clang_helpers import PrepareClangFlags + +compilation_database_folder = os.path.dirname(os.path.realpath(__file__)) + +if compilation_database_folder: + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return flags + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def FlagsForFile( filename ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = database.GetCompilationInfoForFile( filename ) + final_flags = PrepareClangFlags( + MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ), + filename ) + do_cache = True + else: + final_flags = [ ] + do_cache = False + + return { + 'flags': final_flags, + 'do_cache': do_cache + } diff --git a/contrib/subversion/CHANGES b/contrib/subversion/CHANGES index dc382958a..ddaef6799 100644 --- a/contrib/subversion/CHANGES +++ b/contrib/subversion/CHANGES @@ -1,3 +1,932 @@ +Version 1.9.4 +(28 Apr 2016, from /branches/1.9.x) +http://svn.apache.org/repos/asf/subversion/tags/1.9.4 + + User-visible changes: + - Client-side bugfixes: + * diff: support '--summarize --ignore-properties' (part of issue #4567) + * checkout: fix performance regression on NFS (r1710167) + * gpg-agent: properly handle passwords with percent characters (issue #4611) + * svn-graph.pl: fix assertion about a non-canonical path (r1729060 et al) + * hot-backup.py: better input validation (r1721174, r1721175) + * commit: abort on Ctrl-C in plaintext password prompt (issue #4624) + * diff: produce proper forward binary diffs with --git (r1704292, r1704573) + * ra_serf: fix deleting directories with many files (issue #4557) + + - Server-side bugfixes: + * improve documentation for AuthzSVNGroupsFile and groups-db (r1730856) + * fsfs: reduce peak memory usage when listing large directories (r1725180) + * fsfs: fix a rare source of incomplete dump files and reports (r1717876) + + - Client-side and server-side bugfixes: + * update INSTALL documentation file (r1703470 et al) + * fix potential memory access bugs (r1722860 et al) + * fix potential out of bounds read in svn_repos_get_logs5() (r1738259) + + - Bindings bugfixes: + * ignore absent nodes in javahl version of svn status -u (r1720643) + + Developer-visible changes: + - General: + * fix ruby test suite to work with test-unit gem (r1714790) + * allow building against KDE4 without conflict with KDE5 (r1734926) + * fix update_tests.py#76 with SVNPathAuthz short_circuit (r1736432) + * build system tweaks: + * tweak how symbolic error names in maintainer mode (r1735179) + * fix inconsistent behavior of inherited property API (r1717874 et al) + + - API changes: + * properly interpret parameters in svn_wc_get_diff_editor6() (r1728308) + + +Version 1.9.3 +(15 Dec 2015, from /branches/1.9.x) +http://svn.apache.org/repos/asf/subversion/tags/1.9.3 + + User-visible changes: + - Client-side bugfixes: + * svn: fix possible crash in auth credentials cache (r1705328) + * cleanup: avoid unneeded memory growth during pristine cleanup (r1706241) + * diff: fix crash when repository is on server root (r1705060 et al) + * fix translations for commit notifications (r1709389, r1709562) + * ra_serf: fix crash in multistatus parser (r1706323, r1706324) + * svn: report lock/unlock errors as failures (r1701598 et al) + * svn: cleanup user deleted external registrations (r1705843, r1710558) + * svn: allow simple resolving of binary file text conflicts (r1703581) + * svnlook: properly remove tempfiles on diff errors (r1711346) + * ra_serf: report built- and run-time versions of libserf (r1704847) + * ra_serf: set Content-Type header in outgoing requests (r1715224 et al) + * svn: fix merging deletes of svn:eol-style CRLF/CR files (r1703689 et al) + * ra_local: disable zero-copy code path (r1718167) + + - Server-side bugfixes: + * mod_dav_svn: fix heap overflow with skel-encoded requests (CVE-2015-5343) + * mod_authz_svn: fix authz with mod_auth_kerb/mod_auth_ntlm (issue #4602) + * mod_dav_svn: fix display of process ID in cache statistics (r1709553) + * mod_dav_svn: use LimitXMLRequestBody for skel-encoded requests (r1687812) + * svnadmin dump: preserve no-op changes (r1709388 et al, issue #4598) + * fsfs: avoid unneeded I/O when opening transactions (r1715793) + + - Client-side and server-side bugfixes: + * fix heap overflow in svn:// protocol parser (CVE-2015-5259) + + - Bindings bugfixes: + * javahl: fix ABI incompatibilty with 1.8 (r1710104) + * javahl: allow non-absolute paths in SVNClient.vacuum (r1710215, r1710290) + + Developer-visible changes: + - General: + * fix patch filter invocation in svn_client_patch() (r1706783) + * add @since information to config defines (r1706983, r1706999) + * fix running the tests in compatibility mode (r1706375) + * clarify documentation of svn_fs_node_created_rev() (r1717154) + + - API changes: + * fix overflow detection in svn_stringbuf_remove and _replace (r1714358) + * don't ignore some of the parameters to svn_ra_svn_create_conn3 (r1714314) + + +Version 1.9.2 +(30 Sep 2015, from /branches/1.9.x) +http://svn.apache.org/repos/asf/subversion/tags/1.9.2 + + User-visible changes: + - Client-side bugfixes: + * svn: fix crash when saving credentials in kwallet (r1700740, r1700951) + * checkout/update: fix "access denied" error on Windows (r1701064 et al) + * update: fix crash when updating a conflicted tree (r1702198, r1702200) + * commit: fix possible crash (r1702231) + * ra_serf: do not crash on unexpected 'X-SVN-VR-Base' headers (r1702288) + * merge: fix crash when merging to a local add (r1702299 et al) + * svnmucc: fix error during propset+put for existing file (r1702467 et al) + * update: fix crash without .svn/tmp folder (r1701838, r1702203) + * checkout: remove unnecessary I/O operation (r1701638) + * merge: fix possible crash (r1701997) + * update: fix crash with some of the incoming deletes (r1702247) + * upgrade: fix crash for pre-1.3 wc with externals (r1702218 et al) + * revert: fix crash when reverting the root of a move (r1702237 et al) + * svn: do not crash upon specific database corruptions (r1702974, r1702991) + * svn: show utf8proc version in svn --version --verbose (r1702533, r1702891) + + - Server-side bugfixes: + * fix reporting for empty representations in svnfsfs stats (r1698312 et al) + + Developer-visible changes: + - General: + * fix svnfsfs_tests.py in fsfs-v4 and fsfs-v6 modes (r1700215 et al) + + - API changes: + * disable unsupported operations for standard streams (r1701633 et al) + + +Version 1.9.1 +(02 Sep 2015, from /branches/1.9.x) +http://svn.apache.org/repos/asf/subversion/tags/1.9.1 + + User-visible changes: + - Client-side bugfixes: + * Fix crash with GPG-agent with non-canonical $HOME (r1691928, issue #4584) + * Fix checkout errors with svn+ssh:// on Windows (r1696222, r1696225) + * svn: expose expat and zlib versions in svn --version --verbose (r1696387, r1697664) + * svn: improve help text for 'svn info --show-item' (r1698106) + + - Server-side bugfixes: + * svnserve: fixed minor typo in help text (r1694023) + * Enable caching with memcached on Windows (1674626, r1674785) + * Fix an error leak in FSFS verification (r1693886) + * Fix incomplete membuffer cache initialization (r1695022) + * svnfsfs: fix some bugs and inconsistencies in load-index (r1697381 et al.) + + - Client-side and server-side bugfixes: + * Fix alignment fault in ra_svn on 32 bit SPARC machines (r1697914) + + - Bindings bugfixes: + * Fix memory corruption in copy source SWIG bindings (r1694929) + + Developer-visible changes: + * Better configure-time detection of httpd version and authz fix (r1687304 et al.) + * Correct a parameter name in svn_repos_get_fs_build_parser5 (r1694194) + * Resolve circular library reference in libsvn_fs_x (r1696695) + * Fix Unix build on systems without GPG agent (r1694481, r1697824) + +Version 1.9.0 +(5 Aug 2015, from /branches/1.9.x) +http://svn.apache.org/repos/asf/subversion/tags/1.9.0 + + User-visible changes: + - General: + * make all commands provide brief description in help output (r1522518) + * flush stdout before exiting to avoid information being lost (r1543868) + + - Major new features: + * fsfs: new format 7 with more efficient on-disk layout (r1547045 et al) + * blame: support showing prospective as well as previous changes + * info: support printing of individual values with --show-item (r1662620) + * svn auth: new subcommand to manage cached credentials and certs + * svnserve: cache config and authz to lower resource usage and be able to + serve large numbers of connections with a limited number of threads + * membuffer: quadruple the maximum cacheable directory size (r1545948 et al) + * new filesystem fsx (faster, smaller); experimental - see release notes + + - Minor new features and improvements: + * new 'diff-ignore-content-type' runtime configuration option + * new option for 'svnadmin verify': --check-normalization + * new option for 'svnadmin verify': --keep-going + * svnadmin info: new subcommand to print info about a repository + * print summary of conflicts before/after interactive conflict resolution + * import: reduce number of connections to the server needed (r1482962) + * membuffer: rework cache eviction heuristics (r1476664 et at) + * membuffer: improved cache usage statistics (r1489883) + * mergeinfo: new '--log' option (r1492168) + * svnadmin upgrade: progress and cancellation support (r1495545, r1495566) + * cleanup: add '--remove-unversioned' and '--remove-ignored' (issue #3549) + * cleanup: add '--include-externals' option (issue #2325) + * cleanup: add '--quiet' option (r1498661) + * svnadmin load: speedup by setting revprops in one call (r1504079) + * svnadmin load: set svn:date revprop in the initial commit (r1504951) + * reimplement UTF-8 fuzzy conversion using utf8proc (r1511676) + * svnadmin verify: speed up for repos with large directories (r1520419) + * svn merge: interactive conflict resolver tries external tools (r1524145) + * minor speed up in string to time conversion (r1533387) + * windows: speed up console output (r1533994) + * update: optimize wc db usage when obtaining children (r1537065 et al) + * decreased overhead for case-sensitive configuration access (r1538068) + * avoid re-opening repo for in-repo authz if already open (r1538120) + * svnserve: output errors in a more standard way (r1544250) + * faster parsing of config file comments (r1544716) + * avoid trying to open the hooks-env file when it doesn't exist (r1544721) + * svnserve: provide the same logging detail in "run once" mode as provided + in the log file (r1544731) + * svnserve: reduce connection latency (r1544732) + * wc: reduce the number of locks and transactions required (r1545127 et al) + * cat: add '--ignore-keywords' option (r1547517) + * merge and mergeinfo: use fewer RA sessions (r1552265) + * fsfs: limit delta chains from crossing too many shards (r1554942) + * fsfs: option to configure compression level in deltas (r1559748) + * fsfs: enable dir and prop deltas by default for formats that support + it (r1555286) + * fsfs: avoid out of date errors from files in a directory changing when + you change a property on the directory (issue #2118) + * fsfs: temporary transaction properties are preserved when commit is + interrupted (r1549907 et al) + * fsfs: speed up transaction creation (r1544719) + * fsfs: avoid trying to open lock digest files that don't exist (r1544721) + * fsfs: reduce internal overhead by using sorted array instead of hash for + directory representation (r1554711) + * fsfs: skip decoding txdelta windows that are already cached (r1555284) + * fsfs: avoid constructing fulltext when delta we need is stored (r1555297) + * fsfs: improvements to format 6 reading/writing (r1517479) + * fsfs: reduce overhead of parsing noderev structures (r1544717) + * fsfs: speed up node relation comparison (r1554807) + * fsfs: speed up critical open_path() call (r1483301 et al) + * fsfs: speed up node verification (r1520421 et al) + * fsfs: speed up serialization into cache buffer format (r1505056) + * fsfs: avoid caching intermediate fulltexts (r1565100) + * fsfs: reduce lock contention on txn-list-lock (r1569549) + * svnadmin: don't display warnings as errors (r1556297) + * ra_serf: avoid caching properties in the update editor (r1557538) + * ra_serf: decrease latency of requests to get directory contents by + pipelining requests, speeds up 'svn ls --include externals' and + some cases of multi-url diff, including merge (r1557548) + * ra_serf: spool small update reports in memory to avoid making temp files + for small requests (r1557599) + * ra_serf: allow the reuse of serf connections after an error (r1553341) + * ra_serf: improve many error messages (r1551910 et al) + * ra_serf: pipeline lock/unlock requests (r1551918, r1551993, r1552008) + * ra_serf: pipeline requests for inherited properties against old servers + that don't support the faster REPORT (r1552455, r1552475) + * ra_serf: allow reuse of sessions after a canceled request (r1557686) + * ra_serf: reduce memory usage when retrieving revision props (r1557689) + * mod_dav_svn: make out of date error message consistent with generic repos + logic (r1558247) + * allow SQLite to optimize functions that are deterministic (r1559352) + * speed up delta calculations on non-deltifyable sections (r1559767) + * ra_serf: improve memory usage in commit processing (r1520028) + * report progress as cumulative across all sessions (issue #3260) + * ra_serf: don't send DAV headers with GET requests (r1517472) + * mod_dav_svn: SVNCacheTextDeltas defaults to on (r1517479) + * fs: improve scalability of fs_open and similar functions (r1523450 et al) + * svnserve: improve performance and scalability (r1523465 et al) + * svnadmin verify: output progress messages to stdout (r1471095) + * svnadmin crashtest: make output less misleading (r1486046) + * mod_dav_svn: discover copy to src earlier in some cases (r1537440) + * speed up mergeinfo parsing (r1483292, r1483309 et al) + * optimize hash lookups used in mergeinfo and caching (r1483434 et al) + * log: optimize string handling in repos layer (r1483570, r1483572) + * ask disk hardware to sync instead of only syncing to hardware buffers + when OS supports it (r1484439, r1484445) + * optimize diff code to avoid unneeded comparisons (r1485488) + * optimize check if relpaths are canonical (r1485496, r1489828) + * ra_svn: reduce protocol implementation overhead (r1485499, r1485504 et al) + * optimize file translation without keyword substitution (r1486058) + * optimize config file parsing by using unbuffered I/O (r1486897) + * patch: apply ignore settings when deciding to delete dirs (r1490378) + * use a cheaper RA operation for common client calculation (r1496468 et al) + * ra_svn: avoid unnecessary work when doing a blame (r1503046) + * optimize reading files into memory if size is known (r1505068) + * copy: do not error on properties when doing a foreign copy (r1563361) + * membuffer: reduce memory usage by using shorter lived pools (r1564217) + * svnadmin load: add '--ignore-dates' option (r1564789) + * update: reduce sessions used with '--parents' option (r1565920) + * diff: report properties on deleted nodes (r1569320, r1570055) + * diff: switch to diff processor APIs instead of old style callbacks, step + towards resolving long standing bugs and feature requests (r1569551 et al) + * diff: use the proper revision in headers for addition and deletion + of files (r1570053) + * svnadmin lslocks: add cancellation (r1571992) + * svn --version: list available authentication credential caches (r1572106) + * fs: improved detection of changes between two nodes, this should reduce + the number of out of date errors clients see (r1572336) + * allow the use of libmagic to be configured via subversion configuration + file or SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE env variable (r1572916) + * new '--pin-externals' option for svn copy (issue #1258) + * the '--strict' option was renamed '--no-newline' (r1662224) + * merge/update: switch to three-way text conflict markers + (r1591951, r1595522) + * patch: handle renames in git formatted patches (r1594636) + * svnfsfs: new expert tool (r1594860) + * mod_dav_svn: allow server admin to GET the FSFS global cache stats + (r1595160) + * diff: support git-like binary file diffs with '--git' (r1599552) + * diff: support arbitrary context size for internal diff tool with + '-U' option (r1603847, 1603871) + * commit: print progress notification between client finishing + transmitting text deltas and asking server to commit (r1604179) + * fsfs: optimize log commands for repos on Windows by not + using a locale specific function to parse ids (r1605123) + * fsfs: reduce memory usage of reading/writing changed paths caches + (r1605188 et al) + * mod_dav_svn: log post-commit errors to httpd error log as well + as returning them to client (r1606004) + * make server config groups work with svn:// URLs (issue #4512) + * svnadmin hotcopy: report progress when copying revisions and packed + shards for FSFS repositories (r1613339) + * info: show revisions for missing tree conflicts (r1617932) + * fsfs: avoid shared data clashes due to multiple distinct repositories + having identical UUIDs (r1618138 et al) + * status: ignore thumbs.db files by default (r1620955) + * fsfs: harden parsers against data corruption (r1622931, r1622937, + r1622942) + * diff: respect svn:keywords and svn:eol-style when doing arbitrary + diffs (r1623820) + * ra_serf: reduce size of XML generated for reports (r1627333) + * replace generic --trust-server-cert with more specific options to + override specific certificate failures (r1630117 et al) + * commit: improve speed of commits with many files (r1630312 et al) + * svnadmin setrevprop: add '--transaction' option (r1631435) + * svnadmin delrevprop: new subcommand (1592723) + * svnadmin verify: validate the index data against checksums (r1631598) + * svnadmin verify: new option '--metadata-only' (r1593753) + * cp: improve performance of local copies (r1632284, et al) + * fsfs: speed up operations that use revision properties (r1634875, + r1634879) + * checkout/update: use fewer RA sessions (r1635753 et al) + * log: do less work when '--with-no-revprops' is used (r1642231) + * patch: improve command to be more capable as compared to GNU patch + (issue #4533) + * limit server caches to avoid running out of memory if server admin + configured caches larger than supported by the platform (r1645572) + * mod_authz_svn: log implicit read access failures with INFO log level; + Explicit read access failures are still logged with ERROR log level + (r1653032) + * ra_serf: reduce memory usage by removing some extra intermediary state + (r1654681, r1654690) + * revert: improve performance on large working copies without changes + (r1657083) + * ra_svn: improve efficiency of editor processing (r1658194 et al) + * merge: provide different conflict reasons for local additions and + unversioned obstructions (r1659381) + * windows: improve checkout performance on windows by using sqlite truncate + journal mode rather than delete (r1659426) + * status: now accept '-r' argument (r1671164, 1672578, 1673228) + * ls: improve performance of '-v' on tag directories (r1673153) + * resolve: improve conflict prompts for binary files (r1667228 et al) + * fsfs: improve error messages for parsing errors (r1678147, r1678149) + * warn when the '--config-option' FILE:SECTION:OPTION combination may + be invalid. (r1674301 et al) + * ensure full key matching in membuffer cache (r1678950 et al) + * mod_dav_svn: expose cache statistics via HTTP (r1595160) + + - Client-side bugfixes: + * export: fix problem exporting symlinks on windows (r1476093) + * fix non-ascii character handling of command line options (r1476461) + * log: strip EOL marker only after converting to UTF-8 (r1476608) + * ra_serf: avoid dropping errors when making a lock request (r1489526) + * ra_serf: fix an error leak in update logic (r1499686) + * windows: fix issues with wcs in symlinked folders (r1501251) + * fix regression that broke parallel nested checkouts (issue #4390) + * svnmucc: ignore leading r in revision number arguments to -r (r1502636) + * mkdir: use absolute paths to avoid Windows path length limits (r1516816) + * avoid encoding support code when C runtime doesn't support it (r1530582) + * ra_svn: add check against dirents with path separators (r1533812) + * properly delete temporary files when atomic write fails (r1537466) + * wc: fix corner cases in move-update tree conflicts (r1538639 et al) + * windows: fix crash report indentation for x64 reports (r1543589) + * mergeinfo: allow to work on a moved target (issue #4301) + * windows: avoid delay when no homedir is available (r1546814) + * status: report externals in a deterministic way (r1550200) + * copy: avoid an unneeded extra RA session for wc to repo case (r1551564) + * ra_serf: show pre-revprop-change failure on revprop delete (issue #3086) + * svnsync: avoid extra request at end of every revision sync (r1553370) + * ra_serf: fix replace + propset of locked file failures (issue #3674) + * ra_serf: prevent overwriting directory during copy (issue #3314) + * commit: limit number of temporary files open at same time (issue #4172) + * ra_serf: verify incoming integers are really integers (r1557709 et al) + * log: -g --xml doesn't differentiate between forward and reverse merges + (issue #4463) + * windows: improve update and checkout speed (issue #4450) + * log: reduce performance penalties of using -g (r1559912) + * blame: reduce performance penalties of using -g (r1560112) + * ra_local: set svn:txn-user-agent for consistency (r1498608) + * ra_svn: use the stream API properly for communications (r1562072) + * update: provide error when none of targets are wcs (r1565388) + * wc: reduce dependence on unspecified SQLite behavior (r1567080 et al) + * diff: fix diffing directory without permissions to read parent (r1569265, + r1569290) + * diff: improve resolving peg revisions (r1570205 et al) + * diff: fix bug in calculating header paths (r1570584) + * ra_serf: add SSL certificate issuers common name to output (r1573728) + * updates keywords in files not modified during 'svn switch' (issue #1975) + * svnmucc: Normalize line endings with '-F' and '-m' options + (r1592148, r1592150) + * fix problems with read-only authentication caches (issue #4504) + * commit: don't bump just locked files (r1603617) + * log: reduce memory with '-v' (r1604569 et al) + * diff: fix diffing locally deleted nodes under copied directories + (r1605832) + * diff: fix missing node error when diffing a replaced node (r1605866) + * propget: forbid using 'show-inherited-properties' with 'strict' (r1611698) + * windows: avoid delay when user profile isn't writable (r1617926) + * merge: display the correct node kinds for tree conflicts (r1618024, + r1619418, r1619717) + * diff: show the correct revisions in the header (r1619452) + * diff: fix missing header for files with only property changes (r1619476) + * update: raise a tree conflict rather than an obstruction when an + incoming server-excluded node conflicts with a locally added node + (r1619495) + * update: improve tree conflict reason and action descriptions (r1619777) + * ra_serf: fix stalls during checkout/update over http/https (r1621596) + * svnmucc: don't crash when '--version' is used with other arguments + (r1625496) + * checkout: report svn:externals failures via the exit code as other + commands already do (r1628398) + * svn & svnlook: use the right error code when fputs() fails (r1630369) + * export: reject peg specifiers on local destination path (r1635085) + * don't reject command-line arguments in the form of ".@abc", where + "abc" is a peg specifier that may be empty (r1635118) + * fix directory externals not following history (issue #4529) + * remove 'df' and 'm' options from the interactive conflict resolver for + binary files (r1645578) + * mergeinfo parsing: allow source path to be empty (issue #4537) + * mkdir: when using '--parents' don't add entire contents recursively if + target already exists (r1649951) + * resolve errors with move of a nested delete (r1651980, r1651997) + * update: prevent breaking a working copy when a directory is replaced + with an external to a foreign repository (issue #4550) + * update: prevent an invalid wc state when applying a move (r1652184 et al) + * resolve: fix a segfault when breaking a move inside a delete (issue #4491) + * ra_serf: don't handle a commit that didn't produce a new revision as + a successful commit (r1653532) + * export: fix the lack of notifications when starting to handle externals + (issue #4527) + * update: fix a case where we reported an error rather than a tree conflict + (r1655017) + * info: Use local platform style paths in all cases (r1659283) + * handle lack of a configuration file properly (r1660369) + * update: resolve issues with tree conflicts caused by an incoming + delete removing a mixed revision tree (r1660742) + * don't hold onto locks of deleted paths in the client on commit (r1661363) + * info: fix url calculation for a few statuses (r1661476) + * update: when using '--set-depth' avoid removing local changes (r1661585) + * update: fix tree conflict detection on unversioned nodes that exist where + there used to be a deleted node (r1661664) + * status: display tree conflicts even if the node with the tree conflict is + shadowed by a file (r1662331) + * pre-1.6 wc compatibility: fix with obstructed working copies (r1662412) + * resolve: allow directly resolving tree conflicts (r1658435) + * copy: when copying from a wc to a url show all the changes (r1655729) + * info: provide results in a stable order (r1662030) + * revert: allow depth limited reverts of nodes that only have not-present + and/or excluded child nodes (r1662091) + * wc: fix calculating repo path after commits of nodes that shadow a + switched (not-present) node (r1663991, r1666258, r1674032) + * update: resolve assertion on bad update report involving incomplete + status (r1663671, r1666832) + * update: allow a real file to replace a file external (r1664035) + * merge: raise a tree conflict on root of obstructing dir (r1666690) + * cp: fix 'svn cp ^/A/D/H@1 ^/A' to properly create A (r1674455, r1674456) + * status: fix incorrect output with file externals (issue #4580) + * merge: fix part of issue #4582 (r1686175, r1687029, r1688258) + + - Server-side bugfixes: + * svnserve: don't ignore socket initialization errors (r1544253) + * svnserve: don't hide fatal errors in inetd and tunnel modes (r1544256) + * fsfs: log repo path in local style for cache init failure (r1494314) + * fsfs: fix potential transaction corruption (r1519624) + * svnserve: fix logging in multi-threaded servers (r1523502) + * fsfs: don't report out of date errors due to FS corruption (r1527084) + * svnadmin verify: detect inconsistencies that prevent loading (r1536854) + * mod_dav_svn: use 404 status for errors caused by invalid URIs (r1542063) + * mod_dav_svn: use 404 when the repository doesn't exist (r1544259) + * mod_dav_svn: use 'dav_svn:' prefix for filename instead of 'svn:' + (r1544711) + * mod_dav_svn: XML escape lock tokens (r1547427) + * hotcopy: don't create config files when copying pre-1.5 repos (r1547454) + * hotcopy: preserve the rep-cache.db permissions when hotcopying (r1547877) + * mod_dav_svn: fix SVNCacheTextDeltas and SVNAdvertisV2Protocol directive + merging (r1548124) + * mod_dav_svn: always produce an error text even when text specifies + the default message to make diagnosing issues easier (r1553441) + * mod_dav_svn: fix some pool lifetime issues with error messages (r1553868) + * mod_dav_svn: avoid setting option headers multiple times (r1557103) + * fsfs: prevent some commits that could cause future corruption (r1560673) + * cache: fix premature eviction due to 64-bit underflows (r1567996 et al) + * svnserve: fix potential integer overflow in Cyrus SASL support (r1570434) + * bdb: fix potential integer overflow and underflow (r1570701) + * bdb: prevent silent propogation of some corruption (r1570778) + * svnadmin hotcopy: do not corrupt db/current contents when copying old + FSFS repos (r1603485) + * svnadmin hotcopy: don't produce broken copies when a concurrent pack + happens (r1605633) + * log: reduce memory consumption of '-v' (r1605195) + * mod_dav_svn: fix performance issue on Windows with named atomics + (r1611379) + * log: fix a segfault in handling changed paths (r1615364) + * mod_dav_svn: properly forward post-lock/post-unlock failures (r1622235) + * diff: fix handling of depth empty in added directories (r1622024) + * fix a segfault with corrupted changed path lists that try to add root + nodes (r1622944) + * svnadmin verify: report errors nicely rather than possibly aborting + (r1622955) + * svnlook propget: report transaction name rather than revision number in + errors if run with '-t' option (r1623317) + * svnadmin verify: prevent stack overflow conditions in corrupted + repositories (r1623398) + * fsfs upgrade: fix an issue that could block an upgraded format 1 or 2 + repository from being committed to (r1624011) + * fs backends: fix memory lifetime issue (r1632646) + * bdb: output correct checksum in error message about corruption (r1640707) + * fsfs: fix a pool lifetime error related to transaction local caches + (r1643139, 1643233) + * fsfs: avoid "random DAG walks" during checkout over ra_serf (r1645567) + * fsfs/bdb: reduce memory use during tree deletions (r1647820, r1655022) + * mod_authz_svn: fix unbounded memory use when SVNPathAuthz short_circuit + is used (r1647887) + * fsfs: reduce memory use when walking back in history (r1648230) + * dump: remove incorrect kind header on replaced nodes (issue #4553) + * dump: remove duplicate headers for replace-with-copy (issue #4552) + * mod_dav_svn: don't send XML-unsafe characters in svn:author + (issue #4415) + * dump: don't write broken dump files in some ambiguously encoded fsfs + repositories (issue #4554) + * mod_dav_svn: provide a more intuitive error message to the client when + trying to create a directory that already exists (issue #2295) + * fsfs: fix uninitialized memory use in lock/unlock code (r1657525) + * fix a segfault executing a pre-commit hook with legacy locks (r1657893) + * mod_dav_svn: do not ignore skel parsing errors (r1658168) + * fsfs: fix multiple reporting of the same lock (r1658482) + * fsfs: fix pool lifetime issue in lock handling (r1659314) + * mod_dav_svn: properly log assertions and malfuctions (r1660480) + * svnadmin load/dump: preserve deletion of svn:date from r0 (issue #4563) + * svnrdump: don't provide HEAD+1 as base revision when loading deletes + (r1664684) + * mod_dav_svn: improve error message sent with a 405 status code + (r1665195, r1666096, r1666379) + * make detection of invalid base revision on commit behave the same on + all RA layers (r1664664, r1664672, r1664674) + * mod_dav_svn: emit the first few log items as soon as they are available + (r1666965, r1667120) + * mod_dav_svn: prevent a tree walk on copy sources (issue #4351) + * fsfs: fix 'EOF found' error when reading repo (issue #4577) + * svnadmin freeze: unlock rep-cache.db as part of unfreezing + (r1679169, r1679287) + * fsfs: improve stability in the presence of power or network + disk failures during 'svnadmin pack' (r1683378) + * detect invalid svndiff data earlier (r1684077) + + - Client-side and server-side bugfixes: + * use less memory when retrieving extension from filename (r1548480) + * use more optimal code path with old txdelta v1 data (r1485480) + * windows: allow opening SQLite databases on long paths (r1564338) + * fix an out-of-bounds read in the delta combiner (r1569415) + * fix a number of cases of undefined behavior when passing invalid + arguments to memcpy() (r1632530, et al) + * windows: avoid a 12 second delay due to a retry loop in some error + conditions when opening a file (r1657583) + + - Other tool improvements and bugfixes: + * windows: add build-svn-deps-win.pl tool to build dependencies (r1467715) + * svnpubsub: add support for revprop changes (r1486463) + * svnpubsub: do not pass svn commands through shell (r148466, r1486467) + * svnpubsub: hooks exit with an error if they fail (r1486500, r1486597) + * svnpubsub: hooks use "--" to signal end of arguments (r1486513) + * mailer: properly encode mail headers per RFC2047 (r1487532) + * svnwcsub: add a pre-update hook, which can deny update (r1494542 et al) + * fsfs-stats: count file nodes that are added without history (r1519283) + * fsfs-stats: replaced by 'svnfsfs stats' (r1594860) + * fsfs-access-map: count empty reads and unnecessary seeks (r1523441) + * fsfs-access-map: generate scaled/rectangular heatmaps (r1505065) + * fsfs-access-map: deal with strace format issues (r1505065) + * fsfs-access-map: update to know about index files (r1505065) + * svnbench: renamed from svn-bench (r1659226) + * svnbench: add null-info command (r1532196) + * svnlook.py: made usable as a library by adding getter methods (r1541558) + * svnbench, svnraisetreeconflict, svnauthz, svn-rep-sharing-stats: More + consistent error reporting, following pattern of core command-line + programs (r1544194) + * which-error.py: allow which-error.py to be run from symlink (r1547977) + * bash_completion: add svnlook filesize command and options to svn cat, + info, and mergeinfo commands (r1569021) + * new '--conflict-style' option to standalone diff3 tool (r1591750) + * update standalone diff3 tool so it can be used with --diff-cmd + directly (r1591871, r1591876) + * bash_completion: support for auth command (r1596841) + * diff: support '-U' option (r1618618) + * svn-rep-sharing-stats: replaced by 'svnfsfs stats' (r1618861) + * add svn-vendor.py as an alternative to svn_load_dirs.pl that can + auto-detect renames and copies (r1623660) + * svnpredumpfilter.py: fix a scalability problem that made run time + increase greatly on large repositories (r1625674 et al) + * svnpredumpfilter.py: detect copies of copies and handle properly + (r1626182) + * bash_completion: add svnadmin delrevprop (r1631473) + * showchange.pl: removed, obsoleted by 'svn log --diff' (r1631686) + * bash_completion: add new trust options (r1660373) + * bash_completion: add '--pin-externals' (r1662250) + * bash_completion: stop offering deprecated options (r1662291) + * bash_completion: add '--show-item' and '--no-newline' (r1662622) + * svnbench: add null-blame command (r1673785, r1673803, r1674015) + * svnbench: install with default 'make install' (r1685085) + + Developer-visible changes: + - General: + * require Python 2.7+ for development and testing (r1691712, r1691713, r1692448) + * include symbolic names for error codes in maintainer mode (r1467643) + * include symbolic names for warning codes in maintainer mode (r1469855) + * support YouCompleteMe vim plugin (r1476374 et al) + * cache: add pretend_empty mode for maintainers (r1461689, et al) + * allow tests to be run with exclusive wc locks (r1496475) + * fix check for Microsoft C/C++ compiler (r1503148) + * improve windows build to use pre-built dependencies + * optimize ramdrive usage in the windows test runner (r1504511) + * SVN_CMDLINE_DISABLE_CRASH_HANDLER env var added for Windows (r1506507) + * gen-make.py: support Visual Studio 2013 by --vsnet-version=2013 (r1533907) + * windows: don't require APR source when building (r1534037, et al) + * don't compile SQLite amalgamation twice (r1536364) + * make C tests use same temp path as Python tests (r1536700) + * davautocheck: access log logs the r->filename now (r1538048) + * added test to detect duplicate SQL queries (r1538962) + * windows test runner: copy additional apr dlls (r1539300) + * support for debug logging that should be available in serf 1.4.0 (r1541561) + * OS X: fix build when Gnome is installed (r1541698) + * properly detect if expat is libexpat.lib or xml.lib on windows (r1541981) + * doc-clean target works properly with separate build dir (r1542303) + * don't break entire test run if cleaning temp directory failed but return + an error anyway (r1542817) + * windows tests: find java.exe via the sdk if not found via PATH (r1543876) + * fix davautocheck with httpd 2.3.x+ and worker/prefork MPMs (r1544302-3) + * add parallel mode for C tests (r1545334 et al) + * use --bin path for svnrdump when running Python tests (r1548706) + * remove unused --with-openssl option from configure (r1548714) + * tests: stop rewriting shared authz file for every sandbox (r1552064 et al) + * fix unnecessary aborts in maintainer mode on sqlite errors (r1536325) + * new --sqlite-logging option for C tests (r1560409) + * allow building with APR-2 (r1560586) + * remove support for ASM Zlib from Windows build system (r1560864) + * gen-make.py: new -D option (r1567046) + * improve how we fetch the version of Windows (r1568798, 1568806) + * move common code in python tests to library files to avoid side effects of + having tests depending on each other (r1570654) + * disable compiling on platforms where int is shorter than 32-bits and add + the SVN_ALLOW_SHORT_INTS compile definition to override (r1570882) + * disable building *.sbr files with Visual C++ 2010 or later (r1571020) + * require serf 1.3.4 (r1572261) + * require APR and APR-Util 1.3.x (r1572261) + * add mod_dontdothat to davautocheck.sh (r1572312) + * require Apache httpd 2.2.x (r1572339) + * support pkg-config for serf when using a prefix (r1572762) + * don't depend on stdbool.h and inttypes.h for utf8proc (r1573069 et al) + * avoid non-portable find extensions so the build works on Solaris (r1573780) + * Allow PARALLEL value to specify the number of threads to run tests with, + PARALLEL=1 means to use the default (r1573320) + * pkg-config support for libsvn_* libraries (issue #4435) + * binaries built on Mac OS X 10.6+ will not run on older versions (r1651549) + * fix random failure of Makefile-based parallel builds on Unix + when the amalgamated SQLite is being used (r1658357) + * C tests only run with ra_local to avoid duplicate testing (r1609477) + * allow skipping the C tests for any RA method (r1609489) + * new tools for benchmarking on Windows (r1610264) + * svnbench: no longer part of tools but main subversion (r1618860) + * improve detection for libtool (r1627276) + * check the runtime version of the fs_util library in the fs backends + (r1651567) + * properly initialize the src_kind value to avoid problems for other API + users during a copy (r1655723) + * try to improve reliability of applications that do not call + svn_dso_initialize2() right after apr_initialize() (r1659604) + * add svn-wc-db-tester tool for testing working copies (r1660874) + * fix bugs and performance issues using svn_wc_walk_entries3() (r1661110) + * support using Python 3 for building (r1661247) + * don't add -lstdc++ on FreeBSD 10 (r1662329) + * add build support for Visual Studio 2015 (r1663183, r1663184, r1689721 et al.) + * fix test failures when running from a directory whose name contains + characters that must be escaped when used in a URL. (r1664997) + * fix breakage of the serf ra session with svn_ra_get_dir2() and + svn_ra_get_log2(). (r1665213, r1665259, r1665609) + * resolve a race condition in some test suite cleanup code (r1683303) + * fix some tests on non-US default locale on Windows (r1684034) + * document the meaning of "XFAIL" for users building from source (r1683071) + + - API changes: + * new RA callbacks for managing ra_svn tunnels: + svn_ra_callbacks2_t::check_tunnel_func, + svn_ra_callbacks2_t::open_tunnel_func and + svn_ra_callbacks2_t::close_tunnel_func + * new API for retrieving info about a file system: svn_fs_info() + * new API svn_io_file_flush() that wraps apr_file_flush() + * new API svn_io_write_atomic(), that writes, sync and renames a file + * new API svn_compat_log_revprops_out_string() + * deprecate unused datatype svn_ra_get_latest_revnum_func_t + * new API svn_client_cleanup2() (r1496954 et al) + * new API svn_stringbuf_create_wrap() (r1502248) + * new API svn_io_file_aligned_seek() (r1502539) + * ra_svn: fix svn_ra_get_log*() to apply limit when server can't (r1503043) + * svn_client_commit6: notify which path prevents a mv commit (r1503662) + * new APIs svn_io_file_create_empty() and svn_io_file_create_bytes() + (r1505006) + * new API svn_ver_check_list2() (r1502267) + * new API svn_stringbuf_from_stream() (r1532193) + * svn_auth_get_platform_specific_client_providers() now includes ssl + providers (r1534153) + * change dav_svn_split_uri() repos_path argument (r1537812) + * new API svn_repos_fs_type() returns filesystem type (r1538585) + * svn_client__get_inheritable_props() clears wcroot_iprops when revision is + not valid (r1538602) + * SVN_VA_NULL: New macro which is null-pointer constant (r1536307) + * SVN_NEEDS_SENTINEL_NULL: New macro to mark functions that require final + NULL sentinel value, so compilers can warn when missing (r1543394) + * platform specific svn_auth_get_* functions are deprecated, use the + svn_auth_get_platform_specific_provider() function instead (r1543992) + * SVN_INT_ERR macro is deprecated; use svn_handle_error2() or + svn_cmdline_handle_exit_error() instead (r1544142) + * new API svn_client_cat3() allow disabling keyword expansion and retrieving + props (r1544182) + * svn_auth_first_credentials(): provides an error rather than crashing if + auth_baton is NULL (r1544320) + * svn_auth_set_parameter(): Do nothing if auth_baton is NULL (r1544320) + * new API svn_relpath_prefix which returns a relpath with a maximum number + of path components (r1545123, r1673282) + * svn_fs_initialize() is now threadsafe (r1546409) + * svn_checksum_kind_t has two new FNV-1a checksums (r1546699) + * svn_fs_lock: control characters are not allowed in lock tokens (r1547445) + * new API svn_wc_cleanup4() makes some functionality optional and adds + notifications (r1548075 et al) + * new API svn_client_info4() supports walking externals (r1550206 et al) + * new flag for svn_fs_begin_txn2() SVN_FS_TXN_CLIENT_DATE to allow client + to set the final svn:date (r1550228) + * new APIs for easier RA commits svn_client_mtcc_* (r1550758 et al) + * new API svn_ra_session_dup() (r1552324 et al) + * svn_ra_stat() now handles compatibility with 1.0-1.1 svnserve (r1552441) + * new error code SVN_ERR_COMPOSED_ERROR added to allow detection of + composed errors by API consumers (r1553266) + * new error code SVN_ERR_RA_DAV_PRECONDITION_FAILED (r1553668) + * new error code SVN_ERR_RA_CANNOT_CREATE_SESSION (r1554027) + * new API svn_fs_node_relation() (r1554800) + * SVN_EXPERIMENTAL to mark functions that might change in future (r1526012) + * fix bug in svn_client_get_merging_summary() with some params (r1532396) + * new error code SVN_ERR_XML_UNEXPECTED_ELEMENT (r1498938) + * extend stream API to also support incomplete reads (r1561688 et al) + * new API svn_wc_add_from_disk3() (r1563361) + * new API svn_client_revert3() to allow clearing changelists and to have + a metadata_only flag (r1568635, r1657026) + * svn_rangelist_inheritable2(): don't change inheritabilty of remaining + ranges (r1569731) + * svn_rangelist_inheritable2() and svn_mergeinfo_inheritable2(): fix a + pool lifetime issue (r1569764) + * new APIs to support cancelation during unified diff output and + allow the context size to be specified (r1570149 et al) + * APIs related to retrieving logs are now documented to be unlimited when + a negative value is passed for the limit (r1570330, 1570335) + * new APIs: svn_fs_props_different() and svn_fs_contents_different() + (r1572363, r1573111) + * expose SVN_CONFIG_AUTHN_* macros in public API (r1572640) + * add SVN_CONFIG_OPTION_SQLITE_TIMEOUT to allow SQLite busy timeout + to be configured by clients (r1592093) + * add new notification between transmitting deltas and finalizing + commit as svn_wc_notify_commit_finalizing (r1603388) + * svn_client_cat3() API no longer returns entry or WC props (r1603501) + * properly handle canonical paths in svn_io_start_cmd3() (r1604761) + * add SVN_FS_CONFIG_FSFS_BLOCK_READ to control FSFS format 7 block + read feature (r1604933) + * new API svn_cstring_skip_prefix() (r1612823) + * new API svn_diff_mem_string_output_merge3() (r1618599) + * extend svn_wc_info_t with conflicts2 (r1618643) + * new API svn_diff_mem_string_output_unified3() (r1618839) + * new API svn_diff_file_output_merge3() (r1618857) + * svn_fs_props_changed() and svn_fs_contents_changed() no longer return + false positives (r1618880) + * new API svn_wc_conflict_description2_dup() (r1618883) + * extend svn_wc_conflict_description2_t with prop_reject_abspath field + (r1619096) + * extend svn_wc_conflict_description2_t with property values as + svn_string_t's (r1619122) + * fix svn_client_import5() to use absolute paths (r1623974) + * fix a few cases of invalid filling of svn_client_commit_item3_t (r1623981) + * add SVN_FS_CONFIG_FSFS_SHARD_SIZE to allow creation of repositories with + a custom shard size (r1624512) + * new API svn_cmdline_create_auth_baton2 (r1630117) + * make svn_string_*() and svn_stringbuf_*() functions handle C strings + of NULL with 0 length (r1632530, et al) + * fix svn_stream_compressed() for streams without partial read support + (r1639626) + * do not segfault on svn_stream_read_full() or svn_stream_skip() are called + for a no-op stream created via svn_stream_create() (r1639637) + * add SVN_FS_CONFIG_FSFS_LOG_ADDRESSING to control logical addressing + feature of fsfs format 7 (r1640915) + * rename the 'parent_directory' parameter of + svn_wc_parse_externals_description3() to 'defining_directory' and improve + the documentation (r1642690) + * new API svn_repos_get_fs_build_parser5() (r1647563) + * SVN_VERSION_BODY changed so that it is embedded into libraries, ultimately + allowing SVN_VER_TAG constant to be modified by patches (r1651565) + * rename repos_url to repos_url_deocded argument to + svn_repos_get_commit_editor5() to clairfy usage (r1653609) + * make svn_io_set_file_read_only() and svn_io_remove_dir2() ignore + ENOTDIR when ignore_enoent argument is set so the behavior is consistent + across platforms (r1653680) + * make svn_ra_open4() return the documented error when a repository + should be opened in a different location (r1655648) + * no longer return an error when using svn_ra_get_lock() and the path does + not exist (r1656875) + * svn_string_dup() will accept a NULL pointer and return a NULL (r1657254) + * svn_ra_get_file_revs2() now handles SVN_INVALID_REVNUM as HEAD (r1660463) + * new API svn_error_quick_wrapf() (r1662668) + * new API svn_fs_node_has_props() (r1673170, r1673172, r1673692, r1673746) + * new API svn_repos_verify_fs3() (r1492651 ... r1687769) + + - Bindings: + * javahl: add support for the RA layer (r1494650 et al) + * javahl: add ignore-keywords knob to ISVNCLient.doExport (r1494936) + * javahl: improve performance of rev_range_vector_to_apr_array (r1496243) + * javahl: provide whole stack of errors from native exceptions (r1496353) + * javahl: new framework for manipulating in-memory config data (r1497746) + * javahl: add methods to set config change handler callback (r1497753) + * javahl: add inheritance info to JavaHL's RevisionRange type (r1499308) + * swig-rb: fixes to building on Windows (1505406 et al) + * libsvn_swig_*libraries now work as proper shared librares (r1506520 et al) + * swig-py: 'import svn.core.*' imports libsvn_subr public symbols (r1507860) + * javahl: expose the svn_rangelist operations (r1509025) + * javahl: new utility class SVNUtil (r1512354) + * javahl: serialize init of native library (r1519803) + * javahl: fix value truncation checks (r1519913) + * javahl: add ISVNClient.info which exposes svn_client_info4 (r1603481) + * javahl: include name of exception class in error messages (r1532117) + * javahl: simplify JNI environment handling (r1533804) + * javahl: Revsion.UNSPECIFIED added as alias to Revision.START (r1533928) + * javahl: do not require JUnit to build javahl; only to test (r1535603) + * javahl: update Action enum with new entries added in 1.9 (r1536319) + * javahl: expose node property validation utility (r1538133) + * javahl: new style JNI wrapper (r1539114 et al) + * javahl: new utility API to parse/unparse svn:externals (r1539130 et al) + * javahl: allow tests to be run from a RAM disk (r1539215 et al) + * javahl: expose and use the url member of svn_wc_notify_t (r1539601) + * javahl: expose a utility API to resolve relative externals (r1540921) + * javahl: new utility API for file content translation (r1542401) + * javahl: throw NullPointerException when sources is empty (r1543328) + * swig-pl: fix compilation against perl 5.18 on Windows (r1543980) + * javahl: implement streamed file contents translation (r1543985) + * javahl: enable warnings with javac (r1544163, r1544169) + * javahl: cleanup warnings (r1544578 et al) + * javahl: add common utilities for new-style map iterations (r1545925) + * javahl: add an API to get runtime version of native libs (r1545945) + * javahl: use default cache size instead of no cache (r1547248) + * swig-py: add typemap for hunks field of svn_patch_t (r1548379) + * javahl: don't ignore difference between NULL and empty changelist in + ISVNClient.getChangelists (r1553254) + * swig-pl: fix pool issues with setting dates as revisions (r1553822) + * swig-pl: make svn_auth_set_parameter() usable (r1553823) + * javahl: expose inheritiable property names (r1560338) + * javahl: fix bug in parsing single revision merginfo data (r1563140) + * javahl: create JVM crashlogs from svn malfunctions (r1563927, r1564252) + * javahl: avoid problems when building without NLS support (r1566578 et al) + * javahl: expose --alow-mixed-revisions merge option (r1567602) + * javahl: support incomplete reads from streams (r1569631) + * javahl: allow revert API to take a set of paths (r1571461) + * javahl: improve authentication callback (r1597758 et al) + * javahl: load (most) classes on demand (r1602822) + * javahl: expose confg file keys in JavaHL (r1604448) + * javahl: update ISVNClient.info, ISVnClient.cleanup and add + ISvnClient.vacuum (r1604449) + * javahl: update ISVNRepos.hotcopy (r1618894) + * swig-py: Add close to the core.Stream class (r1619077) + * javahl: add example clients that use the authn API (r1640533) + * swig-py: implement dump stream parser (r1642813) + * swig-pl: remove some unneded cleanup code that triggered a cleanup + failure on windows (r1643072) + * swig-pl: make cancel_func, cancel_baton parameter pairs work (r1648852) + * javahl: expose whitespace diff parameters to blame method (issue #4475) + * javahl: update ConflictDescriptor (r1655842) + * javahl: update FileRevision to provide textDelta (r1656911) + * swig-pl: install into prefix (r1658459) + * javahl: expose the metadataOnly option to copy support (r1661451) + * swig-py: add support for svn_fs_lock_many(), svn_fs_unlock_many(), + svn_repos_fs_lock_many() and svn_repos_fs_unlock_many() (r1662867) + * swig-py: fix support for svn_ra_lock() and svn_ra_unlock() (r1662891) + * javahl: allow java callbacks to throw errors back to the svn code + (r1664938, r1664939, r1664940, r1664978, r1664984) + * swig-pl: fix some stack memory problems (r1668618, 1671388) + * swig: warn on using SWIG 3.x as we have compatibility problems + with newer versions of SWIG (1675149) + * javahl: requires Java 1.6 (r1677003) + * javahl: on OS X use /usr/libexec/java_home to find the JDK (r1675774) + * javahl: allow compiling with a C++11 compiler (r1684412) + + +Version 1.8.16 +(28 Apr 2016, from /branches/1.8.x) +http://svn.apache.org/repos/asf/subversion/tags/1.8.16 + + User-visible changes: + - Server-side bugfixes: + * mod_authz_svn: fix authz with mod_auth_kerb/mod_auth_ntlm (issue #4602) + * dump: don't write broken dump files in some ambiguously encoded fsfs + repositories (issue #4554) + + - Client-side and server-side bugfixes: + * update INSTALL documentation file (r1703470, r1703475) + + Developer-visible changes: + - General: + * fix javahl test suite to work on a symlinked RAM disk (r1539230) + * fix ruby test suite to work with test-unit gem (r1714790) + + +Version 1.8.15 +(15 Dec 2015, from /branches/1.8.x) +http://svn.apache.org/repos/asf/subversion/tags/1.8.15 + + User-visible changes: + - Client-side bugfixes: + * gpg-agent: fix crash with non-canonical $HOME (r1691928, issue #4584) + + - Client-side and server-side bugfixes: + * fix a segfault with old style text delta (r1618472 et al) + + - Server-side bugfixes: + * mod_dav_svn: fix heap overflow with skel-encoded requests (CVE-2015-5343) + * fsfs: reduce memory allocation with Apache (r1591005 et al) + * mod_dav_svn: emit first log items as soon as possible (r1666965 et al) + * mod_dav_svn: use LimitXMLRequestBody for skel-encoded requests (r1687812) + + - Bindings bugfixes: + * swig: fix memory corruption in svn_client_copy_source_t (r1694929) + + Developer-visible changes: + - General: + * better configure-time detection of httpd authz fix (r1687304 et al) + * fix compilation with apr 1.2.x (r1701237) + + Version 1.8.14 (5 Aug 2015, from /branches/1.8.x) http://svn.apache.org/repos/asf/subversion/tags/1.8.14 @@ -13,6 +942,9 @@ http://svn.apache.org/repos/asf/subversion/tags/1.8.14 of user and revision after 'svn up' (r1680242) - Server-side bugfixes: + * mod_authz_svn: do not leak information in mixed anonymous/authenticated + httpd (dav) configurations (CVE-2015-3184) + * do not leak paths that were hidden by path-based authz (CVE-2015-3187) * 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) @@ -863,10 +1795,17 @@ http://svn.apache.org/repos/asf/subversion/tags/1.8.0 * add missing API functions to JavaHL bindings (issue #4326) * fix some reference counting bugs in swig-py bindings (r1464899, r1466524) +Version 1.7.22 +(12 Aug 2015, from /branches/1.7.x) +http://svn.apache.org/repos/asf/subversion/tags/1.7.22 + + Developer-visible changes: + - General: + * fix the regression test suite which was broken in 1.7.21 (r1694012) Version 1.7.21 (5 Aug 2015, from /branches/1.7.x) -http://svn.apache.org/repos/asf/subversion/tags/1.8.21 +http://svn.apache.org/repos/asf/subversion/tags/1.7.21 User-visible changes: - Client-side bugfixes: @@ -875,6 +1814,9 @@ http://svn.apache.org/repos/asf/subversion/tags/1.8.21 non-deltas dumpfile (r1652182 et al.) - Server-side bugfixes: + * mod_authz_svn: do not leak information in mixed anonymous/authenticated + httpd (dav) configurations (CVE-2015-3184) + * do not leak paths that were hidden by path-based authz (CVE-2015-3187) * fix 'svnadmin recover' for pre-1.4 FSFS repositories (r1561419) Developer-visible changes: diff --git a/contrib/subversion/COMMITTERS b/contrib/subversion/COMMITTERS index f0a8739f8..8665dba45 100644 --- a/contrib/subversion/COMMITTERS +++ b/contrib/subversion/COMMITTERS @@ -1,4 +1,4 @@ -The following people have commit access to the Subversion sources. +The following people have commit access to the Subversion sources. Note that this is not a full list of Subversion's authors, however -- for that, you'd need to look over the log messages to see all the patch contributors. @@ -58,6 +58,7 @@ Blanket commit access: stefan2 Stefan Fuhrmann jcorvel Johan Corveleyn trent Trent Nelson + kotkov Evgeny Kotkov [[END ACTIVE FULL COMMITTERS. LEAVE THIS LINE HERE; SCRIPTS LOOK FOR IT.]] @@ -96,6 +97,9 @@ Commit access for specific areas: jrvernooij Jelmer Vernooij (Python bindings) sage Sage LaTorra (Ctypes-Python b.) vmpn Vladimir Berezniker (JavaHL bindings) + rschupp Roderich Schupp (Swig bindings) + stilor Alexey Neyman (Python bindings, + svn-vendor.py) Packages: @@ -157,8 +161,10 @@ Commit access for specific areas: artagnon Ramkumar Ramachandra (svnrdump, svntest) arwin Arwin Arni (svn-bisect) joes Joe Schaefer (svnpubsub) - prabhugs Prabhu Gnana Sundar (verify-keep-going) - + humbedooh Daniel Gruno (svnpubsub) + prabhugs Prabhu Gnana Sundar (verify-keep-going) + schabi Markus Schaber (testsuite) + gbg Gabriela Gibson (gtest) Translation of message files: @@ -186,6 +192,7 @@ giorgio_valoti Giorgio Valoti (po: it) IS BOUNCING] fabien Fabien Coelho (po: fr) marcelg Marcel Gosselin (po: fr) + mattiase Mattias Engdegård (po: sv) Experimental branches: diff --git a/contrib/subversion/INSTALL b/contrib/subversion/INSTALL index 25f4e13ca..3ff16702a 100644 --- a/contrib/subversion/INSTALL +++ b/contrib/subversion/INSTALL @@ -3,7 +3,7 @@ A Quick Guide ====================================== -$LastChangedDate: 2013-09-27 06:57:44 +0000 (Fri, 27 Sep 2013) $ +$LastChangedDate: 2015-12-12 04:00:43 +0000 (Sat, 12 Dec 2015) $ Contents: @@ -15,7 +15,7 @@ Contents: D. Documentation II. INSTALLATION - A. Building from a Tarball or RPM + A. Building from a Tarball B. Building the Latest Source under Unix C. Building under Unix in Different Directories D. Installing from a Zip or Installer File under Windows @@ -123,7 +123,8 @@ I. INTRODUCTION create a repository, you have the option of specifying a storage back-end. The Berkeley DB back-end will only be available if the BDB libraries are discovered at compile - time. + time. The Berkeley DB back-end has been deprecated and + is not recommend. * libsasl (OPTIONAL for client and server) @@ -145,7 +146,7 @@ I. INTRODUCTION Subversion contains optional support for storing passwords in KWallet (KDE 4) or GNOME Keyring. - * libmagic + * libmagic (OPTIONAL) If the libmagic library is detected at compile time, it will be used to determine mime-types of binary files @@ -153,6 +154,12 @@ I. INTRODUCTION configured via auto-props or the mime-types-file option take precedence. + * Googlemock aka Gmock (OPTIONAL) + + This optional package is used by the tests for Subversions' + C++ bindings. + + C. Dependencies in Detail Subversion depends on a number of third party tools and libraries. @@ -186,54 +193,12 @@ I. INTRODUCTION commands described in section II.B before installing the following. - 1. Apache Portable Runtime 0.9.7 or 1.X.X (REQUIRED) + 1. Apache Portable Runtime 1.3 or newer (REQUIRED) Whenever you want to build any part of Subversion, you need the Apache Portable Runtime (APR) and the APR Utility (APR-util) libraries. - - **************************************************************** - ** IMPORTANT ISSUE ABOUT APR VERSIONS: READ THIS. ** - ** ** - **************************************************************** - | | - | APR 0.9.X and 1.X are binary-incompatible. | - | | - | This means: | - | | - | - if you are already using Subversion with APR 0.9.X, and | - | then upgrade your libapr to 1.X without rebuilding | - | Subversion, things will break and segfault. | - | | - | - if your Subversion server libraries are linked to one | - | version of APR, but your Apache server is linked to a | - | different version, things will break and segfault. | - | | - | Subversion distribution dependencies: | - | ------------------------------------- | - | | - | For a long time, Subversion's main distribution contained | - | APR and APR-UTIL (both 0.9.x), plus a few other things that | - | we couldn't count on the installation system having. But | - | nowadays, Subversion's requirements are no longer exotic, | - | and so our main distribution contains just the Subversion | - | source code itself -- people compiling Subversion are | - | expected to either have the APR libraries already installed | - | on their system, or to be capable of fetching them easily. | - | | - | Note that it's *perfectly* safe to use APR 1.X from the | - | beginning. In fact, we recommend it. If you're building | - | Subversion for the first time, there's no compatibility | - | issue to worry about, so grab the latest version of APR. | - | | - | If you already have a Subversion installation using APR | - | 0.9.x, it's still possible to move to APR 1.X safely. Just | - | be sure to recompile Subversion (and Apache httpd if | - | necessary) after upgrading APR! | - |______________________________________________________________| - - If you do not have a pre-installed APR and APR-util, you will need to get these yourself: @@ -288,7 +253,7 @@ I. INTRODUCTION compression. Most Unix systems have libz pre-installed, but if you need it, you can get it from - http://www.zlib.net + http://www.zlib.net/ 3. autoconf 2.59 or newer (Unix only) @@ -306,7 +271,7 @@ I. INTRODUCTION newer. The autogen.sh script knows about that. - 5. Serf library 1.2.1 or newer (OPTIONAL) + 5. Serf library 1.3.4 or newer (OPTIONAL) If you want your client to be able to speak to an Apache server (via a http:// or https:// URL), you must link against @@ -349,11 +314,7 @@ I. INTRODUCTION Under Windows, you can specify the paths to these libraries by passing the options --with-zlib and --with-openssl to gen-make.py. - ### Is that right? In-tree build of Neon was disabled in r875974. - This may now apply to Serf, or else gen-make.py should be - updated to remove such options. - - c. Using OpenSSL on the Apache server + b. Using OpenSSL on the Apache server You can also add support for these features to an Apache httpd server to be used for Subversion using the same support libraries. @@ -430,7 +391,7 @@ I. INTRODUCTION http://freshmeat.net/projects/cyrussasl/ - 9. Apache Web Server 2.X (OPTIONAL) + 9. Apache Web Server 2.2.X or newer (OPTIONAL) (http://httpd.apache.org/download.cgi) @@ -443,14 +404,16 @@ I. INTRODUCTION is done: See section III for details. - 10. Python 2.5 or newer (http://www.python.org/) (OPTIONAL) + 10. Python 2.7 or newer (http://www.python.org/) (OPTIONAL) If you want to run "make check" or build from the latest source - under Unix as described in section II.B and III.D, install - Python 2.5 or higher on your system. The majority of the test - suite is written in Python, as is part of Subversion's build + under Unix/Windows as described in section II.B, II.E and III.D, + install Python 2.7 or higher on your system. The majority of the + test suite is written in Python, as is part of Subversion's build system. + Note that Python 3.x is not supported and most likely won't work. + 11. Perl 5.8 or newer (Windows only) (OPTIONAL) @@ -459,26 +422,10 @@ I. INTRODUCTION script. - 12. MASM 6 or newer (Windows only, OPTIONAL) - - The Windows build scripts for Subversion can use the Microsoft - Macro Assembler (MASM) to build an optimized version of the ZLib - library. Make sure that the version of MASM you use is compatible - with the C compiler. If you're using MSVC 6, and don't have MASM 6, - a free MASM-compatible assembler is available here: - - http://www.masm32.com/ - - You only need ML.EXE and ML.ERR from this distribution. + 12. SQLite (REQUIRED) - The VS.NET installation already contains MASM (but note, that - version if MASM is not compatible with MSVC 6). - - - 13. SQLite (REQUIRED) - - Subversion 1.8 requires SQLite version 3.7.12 or above. You can meet - this dependency several ways: + Subversion requires SQLite version 3.7.12 or above. You can meet this + dependency several ways: * Use an SQLite amalgamation file. * Specify an SQLite installation to use. * Let Subversion find an installed SQLite. @@ -491,26 +438,26 @@ I. INTRODUCTION http://www.sqlite.org/download.html - 14. pkg-config (Unix only, OPTIONAL) + 13. pkg-config (Unix only, OPTIONAL) Subversion uses pkg-config to find appropriate options used at build time. - 15. D-Bus (Unix only, OPTIONAL) + 14. D-Bus (Unix only, OPTIONAL) D-Bus is a message bus system. D-Bus is required for support for KWallet and GNOME Keyring. pkg-config is needed to find D-Bus headers and library. - 16. Qt 4 (Unix only, OPTIONAL) + 15. Qt 4 (Unix only, OPTIONAL) Qt is a cross-platform application framework. QtCore, QtDBus and QtGui modules are required for support for KWallet. pkg-config is needed to find Qt headers and libraries. - 17. KDELibs 4 (Unix only, OPTIONAL) + 16. KDELibs 4 (Unix only, OPTIONAL) Subversion contains optional support for storing passwords in KWallet. KDELibs contains core KDE libraries. Subversion uses libkdecore and libkdeui @@ -522,13 +469,13 @@ I. INTRODUCTION --with-kwallet=/path/to/KDE/prefix - 18. GLib 2 (Unix only, OPTIONAL) + 17. GLib 2 (Unix only, OPTIONAL) GLib is a general-purpose utility library. GLib is required for support for GNOME Keyring. pkg-config is needed to find GLib headers and library. - 19. GNOME Keyring (Unix only, OPTIONAL) + 18. GNOME Keyring (Unix only, OPTIONAL) Subversion contains optional support for storing passwords in GNOME Keyring. pkg-config is needed to find GNOME Keyring headers and library. D-Bus and @@ -536,7 +483,7 @@ I. INTRODUCTION then pass the '--with-gnome-keyring' option to `configure`. - 20. Ctypesgen (OPTIONAL) + 19. Ctypesgen (OPTIONAL) Ctypesgen is Python wrapper generator for ctypes. It is used to generate a part of Subversion Ctypes Python bindings (CSVN). If you want to build @@ -547,7 +494,7 @@ I. INTRODUCTION For more information on CSVN, see subversion/bindings/ctypes-python/README. - 21. libmagic (OPTIONAL) + 20. libmagic (OPTIONAL) Subversion's configure script attempts to find libmagic automatically. If it is installed in a non-standard location, then use: @@ -568,6 +515,12 @@ I. INTRODUCTION --with-libmagic + 21. Googlemock (OPTIONAL) + + Googlemock can be installed and built in-tree by invoking + + $ ./get-dep.sh gmock + D. Documentation The primary documentation for Subversion is the free book @@ -582,7 +535,7 @@ I. INTRODUCTION II. INSTALLATION ============ - A. Building from a Tarball or RPM + A. Building from a Tarball ------------------------------ 1. Building from a Tarball @@ -597,34 +550,10 @@ II. INSTALLATION $ make # make install - You can also run the full test suite by running 'make check'. - - - 2. Building from an RPM - - If you are using Linux (or any OS that can use RPM) then another - possibility is to download the binary RPM from the - http://summersoft.fay.ar.us/pub/subversion directory. - - Currently only Linux on the i386 platform is supported - using this method. You might also require additional RPMS - (which can be found in the above mentioned directory) to use the - subversion RPM depending on what packages you already have installed: - - subversion*.i386.rpm - apache*.i386.rpm (Version 2.0.49 or greater) - db*.i386.rpm (Version 4.0.14 or greater; version 4.3.27 or - 4.2.52 is preferred however) - expat (Comes with RedHat) - - After downloading, install it (as root user): - - # rpm -ivh subversion*.386.rpm (add other packages as necessary) - - Note: For an easy way to generate a new version of the RPM - source and binary package from the latest source code you - just checked out, see the packages/rpm/README file for a - one-line build procedure. + You can also run the full test suite by running 'make check'. Even + in successful runs, some tests will report XFAIL; that is normal. + Failed runs are indicated by FAIL or XPASS results, or a non-zero exit + code from "make check". B. Building the Latest Source under Unix @@ -646,7 +575,6 @@ II. INSTALLATION # rm -f /usr/local/lib/libsvn* # rm -f /usr/local/lib/libapr* - # rm -f /usr/local/lib/libexpat* # rm -f /usr/local/lib/libserf* Start the process by running "autogen.sh": @@ -655,7 +583,7 @@ II. INSTALLATION This script will make sure you have all the necessary components available to build Subversion. If any are missing, you will be - told where to get them from. (See the 'Build Requirements' in + told where to get them from. (See the 'Dependency Overview' in section I.) Note: if the command "autoconf" on your machine does not run @@ -786,27 +714,30 @@ II. INSTALLATION E.1 Prerequisites * Visual Studio 6 and service pack. It can be built with later versions - of Visual Studio (Visual Studio.NET 2002, 2003, 2005, 2008 and Visual - C++ Express 2005, 2008) but these instructions assume VS6. + of Visual Studio (Visual Studio.NET 2005-2015, Visual C++ Express + 2005-2010, Visual Studio Express 2012-2013 and Visual Studio Community + 2013-2015) but these instructions assume VS6. * A recent Windows SDK. (Not needed with Visual Studio 2005 and later) If you are using Visual Studio 6, you need the latest SDK which - is compatible with VC6, which is the one from february 2003. + is compatible with VC6, which is the one from February 2003. You can get it from MSDN: - http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm - * Python 2.5 or higher, downloaded from http://www.python.org/ which is + https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/e1147034-9b0b-4494-a5bc-6dfebb6b7eb1/download-and-install-microsoft-platform-sdk-febuary-2003-last-version-with-vc6-support?forum=windowssdk + * Python 2.7 or higher, downloaded from http://www.python.org/ which is used to generate the project files. + Note that Python 3.x is not supported (yet). * Perl 5.8 or higher from http://www.activestate.com/ * Awk (from http://www.cs.princeton.edu/~bwk/btl.mirror/awk95.exe) is needed to compile Apache or APR. Note that this is the actual awk program, not an installer - just rename it to awk.exe and it is ready to use. * Apache apr, apr-util, and optionally apr-iconv libraries, version - 0.9.12 or later. Included in both the Subversion dependencies ZIP file + 1.3 or later. Included in both the Subversion dependencies ZIP file and the Apache 2 source zip. If you are building from a Subversion checkout and have not downloaded Apache 2, then get these 3 libraries from http://www.apache.org/dist/apr/. + * SQLite 3.7.12 or higher from http://www.sqlite.org/download.html * ZLib 1.2 or higher is required and is included in the Subversion - dependencies zip file or can be obtained from http://www.zlib.org + dependencies zip file or can be obtained from http://www.zlib.net/ * Either a Subversion client binary from http://subversion.apache.org/ to do the initial checkout of the Subversion source or the zip file source distribution. See the section "Bootstrapping from a Zip or @@ -818,11 +749,7 @@ II. INSTALLATION * [Optional] Apache 2 source, downloaded from http://httpd.apache.org/download.cgi, these instructions assume version 2.0.58. This is only needed for building the Subversion - server Apache modules. Note that although Subversion will compile - against Apache 2.2.3 and APR 1.2.7, there is a bug that causes - runtime failures with Subversion on Windows. The fix is included in - APR 1.2.8 and will be bundled in the next HTTP Server release - (likely to be 2.2.4). + server Apache modules. ### FIXME Apache 2.2 or greater required. * [Optional] Apache 2 msi install file, also from http://httpd.apache.org/download.cgi (required for running the tests). Only needed for testing the server dso modules and if @@ -835,7 +762,7 @@ II. INSTALLATION components -- versions 4.3.27 and 4.4.20 are available from http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=688 as db-4.3.27-win32.zip and db-4.4.20-win32.zip. - For more information see Section I.5. + For more information see Section I.C.7. * [Optional] Openssl 0.9.7f or higher can be obtained from http://www.openssl.org/source/openssl-0.9.7f.tar.gz * [Optional] A modified version of GNU libintl, called @@ -856,7 +783,7 @@ II. INSTALLATION The Serf library supports secure connections with OpenSSL and on-the-wire compression with zlib. If you want to use the secure connections feature, you should pass the option - "--with-openssl" to the gen-make.py script. See Section I.11 for + "--with-openssl" to the gen-make.py script. See Section I.C.6 for more details. E.3 Preparation @@ -874,7 +801,7 @@ II. INSTALLATION * Install Visual Studio Environment. You either have to tell the installer to register environment variables or run VCVARS32.BAT before building anything. If you are using a newer Visual Studio, - use the 'Visual Studio 200x Command Prompt' on the Start menu. + use the 'Visual Studio 20xx Command Prompt' on the Start menu. * Install and register a recent Windows Core SDK if you are using Visual Studio 6. This is a quote from the Microsoft February 2003 SDK documentation: @@ -897,14 +824,15 @@ II. INSTALLATION * Install Perl (it should add itself to the path) * Copy AWK (awk95.exe) to awk.exe (e.g. SVN\awk\awk.exe) and add the directory containing it (e.g. SVN\awk) to the path. - * Install Apache 2 using the msi file if you are going to test the - server dso modules and are using Visual Studio 6. You must build + * [Optional] Install Apache 2 using the msi file if you are going to test + the server dso modules and are using Visual Studio 6. You must build and install it from source if you are not using Visual Studio 6 and want to build and/or test the server modules. - * If you checked out Subversion from the repository then install the serf - sources into SVN\src-trunk\serf. - * If you want BDB backend support, extract the Berkeley DB files - into SVN\src-trunk\db4-win32. It's a good idea to add + * [Optional] If you checked out Subversion from the repository and want + to build Subversion with http/https access support then install the + serf sources into SVN\src-trunk\serf. + * [Optional] If you want BDB backend support, extract the Berkeley DB + files into SVN\src-trunk\db4-win32. It's a good idea to add SVN\src-trunk\db4-win32\bin to your PATH, so that Subversion can find the Berkeley DB DLLs. @@ -919,8 +847,8 @@ II. INSTALLATION SVN\src-trunk\db4-win32\lib. Again, the DLLs should be somewhere in your path. - * If you want to build the server modules, extract Apache source into - SVN\httpd-2.x.x. + * [Optional] If you want to build the server modules, extract Apache + source into SVN\httpd-2.x.x. * If you are building from a checkout of Subversion, and you are NOT building Apache, then you will need the APR libraries. Depending on how you got your version of APR, either: @@ -932,14 +860,20 @@ II. INSTALLATION SVN\apr-util, and SVN\apr-iconv respectively. * Extract the ZLib sources into SVN\zlib if you are not using the zlib included in the dependencies zip file. - * If you want secure connection (https) client support, extract openssl - into SVN\openssl-x.x.x - * If you want localized message support, extract svn-win32-libintl.zip - into SVN\svn-win32-libintl and extract gettext-x.x.x-bin.zip and - gettext-x.x.x-dep.zip into SVN\gettext-x.x.x-bin. + * [Optional] If you want secure connection (https) client support, or if + you are building with enabled support for serf extract openssl into + SVN\openssl-x.x.x + * [Optional] If you want localized message support, extract + svn-win32-libintl.zip into SVN\svn-win32-libintl and extract + gettext-x.x.x-bin.zip and gettext-x.x.x-dep.zip into + SVN\gettext-x.x.x-bin. Add SVN\gettext-x.x.x-bin\bin to your path. * [Optional] Extract MASM32 (only the ML.EXE and ML.ERR files) into SVN\asm (or extract nasm into SVN\asm) and put it in your path. + * Download the SQLite amalgemation from + http://www.sqlite.org/download.html + and extract it into SVN\sqlite-amalgemation. + See I.C.12 for alternatives to using the amalgemation package. E.4 Building the Binaries @@ -955,14 +889,14 @@ II. INSTALLATION C:>set VER=trunk C:>set DIR=trunk - C:>set DRIVE=C + C:>set BUILD_ROOT=C:\SVN C:>set PYTHONDIR=C:\Python22 C:>set AWKDIR=C:\SVN\Awk C:>set ASMDIR=C:\SVN\asm - C:>set SDKINC=C:\Program Files\Microsoft SDK\include - C:>set SDKLIB=C:\Program Files\Microsoft SDK\lib + C:>set SDKINC="C:\Program Files\Microsoft SDK\include" + C:>set SDKLIB="C:\Program Files\Microsoft SDK\lib" C:>set GETTEXTBIN=C:\SVN\gettext-0.14.1-bin\bin - C:>PATH=%PATH%;%DRIVE%:\SVN\src-%DIR%\db4-win32;%ASMDIR%; + C:>PATH=%PATH%;%BUILD_ROOT%\src-%DIR%\db4-win32;%ASMDIR%; %PYTHONDIR%;%AWKDIR%;%GETTEXTBIN% C:>set INCLUDE=%SDKINC%;%INCLUDE% C:>set LIB=%SDKLIB%;%LIB% @@ -984,17 +918,44 @@ II. INSTALLATION This step is only required for building the server dso modules. - The Subversion gen-make.py script must be run before building Apache or - Apache and Subversion will be running incompatible versions of apr. - - C:>cd src-%DIR% - C:>python gen-make.py -t dsp --with-httpd=..\httpd-2.0.58 - --with-berkeley-db=db4-win32 --with-openssl=..\openssl-0.9.7f - --with-zlib=..\zlib --with-libintl=..\svn-win32-libintl - C:>cd .. C:>set APACHEDIR=C:\Program Files\Apache Group\Apache2 C:>msdev httpd-2.0.58\apache.dsw /MAKE "BuildBin - Win32 Release" + APR + + If you downloaded APR / APR-UTIL / APR_ICONV by source, you will have to + build these libraries first. + Building these libraries on Windows is straight forward and in most cases + as simple as issuing these two commands: + + C:>nmake -f Makefile.win + C:>nmake -f Makefile.win install + + Please refere to the build instructions provided by the library source + for actual build instructions. + + ZLib + + If you downloaded the zlib source, you will have to build ZLib first. + Building ZLib using Visual Studio should be quite simple. Just open the + appropriate solution and build the project zlibstat using the IDE. + + Please refere to the build instructions provided by the library source + for actual build instructions. + + Note that you'd make sure to define ZLIB_WINAPI in the ZLib config + header and move the lib-file into the zlib root-directory. + + Serf + + ### Section about serf might be required/useful to add. + ### scons is required too and serf needs to be configured prior to be + ### able to build Subversion using: + ### scons APR=[PATH_TO_APR] APU=[PATH_TO_APU] OPENSSL=[PATH_TO_OPENSSL] + ### ZLIB=[PATH_TO_ZLIB] PREFIX=[PATH_TO_SERF_DEST] + ### scons check + ### scons install + Subversion Things to note: @@ -1006,7 +967,7 @@ II. INSTALLATION the APR libraries; the options are --with-apr, --with-apr-util and --with-apr-iconv. * If you would like a debug build substitute Debug for Release in - the msdev commands. + the msdev/msbuild commands. * There have been rumors that Subversion on Win32 can be built using the latest cygwin, you probably don't want the zip file source distribution though. ymmv. @@ -1019,8 +980,9 @@ II. INSTALLATION directories must be in the Tools/Options/Directories settings (if you followed the 'Register the SDK with Visual Studio 6' instructions above this has been done for you). - * If you are using Visual Studio .NET change -t dsw into -t vcproj and - add the --vsnet-version=200x option on the gen-make.py command. + * If you are using Visual Studio later than VC6 change -t dsw into + -t vcproj and add the --vsnet-version=20xx option on the gen-make.py + command. In this case you will also have to distribute the C runtime dll with the binaries. Also, since Apache/APR do not provide .vcproj files, you will need to convert the Apache/APR .dsp files to .vcproj files @@ -1030,7 +992,6 @@ II. INSTALLATION The Apache/APR projects required by Subversion are: apr-util\libaprutil.dsp, apr\libapr.dsp, apr-iconv\libapriconv.dsp, apr-util\xml\expat\lib\xml.dsp, - apr-util\uri\gen_uri_delims.dsp (for APR 0.9.x), apr-iconv\ccs\libapriconv_ccs_modules.dsp, and apr-iconv\ces\libapriconv_ces_modules.dsp. * If the server dso modules are being built and tested Apache must not @@ -1051,12 +1012,13 @@ II. INSTALLATION C:>msdev subversion_msvc.dsw /USEENV /MAKE "__ALL_TESTS__ - Win32 Release" C:>cd .. - Or, with Visual C++.NET 2002, 2003, 2005: + Or, with Visual C++.NET 2005 or C++ Express 2005: C:>devenv subversion_vcnet.sln /build "Release" /project "__ALL_TESTS__" C:>cd .. - Or, with Visual C++ Express 2005: + Or, with Visual C++.NET 2008+, C++ Express 2008+, Studio Express 2012+ or + Studio Community 2013+: C:>msbuild subversion_vcnet.sln /t:__ALL_TESTS__ /p:Configuration=Release C:>cd .. @@ -1102,7 +1064,7 @@ II. INSTALLATION Then run the client tests: - C:>PATH=%DRIVE%:\SVN\svn-win32-%VER%\bin;%PATH% + C:>PATH=%BUILD_ROOT%\svn-win32-%VER%\bin;%PATH% C:>cd src-%DIR% C:>python win-tests.py -c -r -v @@ -1152,42 +1114,16 @@ III. BUILDING A SUBVERSION SERVER A. Setting Up Apache ----------------- - (Following the BOOTSTRAPPING FROM RPM procedures above will install and - build the latest Subversion server for Linux RedHat 7.1, 7.2, and PPC - Linux systems *IF* the apache-devel-2.0.41 or greater package is already - installed when the SUBVERSION RPM is built.) - - 1. Obtaining and Installing Apache 2 Subversion tries to compile against the latest released version - of Apache httpd 2.X. The easiest thing for you to do is download + of Apache httpd 2.2+. The easiest thing for you to do is download a source tarball of the latest release and unpack that. - - **************************************************************** - ** IMPORTANT ISSUE ABOUT APACHE VERSIONS: READ THIS. ** - ** ** - **************************************************************** - | | - | First, be sure to read the APR version warning box, back in | - | section I.C.1, which explains that APR 0.9.x and 1.X are | - | binary-incompatible. | - | | - | Apache HTTPD 2.0 uses APR 0.9.x. | - | Apache HTTPD 2.2 uses APR 1.2.x. | - | | - | We recommend using the latest Apache. However, whatever | - | version you choose, you *must* ensure that Subversion | - | and Apache are using the same version of APR. If you don't, | - | things will segfault and break. | - |______________________________________________________________| - - - If you have questions about the Apache httpd 2.0 build, please consult + If you have questions about the Apache httpd 2.2 build, please consult the httpd install documentation: - http://httpd.apache.org/docs-2.0/install.html + http://httpd.apache.org/docs-2.2/install.html At the top of the httpd tree: @@ -1213,7 +1149,7 @@ III. BUILDING A SUBVERSION SERVER line. Make sure this is the same db as the one Subversion uses. This note assumes you have installed Berkeley DB 4.2.52 at its default locations. For more info about the db requirement, - see section I.5. + see section I.C.7. You may also want to include other modules in your build. Add --enable-ssl to turn on SSL support, and --enable-deflate to turn on @@ -1233,7 +1169,7 @@ III. BUILDING A SUBVERSION SERVER --------------------------------------------------------- Go back into your subversion working copy and run ./autogen.sh if - you need to. Then, assuming Apache httpd 2.0 is installed in the + you need to. Then, assuming Apache httpd 2.2 is installed in the standard location, run: $ ./configure @@ -1243,7 +1179,7 @@ III. BUILDING A SUBVERSION SERVER look for other libsvn_*.so libraries on your system. If you see a warning message that the build of mod_dav_svn is - being skipped, this may be because you have Apache httpd 2.X + being skipped, this may be because you have Apache httpd 2.x installed in a non-standard location. You can use the "--with-apxs=" option to locate the apxs script: @@ -1285,7 +1221,7 @@ III. BUILDING A SUBVERSION SERVER /usr/local/apache2/conf/httpd.conf to reflect your setup. At a minimum you should look at the User, Group and ServerName directives. Full details on setting up apache can be found at: - http://httpd.apache.org/docs-2.0/ + http://httpd.apache.org/docs-2.2/ First, your httpd.conf needs to load the mod_dav_svn module. If you pass --enable-mod-activation to Subversion's configure, @@ -1339,6 +1275,7 @@ III. BUILDING A SUBVERSION SERVER Require group svn_readers + ### FIXME Tutorials section refers to old 2.0 docs These are only a few simple examples. For a complete tutorial on Apache access control, please consider taking a look at the tutorials found under "Security" on the following page: @@ -1365,7 +1302,7 @@ III. BUILDING A SUBVERSION SERVER NOTE: If you are unfamiliar with an Apache directive, or not exactly sure about what it does, don't hesitate to look it up in the - documentation: http://httpd.apache.org/docs-2.0/mod/directives.html. + documentation: http://httpd.apache.org/docs-2.2/mod/directives.html. NOTE: Make sure that the user 'nobody' (or whatever UID the httpd process runs as) has permission to read and write the diff --git a/contrib/subversion/LICENSE b/contrib/subversion/LICENSE index fd5f6ba31..8b1b7abcf 100644 --- a/contrib/subversion/LICENSE +++ b/contrib/subversion/LICENSE @@ -268,3 +268,101 @@ For the file subversion/libsvn_subr/utf_width.c * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. + +For the (modified) utf8proc library in subversion/libsvn_subr/utf8proc + + Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + This software distribution contains derived data from a modified version of + the Unicode data files. The following license applies to that data: + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed + under the Terms of Use in http://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of the Unicode data files and any associated documentation (the "Data + Files") or Unicode software and any associated documentation (the + "Software") to deal in the Data Files or Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, and/or sell copies of the Data Files or Software, and + to permit persons to whom the Data Files or Software are furnished to do + so, provided that (a) the above copyright notice(s) and this permission + notice appear with all copies of the Data Files or Software, (b) both the + above copyright notice(s) and this permission notice appear in associated + documentation, and (c) there is clear notice in each modified Data File or + in the Software as well as in the documentation associated with the Data + File(s) or Software that the data or software has been modified. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR + CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THE DATA FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder shall + not be used in advertising or otherwise to promote the sale, use or other + dealings in these Data Files or Software without prior written + authorization of the copyright holder. + + Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be + registered in some jurisdictions. All other trademarks and registered + trademarks mentioned herein are the property of their respective owners. + +For the files subversion/libsvn_subr/x509parse.c and +subversion/libsvn_subr/x509.h + + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/subversion/Makefile.in b/contrib/subversion/Makefile.in index c0a5a54d5..e63a09c6a 100644 --- a/contrib/subversion/Makefile.in +++ b/contrib/subversion/Makefile.in @@ -48,6 +48,7 @@ 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_INTL_LIBS = @SVN_INTL_LIBS@ SVN_SASL_LIBS = @SVN_SASL_LIBS@ SVN_SERF_LIBS = @SVN_SERF_LIBS@ SVN_SQLITE_LIBS = @SVN_SQLITE_LIBS@ @@ -87,6 +88,9 @@ swig_pldir = @libdir@/svn-perl swig_rbdir = $(SWIG_RB_SITE_ARCH_DIR)/svn/ext toolsdir = @bindir@/svn-tools +# where to install pkg-config files +pkgconfig_dir = $(datadir)/pkgconfig + javahl_javadir = @libdir@/svn-javahl javahl_javahdir = @libdir@/svn-javahl/include javahl_libdir = @libdir@ @@ -132,6 +136,9 @@ APACHE_INCLUDES = @APACHE_INCLUDES@ APACHE_LIBEXECDIR = $(DESTDIR)@APACHE_LIBEXECDIR@ APACHE_LDFLAGS = @APACHE_LDFLAGS@ +SVN_USE_GMOCK = @SVN_USE_GMOCK@ +GMOCK_INCLUDES = -I@GMOCK_SRCDIR@ + SWIG = @SWIG@ SWIG_PY_INCLUDES = @SWIG_PY_INCLUDES@ -I$(SWIG_SRC_DIR)/python/libsvn_swig_py SWIG_PY_COMPILE = @SWIG_PY_COMPILE@ @@ -165,11 +172,13 @@ MKDIR = @MKDIR@ DOXYGEN = @DOXYGEN@ # The EXTRA_ parameters can be used to pass extra flags at 'make' time. -CFLAGS = @CFLAGS@ $(EXTRA_CFLAGS) +CFLAGS = @CFLAGS@ @CUSERFLAGS@ $(EXTRA_CFLAGS) CMODEFLAGS = @CMODEFLAGS@ +CNOWARNFLAGS = @CNOWARNFLAGS@ CMAINTAINERFLAGS = @CMAINTAINERFLAGS@ -CXXFLAGS = @CXXFLAGS@ $(EXTRA_CXXFLAGS) +CXXFLAGS = @CXXFLAGS@ @CXXUSERFLAGS@ $(EXTRA_CXXFLAGS) CXXMODEFLAGS = @CXXMODEFLAGS@ +CXXNOWARNFLAGS = @CXXNOWARNFLAGS@ CXXMAINTAINERFLAGS = @CXXMAINTAINERFLAGS@ ### A few of the CFLAGS (e.g. -Wmissing-prototypes, -Wstrict-prototypes, ### -Wmissing-declarations) are not valid for C++, and should be somehow @@ -180,10 +189,12 @@ SWIG_LDFLAGS = @SWIG_LDFLAGS@ $(EXTRA_SWIG_LDFLAGS) SWIG_CPPFLAGS = @SWIG_CPPFLAGS@ $(EXTRA_CPPFLAGS) COMPILE = $(CC) $(CMODEFLAGS) $(CPPFLAGS) $(CMAINTAINERFLAGS) $(CFLAGS) $(INCLUDES) +COMPILE_NOWARN = $(CC) $(CMODEFLAGS) $(CPPFLAGS) $(CNOWARNFLAGS) $(CFLAGS) $(INCLUDES) COMPILE_CXX = $(CXX) $(CXXMODEFLAGS) $(CPPFLAGS) $(CXXMAINTAINERFLAGS) $(CXXFLAGS) $(INCLUDES) +COMPILE_CXX_NOWARN = $(CXX) $(CXXMODEFLAGS) $(CPPFLAGS) $(CXXNOWARNFLAGS) $(CXXFLAGS) $(INCLUDES) LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) $(LT_CFLAGS) LT_COMPILE_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(LT_CFLAGS) - +LT_COMPILE_CXX_NOWARN = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX_NOWARN) $(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` @@ -199,8 +210,12 @@ COMPILE_SWIG_RB = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_RB_COMPILE) $(CPPF 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) +COMPILE_JAVAHL_COMPAT_JAVAC = $(JAVAC) $(JAVAC_COMPAT_FLAGS) -# export an env variable so that the tests can run without being installed +# On Mac OS X, export an env variable so that the tests can run without +# being installed. OS X needs the DYLD_LIBRARY_PATH env variable set in +# order to find the libraries to link against, because we can't effectively +# use rpath due to way rpath is implemented in the Mach executable format. TEST_SHLIB_VAR_JAVAHL=\ if [ "@SVN_APR_SHLIB_PATH_VAR@" = "DYLD_LIBRARY_PATH" ]; then \ for d in $(abs_builddir)/subversion/libsvn_*; do \ @@ -212,9 +227,33 @@ TEST_SHLIB_VAR_JAVAHL=\ done; \ export @SVN_APR_SHLIB_PATH_VAR@; \ fi; +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; +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; # special compilation for files destined for cxxhl -COMPILE_CXXHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(LT_CFLAGS) $(CXXHL_INCLUDES) -o $@ -c +COMPILE_CXXHL_CXX = $(LT_COMPILE_CXX) $(CXXHL_INCLUDES) -o $@ -c +COMPILE_GMOCK_CXX = $(LT_COMPILE_CXX_NOWARN) $(GMOCK_INCLUDES) -o $@ -c +COMPILE_CXXHL_GMOCK_CXX = $(LT_COMPILE_CXX) $(CXXHL_INCLUDES) $(GMOCK_INCLUDES) -o $@ -c LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -rpath $(libdir) LINK_LIB = $(LINK) $(LT_SO_VERSION) @@ -300,19 +339,6 @@ INSTALL_EXTRA_SWIG_PY=\ 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 @@ -343,19 +369,6 @@ INSTALL_EXTRA_SWIG_RB=\ $(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@ @@ -367,6 +380,7 @@ JAVA = @JAVA@ JAVAC = @JAVAC@ JAVADOC = @JAVADOC@ JAVAC_FLAGS = @JAVAC_FLAGS@ +JAVAC_COMPAT_FLAGS = @JAVAC_COMPAT_FLAGS@ JAVAH = @JAVAH@ JAR = @JAR@ @@ -430,6 +444,7 @@ local-distclean: local-clean libtool mkmf.log subversion/svn_private_config.h \ subversion/bindings/javahl/classes \ subversion/bindings/javahl/include \ + subversion/libsvn_*/*.pc \ $(SVN_CONFIG_SCRIPT_FILES) rm -f Makefile @@ -467,13 +482,15 @@ revision-install: 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 +javahl: mkdir-init javahl-java javahl-javah javahl-callback-javah javahl-remote-javah javahl-types-javah javahl-util-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: + if [ -d $(javahl_test_rootdir) ]; then \ + rm -rf $(javahl_test_rootdir)/*; \ + fi 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 @@ -481,37 +498,47 @@ clean-javahl: 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 + $(JAVA) -Xcheck:jni "-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 + $(JAVA) -Xcheck:jni "-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-deprecated-authn-javahl: javahl + @FIX_JAVAHL_LIB@ + $(TEST_SHLIB_VAR_JAVAHL) \ + $(JAVA) -Xcheck:jni "-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)" "-Dtest.authn.deprecated=true" org.apache.subversion.javahl.RunTests check-javahl: check-apache-javahl -check-all-javahl: check-apache-javahl check-tigris-javahl +check-all-javahl: check-apache-javahl check-tigris-javahl check-deprecated-authn-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 "$(SKIP_C_TESTS)" != ""; then \ + flags="--skip-c-tests $$flags"; \ + fi; \ + if test "$(DUMP_LOAD_CROSS_CHECK)" != ""; then \ + flags="--dump-load-cross-check $$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"; \ + 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"; \ @@ -526,7 +553,7 @@ check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $(TEST_DEPS) @BDB_TEST_DEPS@ flags="--fsfs-packing $$flags"; \ fi; \ if test "$(PARALLEL)" != ""; then \ - flags="--parallel $$flags"; \ + flags="--parallel $(PARALLEL) $$flags"; \ fi; \ if test "$(LOG_TO_STDOUT)" != ""; then \ flags="--log-to-stdout $$flags"; \ @@ -544,13 +571,19 @@ check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $(TEST_DEPS) @BDB_TEST_DEPS@ if test "$(HTTP_PROXY)" != ""; then \ flags="--http-proxy $(HTTP_PROXY) $$flags"; \ fi; \ + if test "$(EXCLUSIVE_WC_LOCKS)" != ""; then \ + flags="--exclusive-wc-locks $$flags"; \ + fi; \ + if test "$(MEMCACHED_SERVER)" != ""; then \ + flags="--memcached-server $(MEMCACHED_SERVER) $$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 "make check: Python 2.7 or greater is required,"; \ echo " but was not detected during configure"; \ exit 1; \ fi; @@ -564,7 +597,7 @@ davcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ apache-mod # 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 + @APXS=$(APXS) MAKE=$(MAKE) $(SHELL) $(top_srcdir)/subversion/tests/cmdline/davautocheck.sh # First, run: # subversion/svnserve/svnserve -d -r `pwd`/subversion/tests/cmdline @@ -573,8 +606,8 @@ svncheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ # '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 + @env PYTHON=$(PYTHON) THREADED=$(THREADED) MAKE=$(MAKE) \ + $(SHELL) $(top_srcdir)/subversion/tests/cmdline/svnserveautocheck.sh # First, run: # subversion/svnserve/svnserve --listen-host "::1" -d -r `pwd`/subversion/tests/cmdline @@ -591,6 +624,15 @@ svnsshcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ bdbcheck: bin $(TEST_DEPS) @BDB_TEST_DEPS@ @$(MAKE) check FS_TYPE=bdb +# Produce the clang compilation database as the compile_commands.json file +# in the srcdir. This is used by tools such as the YouCompleteMe vim plugin +# to know the compile flags for various source files so that analysis such +# as syntax checking, static analysis or symbol completion can be done +# outside the build system. To do this it uses the tool called bear: +# https://github.com/rizsotto/Bear +compile-commands: + @bear -o $(abs_srcdir)/compile_commands.json -- $(MAKE) all + # Create an execution coverage report from the data collected during # all execution since the last reset. gcov: @@ -605,12 +647,11 @@ gcov-reset: 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 -- + find . -name "*.gcda" -o -name "*.gcno" -exec 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 --; \ + rm -rf subversion/tests/cmdline/svn-test-work/*; \ fi rm -rf subversion/tests/libsvn_fs/test-repo-* \ subversion/tests/libsvn_fs_base/test-repo-* \ @@ -654,28 +695,42 @@ doc-javahl: -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 + org.apache.subversion.javahl \ + org.apache.subversion.javahl.callback \ + org.apache.subversion.javahl.remote \ + org.apache.subversion.javahl.types \ + org.apache.subversion.javahl.util 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 + rm -rf $(abs_builddir)/doc/doxygen + rm -rf $(abs_builddir)/doc/javadoc + +# Converting from the .rnc XML schemas to various other schema formats. +SCHEMAS_DTD = $(SCHEMA_DIR)/blame.dtd \ + $(SCHEMA_DIR)/diff.dtd \ + $(SCHEMA_DIR)/info.dtd \ + $(SCHEMA_DIR)/list.dtd \ + $(SCHEMA_DIR)/log.dtd \ + $(SCHEMA_DIR)/props.dtd \ + $(SCHEMA_DIR)/status.dtd + +SCHEMAS_RNG = $(SCHEMA_DIR)/blame.rng \ + $(SCHEMA_DIR)/diff.rng \ + $(SCHEMA_DIR)/info.rng \ + $(SCHEMA_DIR)/list.rng \ + $(SCHEMA_DIR)/log.rng \ + $(SCHEMA_DIR)/props.rng \ + $(SCHEMA_DIR)/status.rng + +SCHEMAS_XSD = $(SCHEMA_DIR)/blame.xsd \ + $(SCHEMA_DIR)/diff.xsd \ + $(SCHEMA_DIR)/info.xsd \ + $(SCHEMA_DIR)/list.xsd \ + $(SCHEMA_DIR)/log.xsd \ + $(SCHEMA_DIR)/props.xsd \ + $(SCHEMA_DIR)/status.xsd + +schema: mkdir-init schema-rng schema-dtd schema-xsd schema-rng: $(SCHEMAS_RNG) schema-dtd: $(SCHEMAS_DTD) @@ -781,7 +836,7 @@ $(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 + cd $(SWIG_PL_DIR)/native; $(PERL) Makefile.PL PREFIX=$(prefix) # 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 @@ -862,13 +917,15 @@ 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; \ + check_rb() { \ + $(RUBY) -I $(SWIG_RB_SRC_DIR) $(SWIG_RB_SRC_DIR)/test/run-test.rb "$$@"; \ + }; \ + if check_rb --help 2>&1 | grep -q -- --collector; then \ + check_rb --collector=dir --verbose=$(SWIG_RB_TEST_VERBOSE); \ + elif [ "$(RUBY_MAJOR)" -eq 1 -a "$(RUBY_MINOR)" -lt 9 ] ; then \ + check_rb --verbose=$(SWIG_RB_TEST_VERBOSE); \ + else \ + check_rb; \ fi EXTRACLEAN_SWIG_RB=rm -f $(SWIG_RB_SRC_DIR)/svn_*.c $(SWIG_RB_SRC_DIR)/core.c @@ -925,4 +982,5 @@ INSTALL_EXTRA_TOOLS=\ ln -sf svnmucc$(EXEEXT) $(DESTDIR)$(bindir)/svnsyitf$(EXEEXT); \ if test "$(DESTDIR)$(bindir)" != "$(DESTDIR)$(toolsdir)"; then \ ln -sf $(bindir)/svnmucc$(EXEEXT) $(DESTDIR)$(toolsdir)/svnmucc$(EXEEXT); \ + ln -sf $(bindir)/svnbench$(EXEEXT) $(DESTDIR)$(toolsdir)/svn-bench$(EXEEXT); \ fi diff --git a/contrib/subversion/NOTICE b/contrib/subversion/NOTICE index 4241e0d87..0c4fe7d44 100644 --- a/contrib/subversion/NOTICE +++ b/contrib/subversion/NOTICE @@ -1,5 +1,5 @@ Apache Subversion -Copyright 2015 The Apache Software Foundation +Copyright 2016 The Apache Software Foundation This product includes software developed by many people, and distributed under Contributor License Agreements to The Apache Software Foundation @@ -21,3 +21,8 @@ Inc. MD5 Message-Digest Algorithm, including various modifications by Spyglass Inc., Carnegie Mellon University, and Bell Communications Research, Inc (Bellcore). +This product includes software developed by Public Software Group e. V. +under a permissive license, see LICENSE. + +This software contains code derived from TropicSSL under a BSD 3-Clause +license, see LICENSE. diff --git a/contrib/subversion/autogen.sh b/contrib/subversion/autogen.sh index 83b6364e3..e1961d6ff 100755 --- a/contrib/subversion/autogen.sh +++ b/contrib/subversion/autogen.sh @@ -63,7 +63,8 @@ done # ### APR's libtool. deferring to a second round of change... # -libtoolize="`./build/PrintPath glibtoolize libtoolize libtoolize15`" +# Much like APR except we do not prefer libtool 1 over libtool 2. +libtoolize="`./build/PrintPath glibtoolize libtoolize glibtoolize1 libtoolize15 libtoolize14`" lt_major_version=`$libtoolize --version 2>/dev/null | sed -e 's/^[^0-9]*//' -e 's/\..*//' -e '/^$/d' -e 1q` if [ "x$libtoolize" = "x" ]; then @@ -156,11 +157,11 @@ fi # # 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. +# that running gen-make.py requires Python 2.7 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 "Python 2.7 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" @@ -204,7 +205,7 @@ fi echo "Creating svn_private_config.h.in..." ${AUTOHEADER:-autoheader} -# If there's a config.cache file, we may need to delete it. +# 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 @@ -239,7 +240,7 @@ 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 "./configure CFLAGS='--flags-for-C' CXXFLAGS='--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." diff --git a/contrib/subversion/build-outputs.mk b/contrib/subversion/build-outputs.mk index 280c5d344..68acf271a 100644 --- a/contrib/subversion/build-outputs.mk +++ b/contrib/subversion/build-outputs.mk @@ -11,6 +11,9 @@ FS_BASE_LINK = ../../subversion/libsvn_fs_base/libsvn_fs_base-1.la ../../subvers 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 +FS_X_DEPS = subversion/libsvn_fs_x/libsvn_fs_x-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_X_LINK = ../../subversion/libsvn_fs_x/libsvn_fs_x-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 @@ -20,22 +23,25 @@ RA_SERF_LINK = ../../subversion/libsvn_ra_serf/libsvn_ra_serf-1.la ../../subvers 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 +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_repos subversion/tests/libsvn_fs_fs subversion/tests/libsvn_fs subversion/tests/libsvn_fs_x tools/dev 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/remote subversion/bindings/javahl/src/org/apache/subversion/javahl/types subversion/bindings/javahl/src/org/apache/subversion/javahl/util subversion/bindings/javahl/tests/org/apache/subversion/javahl gmock-fused 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_fs_x 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/cxxhl/src/aprwrap subversion/bindings/javahl/native subversion/bindings/javahl/native/jniwrapper 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/svn tools/server-side tools/dev/wc-ng subversion/svnadmin subversion/svnbench subversion/svndumpfilter subversion/svnfsfs subversion/svnlook subversion/svnmucc tools/dev/svnraisetreeconflict subversion/svnrdump subversion/svnserve subversion/svnsync subversion/svnversion subversion/bindings/swig subversion/tests/libsvn_wc/../../libsvn_subr 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/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_DEPS = subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/bit-array-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_repos/dump-load-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-fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_fs_x/fs-x-pack-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/cmdline/lock-helper$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_client/mtcc-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_subr/packed-data-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT) subversion/tests/libsvn_subr/priority-queue-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/root-pools-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/sqlite-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_fs_x/string-table-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) tools/dev/wc-ng/svn-wc-db-tester$(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/libsvn_subr/x509-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/svnfsfs_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 +TEST_PROGRAMS = subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/bit-array-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_repos/dump-load-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-fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_fs_x/fs-x-pack-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_client/mtcc-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_subr/packed-data-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT) subversion/tests/libsvn_subr/priority-queue-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/root-pools-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/sqlite-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_fs_x/string-table-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/libsvn_subr/x509-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/svnfsfs_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) +check-deps test-deps: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/cmdline/entries-dump$(EXEEXT) subversion/tests/cmdline/lock-helper$(EXEEXT) tools/dev/wc-ng/svn-wc-db-tester$(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/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 +CLEAN_FILES = subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svnbench/svnbench$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnfsfs/svnfsfs$(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-helper$(EXEEXT) 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/svnfsfs_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_client/mtcc-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-fs-fuzzy-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT) subversion/tests/libsvn_fs_x/fs-x-pack-test$(EXEEXT) subversion/tests/libsvn_fs_x/string-table-test$(EXEEXT) subversion/tests/libsvn_ra/ra-test$(EXEEXT) subversion/tests/libsvn_ra_local/ra-local-test$(EXEEXT) subversion/tests/libsvn_repos/dump-load-test$(EXEEXT) subversion/tests/libsvn_repos/repos-test$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/bit-array-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/opt-test$(EXEEXT) subversion/tests/libsvn_subr/packed-data-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT) subversion/tests/libsvn_subr/priority-queue-test$(EXEEXT) subversion/tests/libsvn_subr/revision-test$(EXEEXT) subversion/tests/libsvn_subr/root-pools-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/sqlite-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_subr/x509-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/dev/fsfs-access-map$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) tools/dev/wc-ng/svn-wc-db-tester$(EXEEXT) tools/dev/x509-parser$(EXEEXT) tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) +EXTRACLEAN_FILES = subversion/libsvn_fs_fs/rep-cache-db.h subversion/libsvn_fs_x/rep-cache-db.h subversion/libsvn_subr/internal_statements.h subversion/libsvn_wc/wc-queries.h subversion/tests/libsvn_wc/wc-test-queries.h \ + $(abs_builddir)/subversion/libsvn_subr/errorcode.inc \ + $(abs_builddir)/subversion/libsvn_subr/config_keys.inc \ + $(abs_srcdir)/compile_commands.json SWIG_INCLUDES = -I$(abs_builddir)/subversion \ -I$(abs_srcdir)/subversion/include \ @@ -88,186 +94,204 @@ autogen-swig: autogen-swig-rb ######################################## 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_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_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) +bit_array_test_PATH = subversion/tests/libsvn_subr +bit_array_test_DEPS = subversion/tests/libsvn_subr/bit-array-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +bit_array_test_OBJECTS = bit-array-test.lo +subversion/tests/libsvn_subr/bit-array-test$(EXEEXT): $(bit_array_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(bit_array_test_LDFLAGS) -o bit-array-test$(EXEEXT) $(bit_array_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_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_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_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_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_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_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_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_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 +cxxhl_tests_DEPS = subversion/bindings/cxxhl/tests/cxxhl-tests.lo subversion/bindings/cxxhl/tests/test_aprwrap.lo subversion/bindings/cxxhl/tests/test_exception.lo subversion/bindings/cxxhl/libsvncxxhl-1.la gmock-fused/libgmock-1.la subversion/libsvn_subr/libsvn_subr-1.la +cxxhl_tests_OBJECTS = tests/cxxhl-tests.lo tests/test_aprwrap.lo 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) + if $(SVN_USE_GMOCK) ; then cd subversion/bindings/cxxhl && $(LINK_CXX) $(cxxhl_tests_LDFLAGS) -o cxxhl-tests$(EXEEXT) $(cxxhl_tests_OBJECTS) ../../../subversion/bindings/cxxhl/libsvncxxhl-1.la ../../../gmock-fused/libgmock-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS) ; else echo "fake" > subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) ; fi 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_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_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_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_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_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_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) +dump_load_test_PATH = subversion/tests/libsvn_repos +dump_load_test_DEPS = subversion/tests/libsvn_repos/dump-load-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 +dump_load_test_OBJECTS = dump-load-test.lo +subversion/tests/libsvn_repos/dump-load-test$(EXEEXT): $(dump_load_test_DEPS) + cd subversion/tests/libsvn_repos && $(LINK) $(dump_load_test_LDFLAGS) -o dump-load-test$(EXEEXT) $(dump_load_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) + 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_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_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_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_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_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_fs_fuzzy_test_PATH = subversion/tests/libsvn_fs_fs +fs_fs_fuzzy_test_DEPS = subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-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_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la +fs_fs_fuzzy_test_OBJECTS = fs-fs-fuzzy-test.lo +subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test$(EXEEXT): $(fs_fs_fuzzy_test_DEPS) + cd subversion/tests/libsvn_fs_fs && $(LINK) $(fs_fs_fuzzy_test_LDFLAGS) -o fs-fs-fuzzy-test$(EXEEXT) $(fs_fs_fuzzy_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_repos/libsvn_repos-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS) + +fs_fs_pack_test_PATH = subversion/tests/libsvn_fs_fs +fs_fs_pack_test_DEPS = subversion/tests/libsvn_fs_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_fs_pack_test_OBJECTS = fs-fs-pack-test.lo +subversion/tests/libsvn_fs_fs/fs-fs-pack-test$(EXEEXT): $(fs_fs_pack_test_DEPS) + cd subversion/tests/libsvn_fs_fs && $(LINK) $(fs_fs_pack_test_LDFLAGS) -o fs-fs-pack-test$(EXEEXT) $(fs_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_fs_private_test_PATH = subversion/tests/libsvn_fs_fs +fs_fs_private_test_DEPS = subversion/tests/libsvn_fs_fs/fs-fs-private-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_repos/libsvn_repos-1.la subversion/libsvn_subr/libsvn_subr-1.la +fs_fs_private_test_OBJECTS = fs-fs-private-test.lo +subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT): $(fs_fs_private_test_DEPS) + cd subversion/tests/libsvn_fs_fs && $(LINK) $(fs_fs_private_test_LDFLAGS) -o fs-fs-private-test$(EXEEXT) $(fs_fs_private_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_repos/libsvn_repos-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_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_fs_util/libsvn_fs_util-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) + 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_fs_util/libsvn_fs_util-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS) + +fs_x_pack_test_PATH = subversion/tests/libsvn_fs_x +fs_x_pack_test_DEPS = subversion/tests/libsvn_fs_x/fs-x-pack-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_fs_x/libsvn_fs_x-1.la subversion/libsvn_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la +fs_x_pack_test_OBJECTS = fs-x-pack-test.lo +subversion/tests/libsvn_fs_x/fs-x-pack-test$(EXEEXT): $(fs_x_pack_test_DEPS) + cd subversion/tests/libsvn_fs_x && $(LINK) $(fs_x_pack_test_LDFLAGS) -o fs-x-pack-test$(EXEEXT) $(fs_x_pack_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_fs_x/libsvn_fs_x-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_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_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_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_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_AuthnCallback.h 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_ConfigEvent.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_RemoteFileRevisionsCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_RemoteLocationSegmentsCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_RemoteStatus.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_ReposVerifyCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_StatusCallback.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_TunnelAgent.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_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_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/AuthnCallback.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/ConfigEvent.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/RemoteFileRevisionsCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteStatus.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/ReposVerifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/TunnelAgent.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class +javahl_callback_javah_CLASSES = org.apache.subversion.javahl.callback.AuthnCallback 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.ConfigEvent 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.RemoteFileRevisionsCallback org.apache.subversion.javahl.callback.RemoteLocationSegmentsCallback org.apache.subversion.javahl.callback.RemoteStatus org.apache.subversion.javahl.callback.ReposFreezeAction org.apache.subversion.javahl.callback.ReposNotifyCallback org.apache.subversion.javahl.callback.ReposVerifyCallback org.apache.subversion.javahl.callback.StatusCallback org.apache.subversion.javahl.callback.TunnelAgent 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) @@ -275,120 +299,154 @@ $(javahl_callback_javah_HEADERS): $(javahl_callback_javah_CLASS_FILENAMES) 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_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) + $(COMPILE_JAVAHL_COMPAT_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_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) + $(COMPILE_JAVAHL_COMPAT_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_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/ISVNConfig.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNEditor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRemote.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNReporter.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/JNIObject.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/OperationContext.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/SVNUtil.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SubversionException.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/AuthnCallback.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/ConfigEvent.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/RemoteFileRevisionsCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteStatus.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/ReposVerifyCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/StatusCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/TunnelAgent.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/UserPasswordCallback.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/CommitEditor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteFactory.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteSession.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RetryOpenSession.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StateReporter.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StatusEditor.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/ExternalItem.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/NativeInputStream.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NativeOutputStream.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/RevisionRangeList.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RuntimeVersion.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 subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigImpl.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/DiffLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/PropLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/RequestChannel.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ResponseChannel.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/SubstLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/TunnelChannel.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_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/ISVNConfig.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNReporter.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/JNIObject.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/OperationContext.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/SVNUtil.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.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/ConfigEvent.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/RemoteFileRevisionsCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.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/ReposVerifyCallback.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/TunnelAgent.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/UserPasswordCallback.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RetryOpenSession.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.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/ExternalItem.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/NativeInputStream.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeOutputStream.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/RevisionRangeList.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RuntimeVersion.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 $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ConfigImpl.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ConfigLib.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/DiffLib.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/PropLib.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/SubstLib.java $(abs_srcdir)/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.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_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_ISVNConfig.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ISVNEditor.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ISVNRemote.h subversion/bindings/javahl/include/org_apache_subversion_javahl_ISVNReporter.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_JNIObject.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_OperationContext.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_SVNUtil.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_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_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/ISVNConfig.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNEditor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRemote.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNReporter.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/JNIObject.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/OperationContext.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/SVNUtil.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.ISVNConfig org.apache.subversion.javahl.ISVNEditor org.apache.subversion.javahl.ISVNRemote org.apache.subversion.javahl.ISVNReporter org.apache.subversion.javahl.ISVNRepos org.apache.subversion.javahl.JNIError org.apache.subversion.javahl.JNIObject org.apache.subversion.javahl.NativeException org.apache.subversion.javahl.NativeResources org.apache.subversion.javahl.OperationContext org.apache.subversion.javahl.ProgressEvent org.apache.subversion.javahl.ReposNotifyInformation org.apache.subversion.javahl.SVNClient org.apache.subversion.javahl.SVNRepos org.apache.subversion.javahl.SVNUtil 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_remote_javah_PATH = subversion/bindings/javahl/include +javahl_remote_javah_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_CommitEditor.h subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteFactory.h subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteSession.h subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RetryOpenSession.h subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_StateReporter.h subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_StatusEditor.h +javahl_remote_javah_OBJECTS = +javahl_remote_javah_DEPS = $(javahl_remote_javah_HEADERS) $(javahl_remote_javah_OBJECTS) $(javahl_java_DEPS) +javahl-remote-javah: $(javahl_remote_javah_DEPS) +javahl_remote_javah_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/CommitEditor.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteFactory.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteSession.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RetryOpenSession.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StateReporter.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StatusEditor.class +javahl_remote_javah_CLASSES = org.apache.subversion.javahl.remote.CommitEditor org.apache.subversion.javahl.remote.RemoteFactory org.apache.subversion.javahl.remote.RemoteSession org.apache.subversion.javahl.remote.RetryOpenSession org.apache.subversion.javahl.remote.StateReporter org.apache.subversion.javahl.remote.StatusEditor +$(javahl_remote_javah_HEADERS): $(javahl_remote_javah_CLASS_FILENAMES) + $(COMPILE_JAVAHL_JAVAH) -force -d subversion/bindings/javahl/include -classpath subversion/bindings/javahl/classes:$(javahl_remote_javah_CLASSPATH) $(javahl_remote_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_OBJECTS = subversion/bindings/javahl/classes/org/apache/subversion/javahl/BasicTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/ExceptionTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/RunTests.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNRemoteTests.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/UtilTests.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_SRC = $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/RunTests.java $(abs_srcdir)/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.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/UtilTests.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_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_ExternalItem.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_NativeInputStream.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeOutputStream.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_RevisionRangeList.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_RuntimeVersion.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_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_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/ExternalItem.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/NativeInputStream.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NativeOutputStream.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/RevisionRangeList.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RuntimeVersion.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.ExternalItem 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.NativeInputStream org.apache.subversion.javahl.types.NativeOutputStream 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.RevisionRangeList org.apache.subversion.javahl.types.RuntimeVersion 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) +javahl_util_javah_PATH = subversion/bindings/javahl/include +javahl_util_javah_HEADERS = subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigImpl.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_DiffLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_PropLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_RequestChannel.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ResponseChannel.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_SubstLib.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_TunnelChannel.h +javahl_util_javah_OBJECTS = +javahl_util_javah_DEPS = $(javahl_util_javah_HEADERS) $(javahl_util_javah_OBJECTS) $(javahl_java_DEPS) +javahl-util-javah: $(javahl_util_javah_DEPS) +javahl_util_javah_CLASS_FILENAMES = subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigImpl.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/DiffLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/PropLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/RequestChannel.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ResponseChannel.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/SubstLib.class subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/TunnelChannel.class +javahl_util_javah_CLASSES = org.apache.subversion.javahl.util.ConfigImpl org.apache.subversion.javahl.util.ConfigLib org.apache.subversion.javahl.util.DiffLib org.apache.subversion.javahl.util.PropLib org.apache.subversion.javahl.util.RequestChannel org.apache.subversion.javahl.util.ResponseChannel org.apache.subversion.javahl.util.SubstLib org.apache.subversion.javahl.util.TunnelChannel +$(javahl_util_javah_HEADERS): $(javahl_util_javah_CLASS_FILENAMES) + $(COMPILE_JAVAHL_JAVAH) -force -d subversion/bindings/javahl/include -classpath subversion/bindings/javahl/classes:$(javahl_util_javah_CLASSPATH) $(javahl_util_javah_CLASSES) + + +libgmock_PATH = gmock-fused +libgmock_DEPS = +libgmock_OBJECTS = +gmock-fused/libgmock-1.la: $(libgmock_DEPS) + if $(SVN_USE_GMOCK) ; then cd gmock-fused && $(LINK_CXX_LIB) $(libgmock_LDFLAGS) -o libgmock-1.la $(LT_NO_UNDEFINED) $(libgmock_OBJECTS) $(LIBS) ; else echo "fake" > gmock-fused/libgmock-1.la ; fi + 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_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_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 +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/mtcc.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 mtcc.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_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 +libsvn_diff_DEPS = subversion/libsvn_diff/binary_diff.lo 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 = binary_diff.lo 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 +libsvn_fs_DEPS = $(SVN_FS_LIB_DEPS) subversion/libsvn_fs/access.lo subversion/libsvn_fs/deprecated.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 deprecated.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_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 +libsvn_fs_fs_DEPS = subversion/libsvn_fs_fs/cached_data.lo subversion/libsvn_fs_fs/caching.lo subversion/libsvn_fs_fs/dag.lo subversion/libsvn_fs_fs/dump-index.lo subversion/libsvn_fs_fs/fs.lo subversion/libsvn_fs_fs/fs_fs.lo subversion/libsvn_fs_fs/hotcopy.lo subversion/libsvn_fs_fs/id.lo subversion/libsvn_fs_fs/index.lo subversion/libsvn_fs_fs/load-index.lo subversion/libsvn_fs_fs/lock.lo subversion/libsvn_fs_fs/low_level.lo subversion/libsvn_fs_fs/pack.lo subversion/libsvn_fs_fs/recovery.lo subversion/libsvn_fs_fs/rep-cache.lo subversion/libsvn_fs_fs/rev_file.lo subversion/libsvn_fs_fs/revprops.lo subversion/libsvn_fs_fs/stats.lo subversion/libsvn_fs_fs/temp_serializer.lo subversion/libsvn_fs_fs/transaction.lo subversion/libsvn_fs_fs/tree.lo subversion/libsvn_fs_fs/util.lo subversion/libsvn_fs_fs/verify.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 = cached_data.lo caching.lo dag.lo dump-index.lo fs.lo fs_fs.lo hotcopy.lo id.lo index.lo load-index.lo lock.lo low_level.lo pack.lo recovery.lo rep-cache.lo rev_file.lo revprops.lo stats.lo temp_serializer.lo transaction.lo tree.lo util.lo verify.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_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_fs_x_PATH = subversion/libsvn_fs_x +libsvn_fs_x_DEPS = subversion/libsvn_fs_x/cached_data.lo subversion/libsvn_fs_x/caching.lo subversion/libsvn_fs_x/changes.lo subversion/libsvn_fs_x/dag.lo subversion/libsvn_fs_x/fs.lo subversion/libsvn_fs_x/fs_id.lo subversion/libsvn_fs_x/fs_x.lo subversion/libsvn_fs_x/hotcopy.lo subversion/libsvn_fs_x/id.lo subversion/libsvn_fs_x/index.lo subversion/libsvn_fs_x/lock.lo subversion/libsvn_fs_x/low_level.lo subversion/libsvn_fs_x/noderevs.lo subversion/libsvn_fs_x/pack.lo subversion/libsvn_fs_x/recovery.lo subversion/libsvn_fs_x/rep-cache.lo subversion/libsvn_fs_x/reps.lo subversion/libsvn_fs_x/rev_file.lo subversion/libsvn_fs_x/revprops.lo subversion/libsvn_fs_x/string_table.lo subversion/libsvn_fs_x/temp_serializer.lo subversion/libsvn_fs_x/transaction.lo subversion/libsvn_fs_x/tree.lo subversion/libsvn_fs_x/util.lo subversion/libsvn_fs_x/verify.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_x_OBJECTS = cached_data.lo caching.lo changes.lo dag.lo fs.lo fs_id.lo fs_x.lo hotcopy.lo id.lo index.lo lock.lo low_level.lo noderevs.lo pack.lo recovery.lo rep-cache.lo reps.lo rev_file.lo revprops.lo string_table.lo temp_serializer.lo transaction.lo tree.lo util.lo verify.lo +subversion/libsvn_fs_x/libsvn_fs_x-1.la: $(libsvn_fs_x_DEPS) + cd subversion/libsvn_fs_x && $(LINK_LIB) $(libsvn_fs_x_LDFLAGS) -o libsvn_fs_x-1.la $(LT_NO_UNDEFINED) $(libsvn_fs_x_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_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 @@ -397,513 +455,567 @@ 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_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 +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/eagain_bucket.lo subversion/libsvn_ra_serf/get_deleted_rev.lo subversion/libsvn_ra_serf/get_file.lo subversion/libsvn_ra_serf/get_lock.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/lock.lo subversion/libsvn_ra_serf/log.lo subversion/libsvn_ra_serf/merge.lo subversion/libsvn_ra_serf/mergeinfo.lo subversion/libsvn_ra_serf/multistatus.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/stat.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 eagain_bucket.lo get_deleted_rev.lo get_file.lo get_lock.lo getdate.lo getlocations.lo getlocationsegments.lo getlocks.lo inherited_props.lo lock.lo log.lo merge.lo mergeinfo.lo multistatus.lo options.lo property.lo replay.lo sb_bucket.lo serf.lo stat.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) + 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) $(SVN_ZLIB_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_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 +libsvn_repos_DEPS = subversion/libsvn_repos/authz.lo subversion/libsvn_repos/authz_pool.lo subversion/libsvn_repos/commit.lo subversion/libsvn_repos/config_pool.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 authz_pool.lo commit.lo config_pool.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 +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/bit_array.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/compress.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/fnv1a.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/nls.lo subversion/libsvn_subr/object_pool.lo subversion/libsvn_subr/opt.lo subversion/libsvn_subr/packed_data.lo subversion/libsvn_subr/path.lo subversion/libsvn_subr/pool.lo subversion/libsvn_subr/prefix_string.lo subversion/libsvn_subr/prompt.lo subversion/libsvn_subr/properties.lo subversion/libsvn_subr/quoprint.lo subversion/libsvn_subr/root_pools.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/utf8proc.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/x509info.lo subversion/libsvn_subr/x509parse.lo subversion/libsvn_subr/xml.lo +libsvn_subr_OBJECTS = adler32.lo atomic.lo auth.lo base64.lo bit_array.lo cache-inprocess.lo cache-membuffer.lo cache-memcache.lo cache.lo cache_config.lo checksum.lo cmdline.lo compat.lo compress.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 fnv1a.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 nls.lo object_pool.lo opt.lo packed_data.lo path.lo pool.lo prefix_string.lo prompt.lo properties.lo quoprint.lo root_pools.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 utf8proc.lo utf_validate.lo utf_width.lo validate.lo version.lo win32_crashrpt.lo win32_crypto.lo win32_xlate.lo x509info.lo x509parse.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) + 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) $(SVN_INTL_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_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) + 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) $(SVN_PERL_LIBS) $(SVN_SWIG_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_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) + 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) $(SVN_PYTHON_LIBS) $(SVN_SWIG_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_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) + 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) $(SVN_RUBY_LIBS) $(SVN_SWIG_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_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_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 +libsvncxxhl_DEPS = subversion/bindings/cxxhl/src/aprwrap/impl.lo 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/aprwrap/impl.lo 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 +libsvnjavahl_DEPS = $(javahl_java_DEPS) $(javahl_callback_javah_DEPS) $(javahl_remote_javah_DEPS) $(javahl_types_javah_DEPS) $(javahl_util_javah_DEPS) $(javahl_javah_DEPS) subversion/bindings/javahl/native/Array.lo subversion/bindings/javahl/native/AuthnCallback.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/CommitEditor.lo subversion/bindings/javahl/native/CommitMessage.lo subversion/bindings/javahl/native/CopySources.lo subversion/bindings/javahl/native/CreateJ.lo subversion/bindings/javahl/native/Credential.lo subversion/bindings/javahl/native/DiffOptions.lo subversion/bindings/javahl/native/DiffSummaryReceiver.lo subversion/bindings/javahl/native/EditorCallbacks.lo subversion/bindings/javahl/native/EditorProxy.lo subversion/bindings/javahl/native/EnumMapper.lo subversion/bindings/javahl/native/ExternalItem.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/Iterator.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/JNIUtil.lo subversion/bindings/javahl/native/ListCallback.lo subversion/bindings/javahl/native/LockTokenTable.lo subversion/bindings/javahl/native/LogMessageCallback.lo subversion/bindings/javahl/native/MessageReceiver.lo subversion/bindings/javahl/native/NativeStream.lo subversion/bindings/javahl/native/OperationContext.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/PropertyTable.lo subversion/bindings/javahl/native/ProplistCallback.lo subversion/bindings/javahl/native/RemoteSession.lo subversion/bindings/javahl/native/RemoteSessionContext.lo subversion/bindings/javahl/native/ReposFreezeAction.lo subversion/bindings/javahl/native/ReposNotifyCallback.lo subversion/bindings/javahl/native/ReposVerifyCallback.lo subversion/bindings/javahl/native/Revision.lo subversion/bindings/javahl/native/RevisionRange.lo subversion/bindings/javahl/native/RevisionRangeList.lo subversion/bindings/javahl/native/SVNBase.lo subversion/bindings/javahl/native/SVNClient.lo subversion/bindings/javahl/native/SVNRepos.lo subversion/bindings/javahl/native/StateReporter.lo subversion/bindings/javahl/native/StatusCallback.lo subversion/bindings/javahl/native/StringArray.lo subversion/bindings/javahl/native/SubversionException.lo subversion/bindings/javahl/native/Targets.lo subversion/bindings/javahl/native/Utility.lo subversion/bindings/javahl/native/VersionExtended.lo subversion/bindings/javahl/native/deprecated.lo subversion/bindings/javahl/native/jniwrapper/jni_base.lo subversion/bindings/javahl/native/jniwrapper/jni_channel.lo subversion/bindings/javahl/native/jniwrapper/jni_class_cache.lo subversion/bindings/javahl/native/jniwrapper/jni_io_stream.lo subversion/bindings/javahl/native/jniwrapper/jni_iterator.lo subversion/bindings/javahl/native/jniwrapper/jni_list.lo subversion/bindings/javahl/native/jniwrapper/jni_string_map.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_remote_CommitEditor.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.lo subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.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 AuthnCallback.lo BlameCallback.lo ChangelistCallback.lo ClientContext.lo CommitCallback.lo CommitEditor.lo CommitMessage.lo CopySources.lo CreateJ.lo Credential.lo DiffOptions.lo DiffSummaryReceiver.lo EditorCallbacks.lo EditorProxy.lo EnumMapper.lo ExternalItem.lo File.lo ImportFilterCallback.lo InfoCallback.lo InputStream.lo Iterator.lo JNIByteArray.lo JNICriticalSection.lo JNIMutex.lo JNIStackElement.lo JNIStringHolder.lo JNIUtil.lo ListCallback.lo LockTokenTable.lo LogMessageCallback.lo MessageReceiver.lo NativeStream.lo OperationContext.lo OutputStream.lo PatchCallback.lo Path.lo Pool.lo Prompter.lo PropertyTable.lo ProplistCallback.lo RemoteSession.lo RemoteSessionContext.lo ReposFreezeAction.lo ReposNotifyCallback.lo ReposVerifyCallback.lo Revision.lo RevisionRange.lo RevisionRangeList.lo SVNBase.lo SVNClient.lo SVNRepos.lo StateReporter.lo StatusCallback.lo StringArray.lo SubversionException.lo Targets.lo Utility.lo VersionExtended.lo deprecated.lo jniwrapper/jni_base.lo jniwrapper/jni_channel.lo jniwrapper/jni_class_cache.lo jniwrapper/jni_io_stream.lo jniwrapper/jni_iterator.lo jniwrapper/jni_list.lo jniwrapper/jni_string_map.lo org_apache_subversion_javahl_NativeResources.lo org_apache_subversion_javahl_SVNClient.lo org_apache_subversion_javahl_SVNRepos.lo org_apache_subversion_javahl_remote_CommitEditor.lo org_apache_subversion_javahl_remote_RemoteFactory.lo org_apache_subversion_javahl_remote_RemoteSession.lo org_apache_subversion_javahl_remote_StateReporter.lo org_apache_subversion_javahl_types_RevisionRangeList.lo org_apache_subversion_javahl_types_RuntimeVersion.lo org_apache_subversion_javahl_types_Version.lo org_apache_subversion_javahl_types_VersionExtended.lo org_apache_subversion_javahl_util_ConfigImpl_Category.lo org_apache_subversion_javahl_util_ConfigLib.lo org_apache_subversion_javahl_util_DiffLib.lo org_apache_subversion_javahl_util_PropLib.lo org_apache_subversion_javahl_util_SubstLib.lo org_apache_subversion_javahl_util_TunnelChannel.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) + 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) $(SVN_JAVA_SDK_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_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) +lock_helper_PATH = subversion/tests/cmdline +lock_helper_DEPS = subversion/tests/cmdline/lock-helper.lo subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_subr/libsvn_subr-1.la +lock_helper_OBJECTS = lock-helper.lo +subversion/tests/cmdline/lock-helper$(EXEEXT): $(lock_helper_DEPS) + cd subversion/tests/cmdline && $(LINK) $(lock_helper_LDFLAGS) -o lock-helper$(EXEEXT) $(lock_helper_OBJECTS) ../../../subversion/libsvn_fs/libsvn_fs-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS) + 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_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_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_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 + 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 $(SVN_HTTPD_LIBS) $(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 +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/status.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 status.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 + 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 $(SVN_HTTPD_LIBS) $(SVN_MOD_DAV_LIBS) $(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_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 + 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) $(SVN_HTTPD_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) +mtcc_test_PATH = subversion/tests/libsvn_client +mtcc_test_DEPS = subversion/tests/libsvn_client/mtcc-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 +mtcc_test_OBJECTS = mtcc-test.lo +subversion/tests/libsvn_client/mtcc-test$(EXEEXT): $(mtcc_test_DEPS) + cd subversion/tests/libsvn_client && $(LINK) $(mtcc_test_LDFLAGS) -o mtcc-test$(EXEEXT) $(mtcc_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) 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_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_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) +packed_data_test_PATH = subversion/tests/libsvn_subr +packed_data_test_DEPS = subversion/tests/libsvn_subr/packed-data-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +packed_data_test_OBJECTS = packed-data-test.lo +subversion/tests/libsvn_subr/packed-data-test$(EXEEXT): $(packed_data_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(packed_data_test_LDFLAGS) -o packed-data-test$(EXEEXT) $(packed_data_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(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_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_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_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_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_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_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_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_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_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_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) +prefix_string_test_PATH = subversion/tests/libsvn_subr +prefix_string_test_DEPS = subversion/tests/libsvn_subr/prefix-string-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +prefix_string_test_OBJECTS = prefix-string-test.lo +subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT): $(prefix_string_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(prefix_string_test_LDFLAGS) -o prefix-string-test$(EXEEXT) $(prefix_string_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS) + +priority_queue_test_PATH = subversion/tests/libsvn_subr +priority_queue_test_DEPS = subversion/tests/libsvn_subr/priority-queue-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +priority_queue_test_OBJECTS = priority-queue-test.lo +subversion/tests/libsvn_subr/priority-queue-test$(EXEEXT): $(priority_queue_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(priority_queue_test_LDFLAGS) -o priority-queue-test$(EXEEXT) $(priority_queue_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(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_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_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_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_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_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_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_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_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_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_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_DEPS = subversion/tests/libsvn_ra/ra-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_ra/libsvn_ra-1.la subversion/libsvn_ra_svn/libsvn_ra_svn-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) + 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_ra_svn/libsvn_ra_svn-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_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_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_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) +root_pools_test_PATH = subversion/tests/libsvn_subr +root_pools_test_DEPS = subversion/tests/libsvn_subr/root-pools-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +root_pools_test_OBJECTS = root-pools-test.lo +subversion/tests/libsvn_subr/root-pools-test$(EXEEXT): $(root_pools_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(root_pools_test_LDFLAGS) -o root-pools-test$(EXEEXT) $(root_pools_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(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_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_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_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_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_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_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_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_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_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_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) +sqlite_test_PATH = subversion/tests/libsvn_subr +sqlite_test_DEPS = subversion/tests/libsvn_subr/sqlite-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +sqlite_test_OBJECTS = sqlite-test.lo +subversion/tests/libsvn_subr/sqlite-test$(EXEEXT): $(sqlite_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(sqlite_test_LDFLAGS) -o sqlite-test$(EXEEXT) $(sqlite_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_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_table_test_PATH = subversion/tests/libsvn_fs_x +string_table_test_DEPS = subversion/tests/libsvn_fs_x/string-table-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_fs_x/libsvn_fs_x-1.la subversion/libsvn_subr/libsvn_subr-1.la +string_table_test_OBJECTS = string-table-test.lo +subversion/tests/libsvn_fs_x/string-table-test$(EXEEXT): $(string_table_test_DEPS) + cd subversion/tests/libsvn_fs_x && $(LINK) $(string_table_test_LDFLAGS) -o string-table-test$(EXEEXT) $(string_table_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_fs_x/libsvn_fs_x-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(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_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_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_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 +svn_DEPS = subversion/svn/add-cmd.lo subversion/svn/auth-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/similarity.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 auth-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 similarity.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_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) +svn_wc_db_tester_PATH = tools/dev/wc-ng +svn_wc_db_tester_DEPS = tools/dev/wc-ng/svn-wc-db-tester.lo subversion/libsvn_wc/libsvn_wc-1.la subversion/libsvn_subr/libsvn_subr-1.la +svn_wc_db_tester_OBJECTS = svn-wc-db-tester.lo +tools/dev/wc-ng/svn-wc-db-tester$(EXEEXT): $(svn_wc_db_tester_DEPS) + cd tools/dev/wc-ng && $(LINK) $(svn_wc_db_tester_LDFLAGS) -o svn-wc-db-tester$(EXEEXT) $(svn_wc_db_tester_OBJECTS) ../../../subversion/libsvn_wc/libsvn_wc-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(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_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_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_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) +svnbench_PATH = subversion/svnbench +svnbench_DEPS = subversion/svnbench/help-cmd.lo subversion/svnbench/notify.lo subversion/svnbench/null-blame-cmd.lo subversion/svnbench/null-export-cmd.lo subversion/svnbench/null-info-cmd.lo subversion/svnbench/null-list-cmd.lo subversion/svnbench/null-log-cmd.lo subversion/svnbench/svnbench.lo subversion/svnbench/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 +svnbench_OBJECTS = help-cmd.lo notify.lo null-blame-cmd.lo null-export-cmd.lo null-info-cmd.lo null-list-cmd.lo null-log-cmd.lo svnbench.lo util.lo +subversion/svnbench/svnbench$(EXEEXT): $(svnbench_DEPS) + cd subversion/svnbench && $(LINK) $(svnbench_LDFLAGS) -o svnbench$(EXEEXT) $(svnbench_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) + 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_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_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) +svnfsfs_PATH = subversion/svnfsfs +svnfsfs_DEPS = subversion/svnfsfs/dump-index-cmd.lo subversion/svnfsfs/load-index-cmd.lo subversion/svnfsfs/stats-cmd.lo subversion/svnfsfs/svnfsfs.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_delta/libsvn_delta-1.la subversion/libsvn_subr/libsvn_subr-1.la +svnfsfs_OBJECTS = dump-index-cmd.lo load-index-cmd.lo stats-cmd.lo svnfsfs.lo +subversion/svnfsfs/svnfsfs$(EXEEXT): $(svnfsfs_DEPS) + cd subversion/svnfsfs && $(LINK) $(svnfsfs_LDFLAGS) -o svnfsfs$(EXEEXT) $(svnfsfs_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_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_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_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_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_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 +svnserve_DEPS = subversion/svnserve/cyrus_auth.lo subversion/svnserve/log-escape.lo subversion/svnserve/logger.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 logger.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_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_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_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_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_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_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_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_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 +wc_queries_test_DEPS = subversion/libsvn_subr/sqlite3wrapper.lo 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 = ../../libsvn_subr/sqlite3wrapper.lo 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_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_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) +x509_parser_PATH = tools/dev +x509_parser_DEPS = tools/dev/x509-parser.lo subversion/libsvn_subr/libsvn_subr-1.la +x509_parser_OBJECTS = x509-parser.lo +tools/dev/x509-parser$(EXEEXT): $(x509_parser_DEPS) + cd tools/dev && $(LINK) $(x509_parser_LDFLAGS) -o x509-parser$(EXEEXT) $(x509_parser_OBJECTS) ../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APR_LIBS) $(LIBS) + +x509_test_PATH = subversion/tests/libsvn_subr +x509_test_DEPS = subversion/tests/libsvn_subr/x509-test.lo subversion/tests/libsvn_test-1.la subversion/libsvn_subr/libsvn_subr-1.la +x509_test_OBJECTS = x509-test.lo +subversion/tests/libsvn_subr/x509-test$(EXEEXT): $(x509_test_DEPS) + cd subversion/tests/libsvn_subr && $(LINK) $(x509_test_LDFLAGS) -o x509-test$(EXEEXT) $(x509_test_OBJECTS) ../../../subversion/tests/libsvn_test-1.la ../../../subversion/libsvn_subr/libsvn_subr-1.la $(SVN_APRUTIL_LIBS) $(SVN_APR_LIBS) $(LIBS) + ######################################## # Section 6: Install-Group build targets @@ -915,11 +1027,11 @@ 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) +bin: subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svnbench/svnbench$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnfsfs/svnfsfs$(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 +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_fs_x/libsvn_fs_x-1.la subversion/libsvn_subr/libsvn_subr-1.la gnome-keyring-lib: subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la @@ -935,10 +1047,14 @@ javahl-javah: javahl-lib: subversion/bindings/javahl/native/libsvnjavahl-1.la +javahl-remote-javah: + javahl-tests: javahl-types-javah: +javahl-util-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 @@ -949,7 +1065,7 @@ ramod-lib: subversion/libsvn_fs/libsvn_fs-1.la subversion/libsvn_ra_local/libsvn serf-lib: subversion/libsvn_ra_serf/libsvn_ra_serf-1.la -sub-test: subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT) +sub-test: subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test$(EXEEXT) swig-pl-lib: subversion/bindings/swig/perl/libsvn_swig_perl/libsvn_swig_perl-1.la @@ -961,11 +1077,11 @@ swig-rb: subversion/bindings/swig/ruby/client.la subversion/bindings/swig/ruby/c 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) +test: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) subversion/tests/libsvn_subr/auth-test$(EXEEXT) subversion/tests/libsvn_subr/bit-array-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_repos/dump-load-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-fs-pack-test$(EXEEXT) subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT) subversion/tests/libsvn_fs/fs-test$(EXEEXT) subversion/tests/libsvn_fs_x/fs-x-pack-test$(EXEEXT) subversion/tests/libsvn_subr/hashdump-test$(EXEEXT) subversion/tests/libsvn_subr/io-test$(EXEEXT) subversion/tests/libsvn_test-1.la subversion/tests/cmdline/lock-helper$(EXEEXT) subversion/tests/libsvn_fs/locks-test$(EXEEXT) subversion/tests/libsvn_subr/mergeinfo-test$(EXEEXT) subversion/tests/libsvn_client/mtcc-test$(EXEEXT) subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) subversion/tests/libsvn_subr/opt-test$(EXEEXT) subversion/tests/libsvn_subr/packed-data-test$(EXEEXT) subversion/tests/libsvn_diff/parse-diff-test$(EXEEXT) subversion/tests/libsvn_subr/path-test$(EXEEXT) subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT) subversion/tests/libsvn_subr/priority-queue-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/root-pools-test$(EXEEXT) subversion/tests/libsvn_subr/skel-test$(EXEEXT) subversion/tests/libsvn_subr/spillbuf-test$(EXEEXT) subversion/tests/libsvn_subr/sqlite-test$(EXEEXT) subversion/tests/libsvn_subr/stream-test$(EXEEXT) subversion/tests/libsvn_fs_x/string-table-test$(EXEEXT) subversion/tests/libsvn_subr/string-test$(EXEEXT) subversion/tests/libsvn_subr/subst_translate-test$(EXEEXT) tools/dev/wc-ng/svn-wc-db-tester$(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/libsvn_subr/x509-test$(EXEEXT) -tests: subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) +tests: subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) gmock-fused/libgmock-1.la -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) +tools: tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/server-side/mod_dontdothat/mod_dontdothat.la tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) tools/dev/x509-parser$(EXEEXT) ######################################## @@ -977,36 +1093,58 @@ install-mods-shared: subversion/mod_dav_svn/mod_dav_svn.la subversion/mod_authz_ 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) + $(MKDIR) $(DESTDIR)$(bdb_libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_fs_base ; $(INSTALL_BDB_LIB) libsvn_fs_base-1.la $(DESTDIR)$(bdb_libdir)/libsvn_fs_base-1.la + $(INSTALL_DATA) subversion/libsvn_fs_base/libsvn_fs_base.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_fs_base.pc -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) +install-bin: subversion/svn/svn$(EXEEXT) subversion/svnadmin/svnadmin$(EXEEXT) subversion/svnbench/svnbench$(EXEEXT) subversion/svndumpfilter/svndumpfilter$(EXEEXT) subversion/svnfsfs/svnfsfs$(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/svnbench ; $(INSTALL_BIN) svnbench$(EXEEXT) $(DESTDIR)$(bindir)/svnbench$(EXEEXT) + cd subversion/svndumpfilter ; $(INSTALL_BIN) svndumpfilter$(EXEEXT) $(DESTDIR)$(bindir)/svndumpfilter$(EXEEXT) + + cd subversion/svnfsfs ; $(INSTALL_BIN) svnfsfs$(EXEEXT) $(DESTDIR)$(bindir)/svnfsfs$(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) +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 subversion/libsvn_fs_x/libsvn_fs_x-1.la + $(MKDIR) $(DESTDIR)$(fsmod_libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_subr ; $(INSTALL_FSMOD_LIB) libsvn_subr-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_subr-1.la + $(INSTALL_DATA) subversion/libsvn_subr/libsvn_subr.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_subr.pc cd subversion/libsvn_delta ; $(INSTALL_FSMOD_LIB) libsvn_delta-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_delta-1.la + $(INSTALL_DATA) subversion/libsvn_delta/libsvn_delta.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_delta.pc cd subversion/libsvn_fs_util ; $(INSTALL_FSMOD_LIB) libsvn_fs_util-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_fs_util-1.la + $(INSTALL_DATA) subversion/libsvn_fs_util/libsvn_fs_util.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_fs_util.pc cd subversion/libsvn_fs_fs ; $(INSTALL_FSMOD_LIB) libsvn_fs_fs-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_fs_fs-1.la + $(INSTALL_DATA) subversion/libsvn_fs_fs/libsvn_fs_fs.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_fs_fs.pc + cd subversion/libsvn_fs_x ; $(INSTALL_FSMOD_LIB) libsvn_fs_x-1.la $(DESTDIR)$(fsmod_libdir)/libsvn_fs_x-1.la + $(INSTALL_DATA) subversion/libsvn_fs_x/libsvn_fs_x.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_fs_x.pc install-gnome-keyring-lib: subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring-1.la - $(MKDIR) $(DESTDIR)$(gnome_keyring_libdir) + $(MKDIR) $(DESTDIR)$(gnome_keyring_libdir) $(DESTDIR)$(pkgconfig_dir) 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_DATA) subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_auth_gnome_keyring.pc install-javahl-callback-javah: $(MKDIR) $(DESTDIR)$(javahl_callback_javahdir) @@ -1031,8 +1169,13 @@ install-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-remote-javah: + $(MKDIR) $(DESTDIR)$(javahl_remote_javahdir) + $(INSTALL_EXTRA_JAVAHL_REMOTE_JAVAH) + install-javahl-tests: $(MKDIR) $(DESTDIR)$(javahl_testsdir) $(INSTALL_EXTRA_JAVAHL_TESTS) @@ -1041,16 +1184,25 @@ install-javahl-types-javah: $(MKDIR) $(DESTDIR)$(javahl_types_javahdir) $(INSTALL_EXTRA_JAVAHL_TYPES_JAVAH) +install-javahl-util-javah: + $(MKDIR) $(DESTDIR)$(javahl_util_javahdir) + $(INSTALL_EXTRA_JAVAHL_UTIL_JAVAH) + install-kwallet-lib: subversion/libsvn_auth_kwallet/libsvn_auth_kwallet-1.la - $(MKDIR) $(DESTDIR)$(kwallet_libdir) + $(MKDIR) $(DESTDIR)$(kwallet_libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_auth_kwallet ; $(INSTALL_KWALLET_LIB) libsvn_auth_kwallet-1.la $(DESTDIR)$(kwallet_libdir)/libsvn_auth_kwallet-1.la + $(INSTALL_DATA) subversion/libsvn_auth_kwallet/libsvn_auth_kwallet.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_auth_kwallet.pc 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) + $(MKDIR) $(DESTDIR)$(libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_diff ; $(INSTALL_LIB) libsvn_diff-1.la $(DESTDIR)$(libdir)/libsvn_diff-1.la + $(INSTALL_DATA) subversion/libsvn_diff/libsvn_diff.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_diff.pc cd subversion/libsvn_ra ; $(INSTALL_LIB) libsvn_ra-1.la $(DESTDIR)$(libdir)/libsvn_ra-1.la + $(INSTALL_DATA) subversion/libsvn_ra/libsvn_ra.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_ra.pc cd subversion/libsvn_wc ; $(INSTALL_LIB) libsvn_wc-1.la $(DESTDIR)$(libdir)/libsvn_wc-1.la + $(INSTALL_DATA) subversion/libsvn_wc/libsvn_wc.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_wc.pc cd subversion/libsvn_client ; $(INSTALL_LIB) libsvn_client-1.la $(DESTDIR)$(libdir)/libsvn_client-1.la + $(INSTALL_DATA) subversion/libsvn_client/libsvn_client.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_client.pc 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) @@ -1080,75 +1232,109 @@ install-locale: subversion/po/de.mo subversion/po/es.mo subversion/po/fr.mo subv 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) + $(MKDIR) $(DESTDIR)$(ramod_libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_fs ; $(INSTALL_RAMOD_LIB) libsvn_fs-1.la $(DESTDIR)$(ramod_libdir)/libsvn_fs-1.la + $(INSTALL_DATA) subversion/libsvn_fs/libsvn_fs.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_fs.pc cd subversion/libsvn_ra_svn ; $(INSTALL_RAMOD_LIB) libsvn_ra_svn-1.la $(DESTDIR)$(ramod_libdir)/libsvn_ra_svn-1.la + $(INSTALL_DATA) subversion/libsvn_ra_svn/libsvn_ra_svn.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_ra_svn.pc cd subversion/libsvn_repos ; $(INSTALL_RAMOD_LIB) libsvn_repos-1.la $(DESTDIR)$(ramod_libdir)/libsvn_repos-1.la + $(INSTALL_DATA) subversion/libsvn_repos/libsvn_repos.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_repos.pc cd subversion/libsvn_ra_local ; $(INSTALL_RAMOD_LIB) libsvn_ra_local-1.la $(DESTDIR)$(ramod_libdir)/libsvn_ra_local-1.la + $(INSTALL_DATA) subversion/libsvn_ra_local/libsvn_ra_local.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_ra_local.pc install-serf-lib: subversion/libsvn_ra_serf/libsvn_ra_serf-1.la - $(MKDIR) $(DESTDIR)$(serf_libdir) + $(MKDIR) $(DESTDIR)$(serf_libdir) $(DESTDIR)$(pkgconfig_dir) cd subversion/libsvn_ra_serf ; $(INSTALL_SERF_LIB) libsvn_ra_serf-1.la $(DESTDIR)$(serf_libdir)/libsvn_ra_serf-1.la + $(INSTALL_DATA) subversion/libsvn_ra_serf/libsvn_ra_serf.pc $(DESTDIR)$(pkgconfig_dir)/libsvn_ra_serf.pc -install-sub-test: subversion/tests/libsvn_subr/named_atomic-proc-test$(EXEEXT) +install-sub-test: subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-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) + cd subversion/tests/libsvn_fs_fs ; $(INSTALL_SUB_TEST) fs-fs-fuzzy-test$(EXEEXT) $(DESTDIR)$(sub_testdir)/fs-fs-fuzzy-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) +install-tests: gmock-fused/libgmock-1.la subversion/bindings/cxxhl/cxxhl-tests$(EXEEXT) $(MKDIR) $(DESTDIR)$(testsdir) - cd subversion/bindings/cxxhl ; $(INSTALL_TESTS) cxxhl-tests$(EXEEXT) $(DESTDIR)$(testsdir)/cxxhl-tests$(EXEEXT) + if $(SVN_USE_GMOCK) ; then cd gmock-fused ; $(INSTALL_TESTS) libgmock-1.la $(DESTDIR)$(testsdir)/libgmock-1.la ; fi + + if $(SVN_USE_GMOCK) ; then cd subversion/bindings/cxxhl ; $(INSTALL_TESTS) cxxhl-tests$(EXEEXT) $(DESTDIR)$(testsdir)/cxxhl-tests$(EXEEXT) ; fi + -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) +install-tools: tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/diff4$(EXEEXT) tools/dev/fsfs-access-map$(EXEEXT) tools/server-side/mod_dontdothat/mod_dontdothat.la tools/server-side/svn-populate-node-origins-index$(EXEEXT) tools/server-side/svnauthz$(EXEEXT) tools/server-side/svnauthz-validate$(EXEEXT) tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) tools/dev/x509-parser$(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) + + cd tools/dev ; $(INSTALL_TOOLS) x509-parser$(EXEEXT) $(DESTDIR)$(toolsdir)/x509-parser$(EXEEXT) + $(INSTALL_EXTRA_TOOLS) @@ -1156,7 +1342,7 @@ install-tools: tools/diff/diff$(EXEEXT) tools/diff/diff3$(EXEEXT) tools/diff/dif # 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 +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_x509.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 @@ -1200,6 +1386,7 @@ install-include: subversion/include/mod_authz_svn.h subversion/include/mod_dav_s $(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_x509.h $(DESTDIR)$(includedir)/subversion-1/svn_x509.h $(INSTALL_INCLUDE) $(abs_srcdir)/subversion/include/svn_xml.h $(DESTDIR)$(includedir)/subversion-1/svn_xml.h ######################################## @@ -1208,6 +1395,7 @@ install-include: subversion/include/mod_authz_svn.h subversion/include/mod_dav_s atomic-ra-revprop-change: subversion/tests/cmdline/atomic-ra-revprop-change$(EXEEXT) auth-test: subversion/tests/libsvn_subr/auth-test$(EXEEXT) +bit-array-test: subversion/tests/libsvn_subr/bit-array-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) @@ -1223,18 +1411,21 @@ 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) +dump-load-test: subversion/tests/libsvn_repos/dump-load-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-fs-fuzzy-test: subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test$(EXEEXT) +fs-fs-pack-test: subversion/tests/libsvn_fs_fs/fs-fs-pack-test$(EXEEXT) +fs-fs-private-test: subversion/tests/libsvn_fs_fs/fs-fs-private-test$(EXEEXT) fs-test: subversion/tests/libsvn_fs/fs-test$(EXEEXT) +fs-x-pack-test: subversion/tests/libsvn_fs_x/fs-x-pack-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) +libgmock: gmock-fused/libgmock-1.la 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 @@ -1244,6 +1435,7 @@ 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_fs_x: subversion/libsvn_fs_x/libsvn_fs_x-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 @@ -1257,15 +1449,16 @@ 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 +lock-helper: subversion/tests/cmdline/lock-helper$(EXEEXT) 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) +mtcc-test: subversion/tests/libsvn_client/mtcc-test$(EXEEXT) op-depth-test: subversion/tests/libsvn_wc/op-depth-test$(EXEEXT) opt-test: subversion/tests/libsvn_subr/opt-test$(EXEEXT) +packed-data-test: subversion/tests/libsvn_subr/packed-data-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 @@ -1276,6 +1469,8 @@ 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 +prefix-string-test: subversion/tests/libsvn_subr/prefix-string-test$(EXEEXT) +priority-queue-test: subversion/tests/libsvn_subr/priority-queue-test$(EXEEXT) 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 @@ -1290,6 +1485,7 @@ 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) +root-pools-test: subversion/tests/libsvn_subr/root-pools-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 @@ -1300,19 +1496,22 @@ 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) +sqlite-test: subversion/tests/libsvn_subr/sqlite-test$(EXEEXT) stream-test: subversion/tests/libsvn_subr/stream-test$(EXEEXT) +string-table-test: subversion/tests/libsvn_fs_x/string-table-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) +svn-wc-db-tester: tools/dev/wc-ng/svn-wc-db-tester$(EXEEXT) svnadmin: subversion/svnadmin/svnadmin$(EXEEXT) svnauthz: tools/server-side/svnauthz$(EXEEXT) svnauthz-validate: tools/server-side/svnauthz-validate$(EXEEXT) +svnbench: subversion/svnbench/svnbench$(EXEEXT) svndiff-test: subversion/tests/libsvn_delta/svndiff-test$(EXEEXT) svndumpfilter: subversion/svndumpfilter/svndumpfilter$(EXEEXT) +svnfsfs: subversion/svnfsfs/svnfsfs$(EXEEXT) svnlook: subversion/svnlook/svnlook$(EXEEXT) svnmucc: subversion/svnmucc/svnmucc$(EXEEXT) svnraisetreeconflict: tools/dev/svnraisetreeconflict/svnraisetreeconflict$(EXEEXT) @@ -1329,19 +1528,30 @@ 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) +x509-parser: tools/dev/x509-parser$(EXEEXT) +x509-test: subversion/tests/libsvn_subr/x509-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 +subversion/bindings/cxxhl/src/aprwrap/impl.lo: subversion/bindings/cxxhl/src/aprwrap/impl.cpp subversion/bindings/cxxhl/include/svncxxhl/_compat.hpp subversion/bindings/cxxhl/include/svncxxhl/exception.hpp subversion/bindings/cxxhl/src/aprwrap/hash.hpp subversion/bindings/cxxhl/src/aprwrap/pool.hpp 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_types.h subversion/svn_private_config.h + $(COMPILE_CXXHL_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/src/aprwrap/impl.cpp + +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/bindings/cxxhl/src/aprwrap.hpp subversion/bindings/cxxhl/src/aprwrap/array.hpp subversion/bindings/cxxhl/src/aprwrap/hash.hpp subversion/bindings/cxxhl/src/aprwrap/pool.hpp subversion/bindings/cxxhl/src/private.hpp subversion/bindings/cxxhl/src/private/exception-private.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_pools.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/cxxhl/tests/cxxhl-tests.lo: subversion/bindings/cxxhl/tests/cxxhl-tests.cpp + if $(SVN_USE_GMOCK) ; then $(COMPILE_CXXHL_GMOCK_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/tests/cxxhl-tests.cpp ; else echo "fake" > subversion/bindings/cxxhl/tests/cxxhl-tests.lo ; fi + +subversion/bindings/cxxhl/tests/test_aprwrap.lo: subversion/bindings/cxxhl/tests/test_aprwrap.cpp subversion/bindings/cxxhl/include/svncxxhl/_compat.hpp subversion/bindings/cxxhl/include/svncxxhl/exception.hpp subversion/bindings/cxxhl/src/aprwrap.hpp subversion/bindings/cxxhl/src/aprwrap/array.hpp subversion/bindings/cxxhl/src/aprwrap/hash.hpp subversion/bindings/cxxhl/src/aprwrap/pool.hpp 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/svn_private_config.h + if $(SVN_USE_GMOCK) ; then $(COMPILE_CXXHL_GMOCK_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/tests/test_aprwrap.cpp ; else echo "fake" > subversion/bindings/cxxhl/tests/test_aprwrap.lo ; fi + +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/bindings/cxxhl/src/private.hpp subversion/bindings/cxxhl/src/private/exception-private.hpp subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_types.h + if $(SVN_USE_GMOCK) ; then $(COMPILE_CXXHL_GMOCK_CXX) $(canonicalized_srcdir)subversion/bindings/cxxhl/tests/test_exception.cpp ; else echo "fake" > subversion/bindings/cxxhl/tests/test_exception.lo ; fi subversion/bindings/javahl/classes/org/apache/subversion/javahl/BasicTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java @@ -1361,16 +1571,30 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/ConflictResult.c 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/ExceptionTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/ExceptionTests.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/ISVNConfig.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNConfig.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNEditor.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRemote.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNReporter.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNReporter.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/JNIObject.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/JNIObject.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/OperationContext.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/OperationContext.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 @@ -1379,16 +1603,24 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/RunTests.class: 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/SVNRemoteTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.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/SVNUtil.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.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/UtilTests.class: subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.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/AuthnCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/AuthnCallback.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 @@ -1399,6 +1631,8 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitC 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/ConfigEvent.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ConfigEvent.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 @@ -1419,14 +1653,36 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/Progres 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/RemoteFileRevisionsCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteFileRevisionsCallback.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteStatus.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.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/ReposVerifyCallback.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ReposVerifyCallback.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/TunnelAgent.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.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/remote/CommitEditor.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteFactory.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteSession.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RetryOpenSession.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RetryOpenSession.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StateReporter.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StatusEditor.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.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 @@ -1441,6 +1697,8 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DiffOption 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/ExternalItem.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.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 @@ -1449,6 +1707,10 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/LogDate.cl 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/NativeInputStream.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeInputStream.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NativeOutputStream.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeOutputStream.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 @@ -1457,6 +1719,10 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Revision.c 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/RevisionRangeList.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRangeList.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RuntimeVersion.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RuntimeVersion.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 @@ -1465,6 +1731,22 @@ subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Version.cl 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/apache/subversion/javahl/util/ConfigImpl.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ConfigImpl.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigLib.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ConfigLib.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/DiffLib.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/DiffLib.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/PropLib.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/PropLib.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/RequestChannel.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ResponseChannel.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/SubstLib.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/SubstLib.java + +subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/TunnelChannel.class: subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.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 @@ -1607,6 +1889,8 @@ subversion/bindings/javahl/classes/org/tigris/subversion/javahl/Version.class: s subversion/bindings/javahl/classes/org/tigris/subversion/javahl/WC.class: subversion/bindings/javahl/tests/org/tigris/subversion/javahl/WC.java +subversion/bindings/javahl/include/AuthnCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/AuthnCallback.class + 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 @@ -1623,6 +1907,8 @@ subversion/bindings/javahl/include/ClientNotifyInformation.h: subversion/binding subversion/bindings/javahl/include/CommitCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitCallback.class +subversion/bindings/javahl/include/CommitEditor.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/CommitEditor.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 @@ -1631,6 +1917,12 @@ subversion/bindings/javahl/include/CommitItemStateFlags.h: subversion/bindings/j subversion/bindings/javahl/include/CommitMessageCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/CommitMessageCallback.class +subversion/bindings/javahl/include/ConfigEvent.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ConfigEvent.class + +subversion/bindings/javahl/include/ConfigImpl.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigImpl.class + +subversion/bindings/javahl/include/ConfigLib.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ConfigLib.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 @@ -1643,6 +1935,8 @@ subversion/bindings/javahl/include/CopySource.h: subversion/bindings/javahl/clas subversion/bindings/javahl/include/Depth.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/Depth.class +subversion/bindings/javahl/include/DiffLib.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/DiffLib.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 @@ -1651,8 +1945,18 @@ subversion/bindings/javahl/include/DiffSummaryCallback.h: subversion/bindings/ja subversion/bindings/javahl/include/DirEntry.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/DirEntry.class +subversion/bindings/javahl/include/ExternalItem.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/ExternalItem.class + subversion/bindings/javahl/include/ISVNClient.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNClient.class +subversion/bindings/javahl/include/ISVNConfig.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNConfig.class + +subversion/bindings/javahl/include/ISVNEditor.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNEditor.class + +subversion/bindings/javahl/include/ISVNRemote.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNRemote.class + +subversion/bindings/javahl/include/ISVNReporter.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/ISVNReporter.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 @@ -1665,6 +1969,8 @@ subversion/bindings/javahl/include/InheritedProplistCallback.h: subversion/bindi subversion/bindings/javahl/include/JNIError.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIError.class +subversion/bindings/javahl/include/JNIObject.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/JNIObject.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 @@ -1677,42 +1983,84 @@ subversion/bindings/javahl/include/Mergeinfo.h: subversion/bindings/javahl/class subversion/bindings/javahl/include/NativeException.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/NativeException.class +subversion/bindings/javahl/include/NativeInputStream.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NativeInputStream.class + +subversion/bindings/javahl/include/NativeOutputStream.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/NativeOutputStream.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/OperationContext.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/OperationContext.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/PropLib.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/PropLib.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/RemoteFactory.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteFactory.class + +subversion/bindings/javahl/include/RemoteFileRevisionsCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteFileRevisionsCallback.class + +subversion/bindings/javahl/include/RemoteLocationSegmentsCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteLocationSegmentsCallback.class + +subversion/bindings/javahl/include/RemoteSession.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RemoteSession.class + +subversion/bindings/javahl/include/RemoteStatus.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/RemoteStatus.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/ReposVerifyCallback.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/ReposVerifyCallback.class + +subversion/bindings/javahl/include/RequestChannel.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/RequestChannel.class + +subversion/bindings/javahl/include/ResponseChannel.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/ResponseChannel.class + +subversion/bindings/javahl/include/RetryOpenSession.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/RetryOpenSession.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/RevisionRangeList.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RevisionRangeList.class + +subversion/bindings/javahl/include/RuntimeVersion.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/types/RuntimeVersion.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/SVNUtil.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/SVNUtil.class + +subversion/bindings/javahl/include/StateReporter.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StateReporter.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/StatusEditor.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/remote/StatusEditor.class + +subversion/bindings/javahl/include/SubstLib.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/SubstLib.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/TunnelAgent.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/callback/TunnelAgent.class + +subversion/bindings/javahl/include/TunnelChannel.h: subversion/bindings/javahl/classes/org/apache/subversion/javahl/util/TunnelChannel.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 @@ -1722,36 +2070,54 @@ subversion/bindings/javahl/include/VersionExtended.h: subversion/bindings/javahl 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/AuthnCallback.lo: subversion/bindings/javahl/native/AuthnCallback.cpp subversion/bindings/javahl/native/AuthnCallback.hpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_io.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_x509.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/AuthnCallback.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 +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/OperationContext.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_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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 +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/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.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_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/CommitEditor.lo: subversion/bindings/javahl/native/CommitEditor.cpp subversion/bindings/javahl/native/CommitCallback.h subversion/bindings/javahl/native/CommitEditor.h subversion/bindings/javahl/native/EditorCallbacks.hpp subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/InputStream.h subversion/bindings/javahl/native/Iterator.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/LockTokenTable.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/PropertyTable.h subversion/bindings/javahl/native/RemoteSession.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_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/CommitEditor.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 +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/bindings/javahl/native/RevisionRangeList.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 +subversion/bindings/javahl/native/Credential.lo: subversion/bindings/javahl/native/Credential.cpp subversion/bindings/javahl/native/AuthnCallback.hpp subversion/bindings/javahl/native/Credential.hpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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 + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Credential.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_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_mergeinfo.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/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/EditorCallbacks.lo: subversion/bindings/javahl/native/EditorCallbacks.cpp subversion/bindings/javahl/native/EditorCallbacks.hpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Utility.hpp subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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/EditorCallbacks.cpp + +subversion/bindings/javahl/native/EditorProxy.lo: subversion/bindings/javahl/native/EditorProxy.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EditorProxy.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/NativeStream.hpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.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/EditorProxy.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/ExternalItem.lo: subversion/bindings/javahl/native/ExternalItem.cpp subversion/bindings/javahl/native/ExternalItem.hpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_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/ExternalItem.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 @@ -1764,6 +2130,9 @@ subversion/bindings/javahl/native/InfoCallback.lo: subversion/bindings/javahl/na 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/Iterator.lo: subversion/bindings/javahl/native/Iterator.cpp subversion/bindings/javahl/native/Iterator.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/Iterator.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 @@ -1773,191 +2142,275 @@ subversion/bindings/javahl/native/JNICriticalSection.lo: subversion/bindings/jav 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 +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/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 +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/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/include/private/svn_atomic.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_utf_private.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/LockTokenTable.lo: subversion/bindings/javahl/native/LockTokenTable.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/LockTokenTable.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/LockTokenTable.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/NativeStream.lo: subversion/bindings/javahl/native/NativeStream.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeInputStream.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeOutputStream.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/NativeStream.hpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp 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 subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/NativeStream.cpp + +subversion/bindings/javahl/native/OperationContext.lo: subversion/bindings/javahl/native/OperationContext.cpp subversion/bindings/javahl/native/CommitMessage.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/GlobalConfig.h subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.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_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/OperationContext.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 +subversion/bindings/javahl/native/Path.lo: subversion/bindings/javahl/native/Path.cpp 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/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/svn_private_config.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 +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/AuthnCallback.hpp subversion/bindings/javahl/native/JNIStackElement.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/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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/PropertyTable.lo: subversion/bindings/javahl/native/PropertyTable.cpp subversion/bindings/javahl/native/Array.h subversion/bindings/javahl/native/JNIByteArray.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/PropertyTable.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/PropertyTable.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/RemoteSession.lo: subversion/bindings/javahl/native/RemoteSession.cpp subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EditorProxy.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/Iterator.h subversion/bindings/javahl/native/JNIByteArray.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/LogMessageCallback.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/RemoteSession.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/StateReporter.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.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/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_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/RemoteSession.cpp + +subversion/bindings/javahl/native/RemoteSessionContext.lo: subversion/bindings/javahl/native/RemoteSessionContext.cpp subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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/RemoteSessionContext.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/ReposVerifyCallback.lo: subversion/bindings/javahl/native/ReposVerifyCallback.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/ReposVerifyCallback.h 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_repos.h subversion/include/svn_string.h subversion/include/svn_types.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/ReposVerifyCallback.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/RevisionRangeList.lo: subversion/bindings/javahl/native/RevisionRangeList.cpp subversion/bindings/javahl/native/Iterator.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/RevisionRange.h subversion/bindings/javahl/native/RevisionRangeList.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_mergeinfo.h subversion/include/svn_opt.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/RevisionRangeList.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 +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/ExternalItem.hpp 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/OperationContext.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/PropertyTable.h subversion/bindings/javahl/native/ProplistCallback.h subversion/bindings/javahl/native/RemoteSession.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/RevisionRange.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/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp subversion/include/private/svn_atomic.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_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_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_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 +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/ReposVerifyCallback.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/StateReporter.lo: subversion/bindings/javahl/native/StateReporter.cpp subversion/bindings/javahl/native/EditorProxy.h subversion/bindings/javahl/native/EnumMapper.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/StateReporter.h subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.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/StateReporter.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/SubversionException.lo: subversion/bindings/javahl/native/SubversionException.cpp subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SubversionException.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp 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/SubversionException.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/Utility.lo: subversion/bindings/javahl/native/Utility.cpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Utility.hpp subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp 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_string.h subversion/include/svn_types.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/Utility.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/deprecated.lo: subversion/bindings/javahl/native/deprecated.cpp subversion/bindings/javahl/native/ClientContext.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/SVNClient.h subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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/deprecated.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_base.lo: subversion/bindings/javahl/native/jniwrapper/jni_base.cpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_string.h subversion/include/svn_types.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_base.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_channel.lo: subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp 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_string.h subversion/include/svn_types.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_class_cache.lo: subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp subversion/bindings/javahl/native/AuthnCallback.hpp subversion/bindings/javahl/native/Credential.hpp subversion/bindings/javahl/native/EditorCallbacks.hpp subversion/bindings/javahl/native/ExternalItem.hpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SubversionException.hpp subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp 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_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/jniwrapper/jni_class_cache.cpp -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 +subversion/bindings/javahl/native/jniwrapper/jni_io_stream.lo: subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp 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 subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_iterator.lo: subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_list.lo: subversion/bindings/javahl/native/jniwrapper/jni_list.cpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_list.cpp + +subversion/bindings/javahl/native/jniwrapper/jni_string_map.lo: subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp + +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 $(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 +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/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/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/OperationContext.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/PropertyTable.h subversion/bindings/javahl/native/ProplistCallback.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/RevisionRange.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/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/version.h subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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 +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/ReposVerifyCallback.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_remote_CommitEditor.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_CommitEditor.h subversion/bindings/javahl/native/CommitCallback.h subversion/bindings/javahl/native/CommitEditor.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/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp 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 subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteFactory.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/RemoteSession.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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 subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteSession.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/Prompter.h subversion/bindings/javahl/native/RemoteSession.h subversion/bindings/javahl/native/RemoteSessionContext.h subversion/bindings/javahl/native/Revision.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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/org_apache_subversion_javahl_remote_RemoteSession.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_StateReporter.h subversion/bindings/javahl/native/EditorProxy.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/StateReporter.h subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.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_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 + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_types_RevisionRangeList.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/RevisionRangeList.h subversion/include/private/svn_debug.h subversion/include/svn_error.h subversion/include/svn_error_codes.h subversion/include/svn_mergeinfo.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/org_apache_subversion_javahl_types_RevisionRangeList.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_types_RuntimeVersion.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_version.h subversion/include/svn_wc.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.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 +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 subversion/svn_private_config.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 +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigImpl_Category.h subversion/bindings/javahl/native/CreateJ.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OperationContext.h subversion/bindings/javahl/native/Pool.h subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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_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_util_ConfigImpl_Category.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigLib.h subversion/bindings/javahl/native/AuthnCallback.hpp subversion/bindings/javahl/native/Credential.hpp subversion/bindings/javahl/native/GlobalConfig.h subversion/bindings/javahl/native/JNICriticalSection.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SubversionException.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_x509.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_DiffLib.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIStringHolder.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/OutputStream.h subversion/bindings/javahl/native/Path.h subversion/bindings/javahl/native/Pool.h 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_mergeinfo.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/org_apache_subversion_javahl_util_DiffLib.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_PropLib.h subversion/bindings/javahl/native/EnumMapper.h subversion/bindings/javahl/native/ExternalItem.hpp subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SubversionException.hpp subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_list.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp 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_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_wc.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_SubstLib.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/NativeStream.hpp subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/SVNBase.h subversion/bindings/javahl/native/Utility.hpp subversion/bindings/javahl/native/jniwrapper/jni_array.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp subversion/bindings/javahl/native/jniwrapper/jni_string.hpp subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp 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_subst.h subversion/include/svn_types.h subversion/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp + +subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.lo: subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp subversion/bindings/javahl/include/org_apache_subversion_javahl_util_RequestChannel.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ResponseChannel.h subversion/bindings/javahl/include/org_apache_subversion_javahl_util_TunnelChannel.h subversion/bindings/javahl/native/JNIStackElement.h subversion/bindings/javahl/native/JNIUtil.h subversion/bindings/javahl/native/Pool.h subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp subversion/bindings/javahl/native/jniwrapper/jni_env.hpp subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp subversion/bindings/javahl/native/jniwrapper/jni_object.hpp subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp 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/svn_private_config.h + $(COMPILE_JAVAHL_CXX) $(canonicalized_srcdir)subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.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/perl/libsvn_swig_perl/swigutil_pl__pre_perl.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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 +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/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.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/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_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_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/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_sorts_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/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_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_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 @@ -1965,69 +2418,71 @@ subversion/libsvn_client/changelist.lo: subversion/libsvn_client/changelist.c su 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/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/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/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.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_sorts_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/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_sorts_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_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_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/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/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_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/ctx.lo: subversion/libsvn_client/ctx.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_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/client.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.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_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_string.h subversion/include/svn_subst.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/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_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_sorts_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_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/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/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/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/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_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/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_sorts_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_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/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_sorts_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_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/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/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_sorts_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/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_sorts_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_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/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_sorts_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/mtcc.lo: subversion/libsvn_client/mtcc.c subversion/include/private/svn_client_mtcc.h 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_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_client/client.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_sorts_private.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_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/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_sorts_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/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/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_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/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_sorts_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/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/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_sorts_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/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 @@ -2043,7 +2498,7 @@ subversion/libsvn_client/version.lo: subversion/libsvn_client/version.c subversi 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/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/private/svn_sorts_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_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 @@ -2057,127 +2512,207 @@ subversion/libsvn_delta/depth_filter_editor.lo: subversion/libsvn_delta/depth_fi 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/path_driver.lo: subversion/libsvn_delta/path_driver.c subversion/include/private/svn_debug.h subversion/include/private/svn_fspath.h subversion/include/private/svn_sorts_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_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/svndiff.lo: subversion/libsvn_delta/svndiff.c 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_error_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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_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_delta/xdelta.lo: subversion/libsvn_delta/xdelta.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_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/binary_diff.lo: subversion/libsvn_diff/binary_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_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.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/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_mergeinfo.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/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_mergeinfo.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/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_mergeinfo.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/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_mergeinfo.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_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_string_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_mergeinfo.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_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_mergeinfo.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/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_mergeinfo.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/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/private/svn_sorts_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/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/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_mergeinfo.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_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_diff/util.lo: subversion/libsvn_diff/util.c subversion/include/private/svn_debug.h subversion/include/private/svn_diff_private.h subversion/include/private/svn_sorts_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/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/private/svn_mutex.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/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/deprecated.lo: subversion/libsvn_fs/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_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.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/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/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_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_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/fs-loader.lo: subversion/libsvn_fs/fs-loader.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_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_mutex.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_utf_private.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_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_props.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_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/private/svn_mutex.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_props.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/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_mutex.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_types.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/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/checksum-reps-table.lo: subversion/libsvn_fs_base/bdb/checksum-reps-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/copies-table.lo: subversion/libsvn_fs_base/bdb/copies-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/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_mutex.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_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-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/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_mutex.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_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-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/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_mutex.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_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-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/node-origins-table.lo: subversion/libsvn_fs_base/bdb/node-origins-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/nodes-table.lo: subversion/libsvn_fs_base/bdb/nodes-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/reps-table.lo: subversion/libsvn_fs_base/bdb/reps-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/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_mutex.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_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-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/strings-table.lo: subversion/libsvn_fs_base/bdb/strings-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/txn-table.lo: subversion/libsvn_fs_base/bdb/txn-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/bdb/uuids-table.lo: subversion/libsvn_fs_base/bdb/uuids-table.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/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_mutex.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/include/svn_version.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/err.lo: subversion/libsvn_fs_base/err.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/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_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_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/id.lo: subversion/libsvn_fs_base/id.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_props.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/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_mutex.h subversion/include/private/svn_skel.h subversion/include/private/svn_subr_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_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/revs-txns.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/private/svn_mutex.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_props.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/private/svn_mutex.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_props.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_mutex.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/include/svn_version.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/private/svn_mutex.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_props.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_mutex.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/include/svn_version.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_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_string.h subversion/include/svn_time.h subversion/include/svn_types.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/private/svn_mutex.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_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/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/cached_data.lo: subversion/libsvn_fs_fs/cached_data.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_io_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_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_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/util.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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/index.h subversion/libsvn_fs_fs/rev_file.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mutex.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/cached_data.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/transaction.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/dump-index.lo: subversion/libsvn_fs_fs/dump-index.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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_props.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/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/util.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_fs_private.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_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_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_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/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/hotcopy.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/recovery.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/tree.h subversion/libsvn_fs_fs/util.h subversion/libsvn_fs_fs/verify.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_dep_compat.h subversion/include/private/svn_editor.h subversion/include/private/svn_fs_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fs_util.h subversion/include/private/svn_io_private.h subversion/include/private/svn_mutex.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_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_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/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/tree.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/hotcopy.lo: subversion/libsvn_fs_fs/hotcopy.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_props.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/hotcopy.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/recovery.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/id.lo: subversion/libsvn_fs_fs/id.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_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_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.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/fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rev_file.h + +subversion/libsvn_fs_fs/index.lo: subversion/libsvn_fs_fs/index.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_io.h subversion/include/svn_iter.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/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/index.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/load-index.lo: subversion/libsvn_fs_fs/load-index.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/util.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_fs_private.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_sorts_private.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_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_utf.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/tree.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/low_level.lo: subversion/libsvn_fs_fs/low_level.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_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_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/low_level.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/pack.lo: subversion/libsvn_fs_fs/pack.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_io_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_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_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/recovery.lo: subversion/libsvn_fs_fs/recovery.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_fs/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/recovery.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/util.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_props.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/id.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/rev_file.lo: subversion/libsvn_fs_fs/rev_file.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_io_private.h subversion/include/private/svn_mutex.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_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_props.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/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/revprops.lo: subversion/libsvn_fs_fs/revprops.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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_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/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/revprops.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/stats.lo: subversion/libsvn_fs_fs/stats.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_fs/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/util.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_fs_private.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_sorts_private.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs_fs/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/temp_serializer.h + +subversion/libsvn_fs_fs/transaction.lo: subversion/libsvn_fs_fs/transaction.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_fs_private.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_sorts_private.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_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_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/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/lock.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/tree.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.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_fs_private.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_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_sorts.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/cached_data.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/lock.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/temp_serializer.h subversion/libsvn_fs_fs/transaction.h subversion/libsvn_fs_fs/tree.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/util.lo: subversion/libsvn_fs_fs/util.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.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/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/pack.h subversion/libsvn_fs_fs/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_fs/verify.lo: subversion/libsvn_fs_fs/verify.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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_props.h subversion/include/svn_sorts.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_fs/cached_data.h subversion/libsvn_fs_fs/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rep-cache.h subversion/libsvn_fs_fs/rev_file.h subversion/libsvn_fs_fs/util.h subversion/libsvn_fs_fs/verify.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/private/svn_mutex.h subversion/include/private/svn_subr_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.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_x/cached_data.lo: subversion/libsvn_fs_x/cached_data.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_io_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_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_props.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_delta/delta.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/changes.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/noderevs.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/reps.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/util.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_x/caching.lo: subversion/libsvn_fs_x/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_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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/changes.h subversion/libsvn_fs_x/dag.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/noderevs.h subversion/libsvn_fs_x/reps.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/tree.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_x/changes.lo: subversion/libsvn_fs_x/changes.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_packed_data.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_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_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/changes.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/string_table.h subversion/libsvn_fs_x/temp_serializer.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_x/dag.lo: subversion/libsvn_fs_x/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_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_x/cached_data.h subversion/libsvn_fs_x/dag.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_id.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/transaction.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_x/fs.lo: subversion/libsvn_fs_x/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_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_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_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_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/hotcopy.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/lock.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/recovery.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/tree.h subversion/libsvn_fs_x/util.h subversion/libsvn_fs_x/verify.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_x/fs_id.lo: subversion/libsvn_fs_x/fs_id.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_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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_id.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/rev_file.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_x/fs_x.lo: subversion/libsvn_fs_x/fs_x.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_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_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_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_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/tree.h subversion/libsvn_fs_x/util.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_x/hotcopy.lo: subversion/libsvn_fs_x/hotcopy.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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/hotcopy.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/recovery.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/util.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_x/id.lo: subversion/libsvn_fs_x/id.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_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_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_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/util.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_x/index.lo: subversion/libsvn_fs_x/index.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_sorts_private.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_io.h subversion/include/svn_iter.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/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/util.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_x/lock.lo: subversion/libsvn_fs_x/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_sorts_private.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_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_utf.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/lock.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/tree.h subversion/libsvn_fs_x/util.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_x/low_level.lo: subversion/libsvn_fs_x/low_level.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_sorts_private.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_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_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_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.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_x/noderevs.lo: subversion/libsvn_fs_x/noderevs.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_packed_data.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_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/noderevs.h subversion/libsvn_fs_x/string_table.h subversion/libsvn_fs_x/temp_serializer.h subversion/svn_private_config.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_x/pack.lo: subversion/libsvn_fs_x/pack.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_sorts_private.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_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_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_x/cached_data.h subversion/libsvn_fs_x/changes.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/noderevs.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/reps.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/util.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_x/recovery.lo: subversion/libsvn_fs_x/recovery.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_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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/recovery.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/util.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_x/rep-cache.lo: subversion/libsvn_fs_x/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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/rep-cache-db.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.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_x/reps.lo: subversion/libsvn_fs_x/reps.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_packed_data.h subversion/include/private/svn_sqlite.h subversion/include/private/svn_string_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_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/reps.h subversion/libsvn_fs_x/rev_file.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_fs_x/rev_file.lo: subversion/libsvn_fs_x/rev_file.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_io_private.h subversion/include/private/svn_mutex.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_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_props.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/util.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_fs_x/revprops.lo: subversion/libsvn_fs_x/revprops.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_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_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_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_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/revprops.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_x/string_table.lo: subversion/libsvn_fs_x/string_table.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_packed_data.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_temp_serializer.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/string_table.h + +subversion/libsvn_fs_x/temp_serializer.lo: subversion/libsvn_fs_x/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_sorts_private.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h + +subversion/libsvn_fs_x/transaction.lo: subversion/libsvn_fs_x/transaction.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_io_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sorts_private.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_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_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_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_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/lock.h subversion/libsvn_fs_x/low_level.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/tree.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_x/tree.lo: subversion/libsvn_fs_x/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_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_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_types.h subversion/include/svn_version.h subversion/libsvn_fs/fs-loader.h subversion/libsvn_fs_x/cached_data.h subversion/libsvn_fs_x/dag.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_id.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/lock.h subversion/libsvn_fs_x/pack.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/temp_serializer.h subversion/libsvn_fs_x/transaction.h subversion/libsvn_fs_x/tree.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_x/util.lo: subversion/libsvn_fs_x/util.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_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_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_io.h subversion/include/svn_iter.h subversion/include/svn_mergeinfo.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_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/util.h subversion/svn_private_config.h + +subversion/libsvn_fs_x/verify.lo: subversion/libsvn_fs_x/verify.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_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_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_x/cached_data.h subversion/libsvn_fs_x/fs.h subversion/libsvn_fs_x/fs_x.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/index.h subversion/libsvn_fs_x/rep-cache.h subversion/libsvn_fs_x/rev_file.h subversion/libsvn_fs_x/util.h subversion/libsvn_fs_x/verify.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/private/svn_sorts_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 @@ -2185,11 +2720,11 @@ subversion/libsvn_ra/deprecated.lo: subversion/libsvn_ra/deprecated.c subversion 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/ra_loader.lo: subversion/libsvn_ra/ra_loader.c subversion/include/private/svn_auth_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_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_time.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/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_string_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 @@ -2199,8 +2734,14 @@ subversion/libsvn_ra_serf/blncache.lo: subversion/libsvn_ra_serf/blncache.c subv 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/eagain_bucket.lo: subversion/libsvn_ra_serf/eagain_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/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/get_file.lo: subversion/libsvn_ra_serf/get_file.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_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_props.h subversion/include/svn_ra.h subversion/include/svn_string.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/get_lock.lo: subversion/libsvn_ra_serf/get_lock.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/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 @@ -2209,9 +2750,9 @@ subversion/libsvn_ra_serf/getlocationsegments.lo: subversion/libsvn_ra_serf/getl 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/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_sorts_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_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/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/lock.lo: subversion/libsvn_ra_serf/lock.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_sorts_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_sorts.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 @@ -2219,19 +2760,23 @@ subversion/libsvn_ra_serf/merge.lo: subversion/libsvn_ra_serf/merge.c subversion 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_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/multistatus.lo: subversion/libsvn_ra_serf/multistatus.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_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_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_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/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/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_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/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/stat.lo: subversion/libsvn_ra_serf/stat.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_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_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.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/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 @@ -2249,75 +2794,83 @@ subversion/libsvn_ra_svn/editorp.lo: subversion/libsvn_ra_svn/editorp.c subversi 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/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/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_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_sorts.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/streams.lo: subversion/libsvn_ra_svn/streams.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_io_private.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/authz.lo: subversion/libsvn_repos/authz.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_string_private.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/authz_pool.lo: subversion/libsvn_repos/authz_pool.c 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_object_pool.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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_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/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/private/svn_string_private.h subversion/include/svn_checksum.h subversion/include/svn_compat.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_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/config_pool.lo: subversion/libsvn_repos/config_pool.c 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_object_pool.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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/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/dump.lo: subversion/libsvn_repos/dump.c subversion/include/private/svn_cache.h 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/private/svn_repos_private.h subversion/include/private/svn_sorts_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_utf_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_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/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_sorts_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_utf_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_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_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-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/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_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/load.lo: subversion/libsvn_repos/load.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_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_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/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/log.lo: subversion/libsvn_repos/log.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_mergeinfo_private.h subversion/include/private/svn_sorts_private.h subversion/include/private/svn_subr_private.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_repos.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.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/notify.lo: subversion/libsvn_repos/notify.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.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/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/private/svn_sorts_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_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/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_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/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/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_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_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_repos/rev_hunt.lo: subversion/libsvn_repos/rev_hunt.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_sorts_private.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/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/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_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/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/bit_array.lo: subversion/libsvn_subr/bit_array.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.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-membuffer.lo: subversion/libsvn_subr/cache-membuffer.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_mutex.h subversion/include/private/svn_string_private.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/cache.h subversion/libsvn_subr/fnv1a.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/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_sorts.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/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_config.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_subr/checksum.h subversion/libsvn_subr/fnv1a.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/cmdline.lo: subversion/libsvn_subr/cmdline.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_sorts_private.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/compress.lo: subversion/libsvn_subr/compress.c subversion/include/private/svn_debug.h subversion/include/private/svn_error_private.h subversion/include/private/svn_subr_private.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/svn_private_config.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/private/svn_subr_private.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_file.lo: subversion/libsvn_subr/config_file.c subversion/include/private/svn_debug.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_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_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/config_win.lo: subversion/libsvn_subr/config_win.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/private/svn_utf_private.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 @@ -2327,67 +2880,71 @@ subversion/libsvn_subr/date.lo: subversion/libsvn_subr/date.c subversion/include 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/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/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/auth.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_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/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/private/svn_subr_private.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_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_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/fnv1a.lo: subversion/libsvn_subr/fnv1a.c subversion/include/private/svn_debug.h subversion/include/private/svn_subr_private.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/libsvn_subr/fnv1a.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/libsvn_subr/auth.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/hash.lo: subversion/libsvn_subr/hash.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_sorts_private.h subversion/include/private/svn_subr_private.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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.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/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/private/svn_string_private.h subversion/include/private/svn_utf_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/log.lo: subversion/libsvn_subr/log.c subversion/include/private/svn_debug.h subversion/include/private/svn_log.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_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/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/libsvn_subr/auth.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/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_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_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/md5.lo: subversion/libsvn_subr/md5.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_md5.h subversion/include/svn_types.h subversion/libsvn_subr/checksum.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/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_sorts_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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_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/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/mutex.lo: subversion/libsvn_subr/mutex.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_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/private/svn_string_private.h subversion/include/private/svn_utf_private.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/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/object_pool.lo: subversion/libsvn_subr/object_pool.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_object_pool.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.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/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/packed_data.lo: subversion/libsvn_subr/packed_data.c subversion/include/private/svn_debug.h subversion/include/private/svn_delta_private.h subversion/include/private/svn_editor.h subversion/include/private/svn_packed_data.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.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/prefix_string.lo: subversion/libsvn_subr/prefix_string.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.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/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/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/pseudo_md5.lo: subversion/libsvn_subr/pseudo_md5.c subversion/include/private/svn_pseudo_md5.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_config.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/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/root_pools.lo: subversion/libsvn_subr/root_pools.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.h subversion/include/private/svn_subr_private.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/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/sorts.lo: subversion/libsvn_subr/sorts.c subversion/include/private/svn_debug.h subversion/include/private/svn_sorts_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_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/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_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/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/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_io_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_token.h subversion/include/private/svn_utf_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_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 @@ -2397,23 +2954,23 @@ subversion/libsvn_subr/ssl_client_cert_pw_providers.lo: subversion/libsvn_subr/s 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/stream.lo: subversion/libsvn_subr/stream.c subversion/include/private/svn_atomic.h subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_utf_private.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/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/subst.lo: subversion/libsvn_subr/subst.c subversion/include/private/svn_debug.h subversion/include/private/svn_eol_private.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/sysinfo.lo: subversion/libsvn_subr/sysinfo.c subversion/include/private/svn_debug.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_utf_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_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/time.lo: subversion/libsvn_subr/time.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_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/types.lo: subversion/libsvn_subr/types.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.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_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 @@ -2421,27 +2978,33 @@ subversion/libsvn_subr/username_providers.lo: subversion/libsvn_subr/username_pr 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/utf8proc.lo: subversion/libsvn_subr/utf8proc.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.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/libsvn_subr/utf8proc/utf8proc.c subversion/libsvn_subr/utf8proc/utf8proc.h subversion/libsvn_subr/utf8proc/utf8proc_data.c 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_string_private.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/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/utf_width.lo: subversion/libsvn_subr/utf_width.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.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/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_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_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/sysinfo.h subversion/libsvn_subr/win32_crashrpt.h subversion/libsvn_subr/win32_crashrpt_dll.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/libsvn_subr/auth.h subversion/svn_private_config.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/private/svn_subr_private.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_utf.h subversion/libsvn_subr/win32_xlate.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/x509info.lo: subversion/libsvn_subr/x509info.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_string.h subversion/include/svn_types.h subversion/include/svn_x509.h subversion/libsvn_subr/x509.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_subr/x509parse.lo: subversion/libsvn_subr/x509parse.c subversion/include/private/svn_debug.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_string.h subversion/include/svn_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_x509.h subversion/libsvn_subr/x509.h + +subversion/libsvn_subr/xml.lo: subversion/libsvn_subr/xml.c subversion/include/private/svn_debug.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.h subversion/include/private/svn_utf_private.h subversion/include/svn_checksum.h subversion/include/svn_config.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/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_sorts_private.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_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 @@ -2457,61 +3020,61 @@ subversion/libsvn_wc/crop.lo: subversion/libsvn_wc/crop.c subversion/include/pri 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/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_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_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_sorts_private.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_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/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_types.h subversion/include/svn_wc.h subversion/libsvn_wc/diff.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/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/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/token-map.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/wc_db_private.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/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_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/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/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/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_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/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_sorts_private.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/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/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_sorts_private.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_time.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/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/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_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/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/token-map.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.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_sorts_private.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_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_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_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_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_skel.h subversion/include/private/svn_sorts_private.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/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_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_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/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_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_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/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 @@ -2525,7 +3088,7 @@ subversion/mod_dav_svn/authz.lo: subversion/mod_dav_svn/authz.c subversion/inclu 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 +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_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_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 @@ -2537,7 +3100,7 @@ subversion/mod_dav_svn/merge.lo: subversion/mod_dav_svn/merge.c subversion/inclu 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 +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_config.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 @@ -2576,13 +3139,16 @@ subversion/mod_dav_svn/reports/replay.lo: subversion/mod_dav_svn/reports/replay. 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 +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/private/svn_sorts_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_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 +subversion/mod_dav_svn/status.lo: subversion/mod_dav_svn/status.c subversion/include/mod_authz_svn.h subversion/include/private/svn_cache.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_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_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_xml.h subversion/mod_dav_svn/dav_svn.h subversion/svn_private_config.h + if $(INSTALL_APACHE_MODS) ; then $(COMPILE_APACHE_MOD) $(canonicalized_srcdir)subversion/mod_dav_svn/status.c ; else echo "fake" > subversion/mod_dav_svn/status.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/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 +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_string_private.h subversion/include/private/svn_subr_private.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_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 @@ -2611,7 +3177,9 @@ 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/auth-cmd.lo: subversion/svn/auth-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_sorts_private.h subversion/include/private/svn_token.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_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_time.h subversion/include/svn_types.h subversion/include/svn_utf.h subversion/include/svn_wc.h subversion/include/svn_x509.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_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/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 @@ -2633,13 +3201,13 @@ subversion/svn/delete-cmd.lo: subversion/svn/delete-cmd.c subversion/include/pri 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/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_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/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/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_string_private.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/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_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_types.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 @@ -2649,17 +3217,17 @@ subversion/svn/list-cmd.lo: subversion/svn/list-cmd.c subversion/include/private 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/log-cmd.lo: subversion/svn/log-cmd.c subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_sorts_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_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-log.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/merge-cmd.lo: subversion/svn/merge-cmd.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.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/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_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_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn/cl-log.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/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_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 @@ -2667,7 +3235,7 @@ subversion/svn/propdel-cmd.lo: subversion/svn/propdel-cmd.c subversion/include/p 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/propget-cmd.lo: subversion/svn/propget-cmd.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_sorts_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_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 @@ -2683,6 +3251,8 @@ subversion/svn/resolved-cmd.lo: subversion/svn/resolved-cmd.c subversion/include 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/similarity.lo: subversion/svn/similarity.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_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_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 @@ -2697,197 +3267,235 @@ subversion/svn/update-cmd.lo: subversion/svn/update-cmd.c subversion/include/pri 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/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_diff_tree.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_sorts_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_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_xml.h subversion/svn_private_config.h + +subversion/svnbench/help-cmd.lo: subversion/svnbench/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 subversion/svnbench/cl.h + +subversion/svnbench/notify.lo: subversion/svnbench/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 subversion/svnbench/cl.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/svnbench/null-blame-cmd.lo: subversion/svnbench/null-blame-cmd.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_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_wc.h subversion/svn_private_config.h subversion/svnbench/cl.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/svnbench/null-export-cmd.lo: subversion/svnbench/null-export-cmd.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_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 subversion/svnbench/cl.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/svnbench/null-info-cmd.lo: subversion/svnbench/null-info-cmd.c subversion/include/private/svn_client_private.h subversion/include/private/svn_debug.h subversion/include/private/svn_diff_tree.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_private_config.h subversion/svnbench/cl.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/svnbench/null-list-cmd.lo: subversion/svnbench/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 subversion/svnbench/cl.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/svnbench/null-log-cmd.lo: subversion/svnbench/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 subversion/svnbench/cl.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/svnbench/svnbench.lo: subversion/svnbench/svnbench.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 subversion/svnbench/cl.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/svnbench/util.lo: subversion/svnbench/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 subversion/svnbench/cl.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_editor.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_sorts_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_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/svnfsfs/dump-index-cmd.lo: subversion/svnfsfs/dump-index-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_fs_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_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_string.h subversion/include/svn_types.h subversion/svnfsfs/svnfsfs.h + +subversion/svnfsfs/load-index-cmd.lo: subversion/svnfsfs/load-index-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_fs_private.h subversion/include/private/svn_sorts_private.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_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnfsfs/svnfsfs.h + +subversion/svnfsfs/stats-cmd.lo: subversion/svnfsfs/stats-cmd.c subversion/include/private/svn_debug.h subversion/include/private/svn_fs_fs_private.h subversion/include/private/svn_sorts_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_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnfsfs/svnfsfs.h + +subversion/svnfsfs/svnfsfs.lo: subversion/svnfsfs/svnfsfs.c subversion/include/private/svn_cmdline_private.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_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 subversion/include/svn_version.h subversion/svn_private_config.h subversion/svnfsfs/svnfsfs.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_sorts_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_client_mtcc.h subversion/include/private/svn_cmdline_private.h subversion/include/private/svn_debug.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_repos_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_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/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/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_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/private/svn_repos_private.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_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/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_mutex.h subversion/include/private/svn_ra_svn_private.h subversion/include/private/svn_repos_private.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_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/log-escape.lo: subversion/svnserve/log-escape.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_mutex.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_private.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/logger.lo: subversion/svnserve/logger.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_mutex.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_private.h subversion/include/private/svn_subr_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_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_time.h subversion/include/svn_types.h subversion/svn_private_config.h subversion/svnserve/logger.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/serve.lo: subversion/svnserve/serve.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_log.h subversion/include/private/svn_mergeinfo_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_ra_svn_private.h subversion/include/private/svn_repos_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_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/logger.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_editor.h subversion/include/private/svn_mutex.h subversion/include/private/svn_repos_private.h subversion/include/private/svn_string_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_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/logger.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/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/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/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/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/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/cmdline/lock-helper.lo: subversion/tests/cmdline/lock-helper.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_pools.h subversion/include/svn_string.h subversion/include/svn_types.h + +subversion/tests/libsvn_client/client-test.lo: subversion/tests/libsvn_client/client-test.c subversion/include/private/svn_client_mtcc.h 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_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_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_client/mtcc-test.lo: subversion/tests/libsvn_client/mtcc-test.c subversion/include/private/svn_client_mtcc.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_wc.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_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_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_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_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/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/vdelta-test.lo: subversion/tests/libsvn_delta/vdelta-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_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/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/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_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/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_diff/diff-diff3-test.lo: subversion/tests/libsvn_diff/diff-diff3-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_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_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_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/parse-diff-test.lo: subversion/tests/libsvn_diff/parse-diff-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_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_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/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_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/private/svn_fs_util.h subversion/include/private/svn_fspath.h subversion/include/private/svn_mutex.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_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/libsvn_fs/fs-loader.h subversion/svn_private_config.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.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/locks-test.lo: subversion/tests/libsvn_fs/locks-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_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/tests/svn_test.h subversion/tests/svn_test_fs.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_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_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_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/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/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_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_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/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/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/strings-reps-test.lo: subversion/tests/libsvn_fs_base/strings-reps-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_mutex.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_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_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_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_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_fs/fs-fs-fuzzy-test.lo: subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sqlite.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_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/fs.h subversion/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/rev_file.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-fs-pack-test.lo: subversion/tests/libsvn_fs_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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.h subversion/include/private/svn_sqlite.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_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/libsvn_fs_fs/fs_fs.h subversion/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/low_level.h subversion/libsvn_fs_fs/util.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_fs_fs/fs-fs-private-test.lo: subversion/tests/libsvn_fs_fs/fs-fs-private-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_fs_private.h subversion/include/private/svn_fs_private.h subversion/include/private/svn_mutex.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_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_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/libsvn_fs_fs/id.h subversion/libsvn_fs_fs/index.h subversion/libsvn_fs_fs/rev_file.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_fs_x/fs-x-pack-test.lo: subversion/tests/libsvn_fs_x/fs-x-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_sqlite.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_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_x/fs.h subversion/libsvn_fs_x/id.h subversion/libsvn_fs_x/reps.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.h + +subversion/tests/libsvn_fs_x/string-table-test.lo: subversion/tests/libsvn_fs_x/string-table-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_temp_serializer.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_pools.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/libsvn_fs_x/string_table.h subversion/tests/svn_test.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_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_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/dir-delta-editor.lo: subversion/tests/libsvn_repos/dir-delta-editor.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_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/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_repos/dump-load-test.lo: subversion/tests/libsvn_repos/dump-load-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_editor.h subversion/include/private/svn_repos_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_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_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/private/svn_string_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_repos.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_version.h subversion/libsvn_subr/config_impl.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/bit-array-test.lo: subversion/tests/libsvn_subr/bit-array-test.c subversion/include/private/svn_debug.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_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/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/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_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_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/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/checksum-test.lo: subversion/tests/libsvn_subr/checksum-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_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/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/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_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_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/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/config-test.lo: subversion/tests/libsvn_subr/config-test.c subversion/include/private/svn_debug.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_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/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/crypto-test.lo: subversion/tests/libsvn_subr/crypto-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_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/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/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_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_pools.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/error-code-test.lo: subversion/tests/libsvn_subr/error-code-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_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/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_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/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/hashdump-test.lo: subversion/tests/libsvn_subr/hashdump-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_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/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/io-test.lo: subversion/tests/libsvn_subr/io-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_dep_compat.h subversion/include/private/svn_io_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_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/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/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_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_string.h subversion/include/svn_types.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_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_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/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/packed-data-test.lo: subversion/tests/libsvn_subr/packed-data-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_packed_data.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/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/path-test.lo: subversion/tests/libsvn_subr/path-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_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/prefix-string-test.lo: subversion/tests/libsvn_subr/prefix-string-test.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_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/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/priority-queue-test.lo: subversion/tests/libsvn_subr/priority-queue-test.c subversion/include/private/svn_debug.h subversion/include/private/svn_sorts_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_path.h subversion/include/svn_sorts.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/tests/svn_test.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/revision-test.lo: subversion/tests/libsvn_subr/revision-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_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/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/root-pools-test.lo: subversion/tests/libsvn_subr/root-pools-test.c subversion/include/private/svn_atomic.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_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/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/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_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_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/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/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_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/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/sqlite-test.lo: subversion/tests/libsvn_subr/sqlite-test.c subversion/include/private/svn_debug.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_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/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/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_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_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_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_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_sorts.h subversion/include/svn_string.h subversion/include/svn_types.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_subr/subst_translate-test.lo: subversion/tests/libsvn_subr/subst_translate-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_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_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_subr/time-test.lo: subversion/tests/libsvn_subr/time-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_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_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_subr/translate-test.lo: subversion/tests/libsvn_subr/translate-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_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_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_subr/utf-test.lo: subversion/tests/libsvn_subr/utf-test.c 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_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_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/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_subr/x509-test.lo: subversion/tests/libsvn_subr/x509-test.c 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_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_time.h subversion/include/svn_types.h subversion/include/svn_x509.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/libsvn_subr/sqlite3wrapper.lo: subversion/libsvn_subr/sqlite3wrapper.c subversion/svn_private_config.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/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_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_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/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/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_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_wc/wc_db.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/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_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_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/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/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_sorts_private.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/libsvn_wc/wc-test-queries.h subversion/tests/svn_test.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/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/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/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/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-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/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/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-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/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.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/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/svn_private_config.h subversion/tests/libsvn_wc/utils.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 +subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.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 +subversion/tests/libsvn_wc/utils.lo: subversion/tests/libsvn_wc/utils.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_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_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/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/svn_private_config.h subversion/tests/libsvn_wc/utils.h subversion/tests/svn_test.h subversion/tests/svn_test_fs.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 +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 -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 +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/libsvn_wc/workqueue.h subversion/svn_private_config.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 +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_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_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 -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 +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 -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 +subversion/tests/svn_test_fs.lo: subversion/tests/svn_test_fs.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_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 -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 +subversion/tests/svn_test_main.lo: subversion/tests/svn_test_main.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_mutex.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_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/include/svn_version.h subversion/svn_private_config.h subversion/tests/svn_test.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/dev/wc-ng/svn-wc-db-tester.lo: tools/dev/wc-ng/svn-wc-db-tester.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_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_utf.h subversion/include/svn_version.h subversion/include/svn_wc.h subversion/libsvn_wc/wc.h subversion/libsvn_wc/wc_db.h subversion/svn_private_config.h + +tools/dev/x509-parser.lo: tools/dev/x509-parser.c subversion/include/private/svn_debug.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_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_time.h subversion/include/svn_types.h subversion/include/svn_x509.h subversion/svn_private_config.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/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_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.h subversion/include/svn_utf.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/diff/diff3.lo: tools/diff/diff3.c subversion/include/private/svn_debug.h subversion/include/private/svn_token.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_mergeinfo.h subversion/include/svn_opt.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/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_mergeinfo.h subversion/include/svn_pools.h subversion/include/svn_string.h subversion/include/svn_types.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 diff --git a/contrib/subversion/build.conf b/contrib/subversion/build.conf index 8d5a54314..4f020da1b 100644 --- a/contrib/subversion/build.conf +++ b/contrib/subversion/build.conf @@ -40,13 +40,23 @@ private-includes = subversion/libsvn_delta/compose_delta.c subversion/bindings/cxxhl/include/*.hpp subversion/bindings/cxxhl/include/svncxxhl/*.hpp + subversion/bindings/cxxhl/src/*.hpp + subversion/bindings/cxxhl/src/aprwrap/*.hpp + subversion/bindings/cxxhl/src/private/*.hpp + subversion/bindings/javahl/native/*.hpp + subversion/bindings/javahl/native/jniwrapper/jni_*.hpp + subversion/libsvn_subr/utf8proc/utf8proc.h + subversion/libsvn_subr/utf8proc/utf8proc.c + subversion/libsvn_subr/utf8proc/utf8proc_data.c private-built-includes = subversion/svn_private_config.h subversion/libsvn_fs_fs/rep-cache-db.h + subversion/libsvn_fs_x/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/tests/libsvn_wc/wc-test-queries.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 @@ -56,15 +66,30 @@ private-built-includes = 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_NativeInputStream.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeOutputStream.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_RuntimeVersion.h subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Revision.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_types_RevisionRangeList.h subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_UserPasswordCallback.h - + subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteSession.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteFactory.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_CommitEditor.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_StateReporter.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigImpl_Category.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigLib.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_DiffLib.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_PropLib.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_SubstLib.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_TunnelChannel.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_RequestChannel.h + subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ResponseChannel.h test-scripts = subversion/tests/cmdline/*_tests.py @@ -106,6 +131,7 @@ swig-checkout-files = common.swg swigrun.swg runtime.swg # 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 +# msvc-force-static- visual studio always uses static libraries for svn libs # 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 @@ -121,6 +147,7 @@ libs = libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_diff libsvn_subr apriconv apr manpages = subversion/svn/svn.1 install = bin +msvc-libs = setargv.obj # The subversion repository administration tool [svnadmin] @@ -130,6 +157,7 @@ path = subversion/svnadmin install = bin manpages = subversion/svnadmin/svnadmin.1 libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr +msvc-libs = setargv.obj # The subversion repository dump filtering tool [svndumpfilter] @@ -215,7 +243,7 @@ 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 +msvc-export = svn_client.h private/svn_client_mtcc.h private/svn_client_private.h # Routines for binary diffing and tree-deltas [libsvn_delta] @@ -248,6 +276,7 @@ add-install-deps = $(SVN_FS_LIB_INSTALL_DEPS) msvc-export = svn_fs.h private/svn_fs_private.h [libsvn_fs_base] +description = Subversion Filesystem Base Library type = fs-module path = subversion/libsvn_fs_base sources = *.c bdb/*.c util/*.c @@ -256,14 +285,24 @@ libs = libsvn_delta libsvn_subr aprutil apriconv apr bdb libsvn_fs_util msvc-static = yes [libsvn_fs_fs] +description = Subversion FSFS Repository Filesystem Library 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 +[libsvn_fs_x] +description = Subversion FSX Repository Filesystem Library +type = fs-module +path = subversion/libsvn_fs_x +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] +description = Subversion Filesystem Utility Library type = lib install = fsmod-lib path = subversion/libsvn_fs_util @@ -273,7 +312,7 @@ msvc-static = yes # General API for accessing repositories [libsvn_ra] -description = Subversion Repository Access Library +description = Subversion General Repository Access Library type = lib path = subversion/libsvn_ra libs = libsvn_delta libsvn_subr ra-libs apriconv apr @@ -285,15 +324,16 @@ msvc-export = svn_ra.h private\svn_ra_private.h # Accessing repositories via DAV through serf [libsvn_ra_serf] +description = Subversion HTTP/WebDAV Protocol Repository Access Library 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 +libs = libsvn_delta libsvn_subr aprutil apriconv apr serf xml zlib msvc-static = yes # Accessing repositories via SVN [libsvn_ra_svn] +description = Subversion SVN Protocol Repository Access Library type = ra-module path = subversion/libsvn_ra_svn install = ramod-lib @@ -302,6 +342,7 @@ msvc-static = yes # Accessing repositories via direct libsvn_fs [libsvn_ra_local] +description = Subversion Local Repository Access Library type = ra-module path = subversion/libsvn_ra_local install = ramod-lib @@ -323,7 +364,7 @@ description = Subversion General Utility Library type = lib install = fsmod-lib path = subversion/libsvn_subr -libs = aprutil apriconv apr xml zlib apr_memcache sqlite magic +libs = aprutil apriconv apr xml zlib apr_memcache sqlite magic intl msvc-libs = kernel32.lib advapi32.lib shfolder.lib ole32.lib crypt32.lib version.lib msvc-export = @@ -332,7 +373,7 @@ msvc-export = 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 + svn_utf.h svn_version.h svn_xml.h svn_x509.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 @@ -340,9 +381,10 @@ msvc-export = 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_sorts_private.h private\svn_auth_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 + private\svn_subr_private.h private\svn_mutex.h + private\svn_packed_data.h private\svn_object_pool.h private\svn_cert.h # Working copy management lib [libsvn_wc] @@ -360,10 +402,9 @@ 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 +libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr libhttpd mod_dav nonlibs = apr aprutil install = apache-mod -msvc-libs = mod_dav.lib libhttpd.lib [mod_authz_svn] description = Subversion path-based authorization module for Apache @@ -371,9 +412,8 @@ when = INSTALL_APACHE_MODS type = apache-mod path = subversion/mod_authz_svn nonlibs = mod_dav_svn apr aprutil -libs = libsvn_repos libsvn_subr +libs = libsvn_repos libsvn_subr libhttpd install = apache-mod -msvc-libs = libhttpd.lib [mod_dontdothat] description = Apache Httpd module to block certain kinds of Apache Subversion requests @@ -381,21 +421,35 @@ when = INSTALL_APACHE_MODS type = apache-mod path = tools/server-side/mod_dontdothat nonlibs = mod_dav_svn apr aprutil -libs = libsvn_subr xml +libs = libsvn_subr xml libhttpd install = tools -msvc-libs = libhttpd.lib + +# The Subversion FSFS repository manipulation tool +[svnfsfs] +description = Subversion FSFS Repository Manipulation Tool +type = exe +path = subversion/svnfsfs +install = bin +libs = libsvn_repos libsvn_fs libsvn_fs_fs libsvn_delta libsvn_subr apriconv apr +msvc-libs = setargv.obj # ---------------------------------------------------------------------------- # # CONSTRUCTED HEADERS # -[rep_cache] -description = Schema for the rep-sharing feature +[rep_cache_fs_fs] +description = Schema for the FSFS rep-sharing feature type = sql-header path = subversion/libsvn_fs_fs sources = rep-cache-db.sql +[rep_cache_fs_x] +description = Schema for the FSX rep-sharing feature +type = sql-header +path = subversion/libsvn_fs_x +sources = rep-cache-db.sql + [wc_queries] desription = Queries on the WC database type = sql-header @@ -408,6 +462,11 @@ type = sql-header path = subversion/libsvn_subr sources = internal_statements.sql +[wc_test_queries] +description = Queries using working copy tests +type = sql-header +path = subversion/tests/libsvn_wc +sources = wc-test-queries.sql # ---------------------------------------------------------------------------- # @@ -431,7 +490,6 @@ 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 @@ -501,35 +559,39 @@ description = Subversion WC library bindings 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 +libs = libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_subr + apriconv apr python swig link-cmd = $(LINK) install = swig-py-lib # need special build rule to include -DSWIGPYTHON compile-cmd = $(COMPILE_SWIG_PY) msvc-static = no +msvc-export = ../bindings/swig/python/libsvn_swig_py/swigutil_py.h # 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 +libs = libsvn_delta libsvn_subr apriconv apr perl swig install = swig-pl-lib # need special build rule to include compile-cmd = $(COMPILE_SWIG_PL) -msvc-static = yes +msvc-static = no +msvc-export = ../bindings/swig/perl/libsvn_swig_perl/swigutil_pl.h # 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 +libs = libsvn_client libsvn_wc libsvn_delta libsvn_subr apriconv apr ruby swig link-cmd = $(LINK) $(SWIG_RB_LIBS) install = swig-rb-lib # need special build rule to include compile-cmd = $(COMPILE_SWIG_RB) msvc-static = no +msvc-export = ../bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h # ---------------------------------------------------------------------------- # @@ -539,7 +601,9 @@ msvc-static = no 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/remote subversion/bindings/javahl/src/org/apache/subversion/javahl/types + subversion/bindings/javahl/src/org/apache/subversion/javahl/util src-root = subversion/bindings/javahl/src sources = *.java install = javahl-java @@ -552,7 +616,7 @@ type = java path = subversion/bindings/javahl/src/org/tigris/subversion/javahl sources = *.java install = javahl-java -link-cmd = $(COMPILE_JAVAHL_JAVAC) +link-cmd = $(COMPILE_JAVAHL_COMPAT_JAVAC) classes = subversion/bindings/javahl/classes add-deps = $(javahl_java_DEPS) ### Replace JAR call in INSTALL_EXTRA_JAVAHL_JAVA macro Makefile.in. @@ -576,15 +640,37 @@ type = java path = subversion/bindings/javahl/tests/org/tigris/subversion/javahl sources = *.java install = javahl-java -link-cmd = $(COMPILE_JAVAHL_JAVAC) +link-cmd = $(COMPILE_JAVAHL_COMPAT_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-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-remote-javah] +type = javah +path = subversion/bindings/javahl/src/org/apache/subversion/javahl/remote +classes = subversion/bindings/javahl/classes +headers = subversion/bindings/javahl/include +package = org.apache.subversion.javahl.remote +sources = *.java +add-deps = $(javahl_java_DEPS) +install = javahl-javah +link-cmd = $(COMPILE_JAVAHL_JAVAH) -force + [javahl-types-javah] -type = javah +type = javah path = subversion/bindings/javahl/src/org/apache/subversion/javahl/types classes = subversion/bindings/javahl/classes headers = subversion/bindings/javahl/include @@ -594,19 +680,19 @@ 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 +[javahl-util-javah] +type = javah +path = subversion/bindings/javahl/src/org/apache/subversion/javahl/util classes = subversion/bindings/javahl/classes headers = subversion/bindings/javahl/include -package = org.apache.subversion.javahl.callback +package = org.apache.subversion.javahl.util sources = *.java add-deps = $(javahl_java_DEPS) install = javahl-javah link-cmd = $(COMPILE_JAVAHL_JAVAH) -force [javahl-javah] -type = javah +type = javah path = subversion/bindings/javahl/src/org/apache/subversion/javahl classes = subversion/bindings/javahl/classes headers = subversion/bindings/javahl/include @@ -621,9 +707,11 @@ 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) + libsvn_subr libsvn_fs aprutil apriconv apr java-sdk +sources = *.cpp jniwrapper/*.cpp +add-deps = $(javahl_java_DEPS) $(javahl_callback_javah_DEPS) + $(javahl_remote_javah_DEPS) $(javahl_types_javah_DEPS) + $(javahl_util_javah_DEPS) $(javahl_javah_DEPS) install = javahl-lib # need special build rule to include -I$(JDK)/include/jni.h compile-cmd = $(COMPILE_JAVAHL_CXX) @@ -640,7 +728,7 @@ 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 +sources = src/*.cpp src/aprwrap/*.cpp install = cxxhl-lib msvc-static = yes compile-cmd = $(COMPILE_CXXHL_CXX) @@ -648,14 +736,32 @@ link-cmd = $(LINK_CXX_LIB) [cxxhl-tests] description = Unit tests for Subversion C++ HighLevel bindings +when = SVN_USE_GMOCK type = exe path = subversion/bindings/cxxhl -libs = libsvncxxhl libsvn_subr +libs = libsvncxxhl libgmock libsvn_subr apr sources = tests/*.cpp install = tests -compile-cmd = $(COMPILE_CXXHL_CXX) +compile-cmd = $(COMPILE_CXXHL_GMOCK_CXX) link-cmd = $(LINK_CXX) + +# ---------------------------------------------------------------------------- +# +# Gmock targets +# + +[libgmock] +description = Googlemock Library +when = SVN_USE_GMOCK +type = lib +path = gmock-fused +sources = gmock-gtest-all.cc +install = tests +msvc-static = yes +compile-cmd = $(COMPILE_GMOCK_CXX) +link-cmd = $(LINK_CXX_LIB) + # ---------------------------------------------------------------------------- # # TESTING TARGETS @@ -702,15 +808,52 @@ libs = libsvn_test libsvn_fs libsvn_fs_base libsvn_delta # ---------------------------------------------------------------------------- # Tests for libsvn_fs_fs -[fs-pack-test] +[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 +sources = fs-fs-pack-test.c install = test libs = libsvn_test libsvn_fs libsvn_fs_fs libsvn_delta libsvn_subr apriconv apr +[fs-fs-fuzzy-test] +description = Use fuzzying to test FSFS corruption resilience +type = exe +path = subversion/tests/libsvn_fs_fs +sources = fs-fs-fuzzy-test.c +install = sub-test +libs = libsvn_test libsvn_fs libsvn_fs_fs libsvn_delta + libsvn_repos libsvn_subr apriconv apr + +[fs-fs-private-test] +description = Test FSSF private API +type = exe +path = subversion/tests/libsvn_fs_fs +sources = fs-fs-private-test.c +install = test +libs = libsvn_test libsvn_fs libsvn_fs_fs libsvn_delta + libsvn_repos libsvn_subr apriconv apr + +# ---------------------------------------------------------------------------- +# Tests for libsvn_fs_x +[fs-x-pack-test] +description = Test fsx packing in libsvn_fs_x +type = exe +path = subversion/tests/libsvn_fs_x +sources = fs-x-pack-test.c +install = test +libs = libsvn_test libsvn_fs libsvn_fs_x libsvn_delta + libsvn_subr apriconv apr + +[string-table-test] +description = Test fsfs string tables +type = exe +path = subversion/tests/libsvn_fs_x +sources = string-table-test.c +install = test +libs = libsvn_test libsvn_fs_x libsvn_subr apr + # ---------------------------------------------------------------------------- # Tests for libsvn_fs @@ -721,6 +864,7 @@ path = subversion/tests/libsvn_fs sources = locks-test.c install = test libs = libsvn_test libsvn_fs libsvn_delta libsvn_subr apriconv apr +msvc-force-static = yes [fs-test] description = Test locks in libsvn_fs @@ -729,7 +873,7 @@ path = subversion/tests/libsvn_fs sources = fs-test.c install = test libs = libsvn_test libsvn_fs libsvn_delta - libsvn_subr aprutil apriconv apr + libsvn_fs_util libsvn_subr aprutil apriconv apr # ---------------------------------------------------------------------------- # Tests for libsvn_repos @@ -742,6 +886,14 @@ sources = repos-test.c dir-delta-editor.c install = test libs = libsvn_test libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr +[dump-load-test] +description = Test dumping/loading repositories in libsvn_repos +type = exe +path = subversion/tests/libsvn_repos +sources = dump-load-test.c +install = test +libs = libsvn_test libsvn_repos libsvn_fs libsvn_delta libsvn_subr apriconv apr + # ---------------------------------------------------------------------------- # Tests for libsvn_subr @@ -753,6 +905,14 @@ sources = auth-test.c install = test libs = libsvn_test libsvn_subr apr +[bit-array-test] +description = Test packed bit arrays +type = exe +path = subversion/tests/libsvn_subr +sources = bit-array-test.c +install = test +libs = libsvn_test libsvn_subr apr + [cache-test] description = Test in-memory cache type = exe @@ -768,6 +928,7 @@ path = subversion/tests/libsvn_subr sources = checksum-test.c install = test libs = libsvn_test libsvn_subr apr zlib +msvc-force-static = yes [compat-test] description = Test compatibility functions @@ -792,6 +953,7 @@ path = subversion/tests/libsvn_subr sources = crypto-test.c install = test libs = libsvn_test libsvn_subr aprutil apr +msvc-force-static = yes [dirent_uri-test] description = Test dirent_uri library @@ -849,27 +1011,35 @@ sources = mergeinfo-test.c install = test libs = libsvn_test libsvn_subr apr -[named_atomic-test] -description = Test named atomics +[packed-data-test] +description = Test path library type = exe path = subversion/tests/libsvn_subr -sources = named_atomic-test.c +sources = packed-data-test.c install = test -libs = libsvn_test libsvn_subr apr +libs = libsvn_test libsvn_subr apriconv apr -[named_atomic-proc-test] -description = Sub-process for named atomics +[path-test] +description = Test path library type = exe path = subversion/tests/libsvn_subr -sources = named_atomic-test-proc.c -install = sub-test -libs = libsvn_subr apr +sources = path-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr -[path-test] +[prefix-string-test] description = Test path library type = exe path = subversion/tests/libsvn_subr -sources = path-test.c +sources = prefix-string-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr + +[priority-queue-test] +description = Test path library +type = exe +path = subversion/tests/libsvn_subr +sources = priority-queue-test.c install = test libs = libsvn_test libsvn_subr apriconv apr @@ -881,6 +1051,14 @@ sources = revision-test.c install = test libs = libsvn_test libsvn_subr apr +[root-pools-test] +description = Test time functions +type = exe +path = subversion/tests/libsvn_subr +sources = root-pools-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr + [skel-test] description = Test skels in libsvn_subr type = exe @@ -913,6 +1091,14 @@ sources = string-test.c install = test libs = libsvn_test libsvn_subr apriconv apr +[sqlite-test] +description = Test stream library +type = exe +path = subversion/tests/libsvn_subr +sources = sqlite-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr + [time-test] description = Test time functions type = exe @@ -945,6 +1131,15 @@ sources = translate-test.c install = test libs = libsvn_test libsvn_subr apriconv apr +[x509-test] +description = Test x509 parser +type = exe +path = subversion/tests/libsvn_subr +sources = x509-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr + + # ---------------------------------------------------------------------------- # Tests for libsvn_delta @@ -974,6 +1169,15 @@ 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 +msvc-force-static = yes + +[mtcc-test] +description = Test Multi Command Context +type = exe +path = subversion/tests/libsvn_client +sources = mtcc-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 @@ -1003,7 +1207,7 @@ type = exe path = subversion/tests/libsvn_ra sources = ra-test.c install = test -libs = libsvn_test libsvn_ra libsvn_fs libsvn_delta libsvn_subr +libs = libsvn_test libsvn_ra libsvn_ra_svn libsvn_fs libsvn_delta libsvn_subr apriconv apr # ---------------------------------------------------------------------------- @@ -1028,6 +1232,7 @@ 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 +msvc-force-static = yes [db-test] description = Test the wc-ng database subsystem @@ -1036,6 +1241,7 @@ path = subversion/tests/libsvn_wc sources = db-test.c utils.c install = test libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr +msvc-force-static = yes [pristine-store-test] description = Test the wc-ng pristine text storage subsystem @@ -1044,6 +1250,7 @@ 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 +msvc-force-static = yes [entries-compat-test] description = Test backwards compat for the entry interface @@ -1052,6 +1259,7 @@ path = subversion/tests/libsvn_wc sources = entries-compat.c utils.c install = test libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr +msvc-force-static = yes [op-depth-test] description = Test layered tree changes @@ -1060,12 +1268,13 @@ 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 +msvc-force-static = yes [wc-queries-test] description = Test Sqlite query evaluation type = exe path = subversion/tests/libsvn_wc -sources = wc-queries-test.c +sources = wc-queries-test.c ../../libsvn_subr/sqlite3wrapper.c install = test libs = libsvn_test libsvn_subr apriconv apr sqlite @@ -1076,6 +1285,7 @@ path = subversion/tests/libsvn_wc sources = wc-test.c utils.c install = test libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr +msvc-force-static = yes # ---------------------------------------------------------------------------- # These are not unit tests at all, they are small programs that exercise @@ -1107,6 +1317,7 @@ path = subversion/tests/cmdline sources = entries-dump.c install = test libs = libsvn_wc libsvn_subr apriconv apr +msvc-force-static = yes testing = skip [atomic-ra-revprop-change] @@ -1117,12 +1328,21 @@ install = test libs = libsvn_ra libsvn_subr apriconv apr testing = skip +[lock-helper] +type = exe +path = subversion/tests/cmdline +sources = lock-helper.c +install = test +libs = libsvn_fs 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 +msvc-force-static = yes testing = skip [wc-incomplete-tester] @@ -1131,6 +1351,16 @@ path = subversion/tests/libsvn_wc sources = wc-incomplete-tester.c install = test libs = libsvn_wc libsvn_subr apriconv apr +msvc-force-static = yes +testing = skip + +[svn-wc-db-tester] +type = exe +path = tools/dev/wc-ng +sources = svn-wc-db-tester.c +install = test +libs = libsvn_wc libsvn_subr apr +msvc-force-static = yes testing = skip # ---------------------------------------------------------------------------- @@ -1142,15 +1372,25 @@ testing = skip type = lib external-lib = $(SVN_APR_LIBS) msvc-libs = ws2_32.lib rpcrt4.lib mswsock.lib +pkg-config = apr-@SVN_APR_MAJOR_VERSION@ [aprutil] type = lib external-lib = $(SVN_APRUTIL_LIBS) +pkg-config = apr-util-@SVN_APR_MAJOR_VERSION@ [apriconv] type = lib external-lib = $(SVN_APRUTIL_LIBS) +[libhttpd] +type = lib +external-lib = $(SVN_HTTPD_LIBS) + +[mod_dav] +type = lib +external-lib = $(SVN_MOD_DAV_LIBS) + [bdb] type = lib external-lib = $(SVN_DB_LIBS) @@ -1158,6 +1398,7 @@ external-lib = $(SVN_DB_LIBS) [gnome-keyring] type = lib external-lib = $(SVN_GNOME_KEYRING_LIBS) +pkg-config = gnome-keyring-1 [kwallet] type = lib @@ -1171,10 +1412,18 @@ external-lib = $(SVN_MAGIC_LIBS) type = lib external-lib = $(SVN_SASL_LIBS) +[openssl] +type = lib +external-lib = $(SVN_OPENSSL_LIBS) +msvc-libs = ssleay32.lib libeay32.lib + +[intl] +type = lib +external-lib = $(SVN_INTL_LIBS) + [zlib] type = lib external-lib = $(SVN_ZLIB_LIBS) -external-project = zlib msvc-static = yes [apr_memcache] @@ -1184,18 +1433,41 @@ 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 +libs = apr aprutil openssl xml zlib +msvc-libs = secur32.lib +pkg-config = serf-1 +pkg-config-private = yes [sqlite] type = lib external-lib = $(SVN_SQLITE_LIBS) +pkg-config = sqlite3 +pkg-config-private = yes [xml] type = lib external-lib = $(SVN_XML_LIBS) +[swig] +type = lib +external-lib = $(SVN_SWIG_LIBS) + +[perl] +type = lib +external-lib = $(SVN_PERL_LIBS) + +[python] +type = lib +external-lib = $(SVN_PYTHON_LIBS) + +[ruby] +type = lib +external-lib = $(SVN_RUBY_LIBS) + +[java-sdk] +type = lib +external-lib = $(SVN_JAVA_SDK_LIBS) + [ra-libs] type = lib external-lib = $(SVN_RA_LIB_LINK) @@ -1204,7 +1476,7 @@ 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 +libs = libsvn_fs_base libsvn_fs_fs libsvn_fs_x [__ALL__] type = project @@ -1213,37 +1485,44 @@ libs = svn svnadmin svndumpfilter svnlook svnmucc svnserve svnrdump svnsync svnversion mod_authz_svn mod_dav_svn mod_dontdothat svnauthz svnauthz-validate svnraisetreeconflict + svnfsfs svnbench [__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 + fs-test fs-base-test fs-fsfs-test fs-fs-pack-test fs-fs-fuzzy-test + fs-fs-private-test fs-x-pack-test string-table-test + skel-test strings-reps-test changes-test locks-test + repos-test dump-load-test checksum-test compat-test config-test hashdump-test mergeinfo-test - opt-test path-test stream-test string-test time-test utf-test + opt-test packed-data-test path-test prefix-string-test + priority-queue-test root-pools-test stream-test + string-test time-test utf-test bit-array-test error-test error-code-test cache-test spillbuf-test crypto-test - named_atomic-test named_atomic-proc-test revision-test + revision-test subst_translate-test io-test translate-test random-test window-test diff-diff3-test ra-test ra-local-test + sqlite-test svndiff-test vdelta-test entries-dump atomic-ra-revprop-change wc-lock-tester wc-incomplete-tester - client-test + lock-helper + client-test mtcc-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 + parse-diff-test x509-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 + diff diff3 diff4 fsfs-access-map svnauth + svn-populate-node-origins-index x509-parser svn-wc-db-tester [__LIBS__] type = project @@ -1286,20 +1565,6 @@ 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 @@ -1328,10 +1593,11 @@ sources = diff4.c install = tools libs = libsvn_diff libsvn_subr apriconv apr -[svn-bench] +[svnbench] +description = Benchmarking and diagnostics tool for the network layer type = exe -path = tools/client-side/svn-bench -install = tools +path = subversion/svnbench +install = bin libs = libsvn_client libsvn_wc libsvn_ra libsvn_subr libsvn_delta apriconv apr @@ -1371,9 +1637,10 @@ path = tools/dev/svnraisetreeconflict libs = libsvn_wc libsvn_subr apriconv apr install = tools -[svn-rep-sharing-stats] +[x509-parser] +description = Tool to verify x509 certificates type = exe -path = tools/server-side -sources = svn-rep-sharing-stats.c +path = tools/dev +sources = x509-parser.c install = tools -libs = libsvn_repos libsvn_fs libsvn_fs_fs libsvn_subr apriconv apr +libs = libsvn_subr apr diff --git a/contrib/subversion/configure b/contrib/subversion/configure index 8f43929d9..19fee6eb8 100755 --- a/contrib/subversion/configure +++ b/contrib/subversion/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for subversion 1.8.14. +# Generated by GNU Autoconf 2.69 for subversion 1.9.4. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='subversion' PACKAGE_TARNAME='subversion' -PACKAGE_VERSION='1.8.14' -PACKAGE_STRING='subversion 1.8.14' +PACKAGE_VERSION='1.9.4' +PACKAGE_STRING='subversion 1.9.4' PACKAGE_BUGREPORT='http://subversion.apache.org/' PACKAGE_URL='' @@ -673,6 +673,7 @@ JNI_INCLUDES JAR JAVAH JAVADOC +JAVAC_COMPAT_FLAGS JAVAC_FLAGS JAVAC JAVA @@ -711,9 +712,12 @@ SVN_MAGIC_INCLUDES MSGFMTFLAGS NO_GETTEXT_CODESET GETTEXT_CODESET +SVN_INTL_LIBS XGETTEXT MSGMERGE MSGFMT +SVN_USE_GMOCK +GMOCK_SRCDIR SVN_GNOME_KEYRING_LIBS SVN_GNOME_KEYRING_INCLUDES SVN_HAVE_GPG_AGENT @@ -731,6 +735,7 @@ LT_LDFLAGS LT_CFLAGS SVN_LIBTOOL CXXCPP +LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO @@ -765,6 +770,7 @@ SVN_SERF_LIBS SVN_SERF_INCLUDES PKG_CONFIG SVN_LT_SOVERSION +SVN_APR_MAJOR_VERSION SVN_APRUTIL_LIBS SVN_APRUTIL_CONFIG SVN_APRUTIL_INCLUDES @@ -793,12 +799,16 @@ build_cpu build SED CPP +CXXUSERFLAGS CXXMAINTAINERFLAGS +CXXNOWARNFLAGS CXXMODEFLAGS ac_ct_CXX CXXFLAGS CXX +CUSERFLAGS CMAINTAINERFLAGS +CNOWARNFLAGS CMODEFLAGS OBJEXT EXEEXT @@ -865,6 +875,7 @@ enable_shared enable_static with_pic enable_fast_install +with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock @@ -880,12 +891,12 @@ with_sasl enable_keychain with_gpg_agent with_gnome_keyring +enable_gmock enable_ev2_impl enable_nls with_libmagic with_kwallet enable_plaintext_password_storage -with_openssl enable_debug enable_optimize enable_disallowing_of_undefined_references @@ -918,6 +929,7 @@ CXX CXXFLAGS CCC CPP +LT_SYS_LIBRARY_PATH CXXCPP' @@ -1459,7 +1471,7 @@ 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.14 to adapt to many kinds of systems. +\`configure' configures subversion 1.9.4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1525,7 +1537,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of subversion 1.8.14:";; + short | recursive ) echo "Configuration of subversion 1.9.4:";; esac cat <<\_ACEOF @@ -1534,7 +1546,7 @@ Optional Features: --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 + Force build 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] @@ -1558,6 +1570,7 @@ Optional Features: 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 + --disable-gmock Do not use the Googlemock testing framework --enable-ev2-impl Use Ev2 implementations, where available [EXPERIMENTAL] --disable-nls Disable gettext functionality @@ -1609,6 +1622,9 @@ Optional Packages: --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-aix-soname=aix|svr4|both + shared library versioning (aka "SONAME") variant to + provide on AIX, [default=aix]. --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). @@ -1630,14 +1646,6 @@ Optional Packages: (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. @@ -1673,6 +1681,8 @@ Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags CPP C preprocessor + LT_SYS_LIBRARY_PATH + User-defined run-time library search path. CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help @@ -1741,7 +1751,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -subversion configure 1.8.14 +subversion configure 1.9.4 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2285,7 +2295,7 @@ 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.14, which was +It was created by subversion $as_me 1.9.4, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2665,8 +2675,8 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. -{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Subversion 1.8.14" >&5 -$as_echo "$as_me: Configuring Subversion 1.8.14" >&6;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring Subversion 1.9.4" >&5 +$as_echo "$as_me: Configuring Subversion 1.9.4" >&6;} abs_srcdir="`cd $srcdir && pwd`" @@ -2706,7 +2716,6 @@ EOF # ==== Check for programs ==================================================== # Look for a C compiler (before anything can set CFLAGS) -CMAINTAINERFLAGS="$CUSERFLAGS" CUSERFLAGS="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3502,6 +3511,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu CFLAGS_KEEP="$CFLAGS" CFLAGS="" + if test "$GCC" = "yes"; then _svn_xxflags__save="$CFLAGS" ac_ext=c @@ -3618,13 +3628,62 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + fi CMODEFLAGS="$CFLAGS" + CFLAGS="" + + if test "$GCC" = "yes"; then + + _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 -w" >&5 +$as_echo_n "checking if $CC accepts -w... " >&6; } + CFLAGS="-w $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 + + CNOWARNFLAGS="$CFLAGS" CFLAGS="$CFLAGS_KEEP" + + + if test "$GCC" = "yes"; then + _svn_xxflags__save="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3662,10 +3721,10 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + fi # Look for a C++ compiler (before anything can set CXXFLAGS) -CXXMAINTAINERFLAGS="$CXXUSERFLAGS" CXXUSERFLAGS="$CXXFLAGS" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' @@ -3929,6 +3988,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu CXXFLAGS_KEEP="$CXXFLAGS" CXXFLAGS="" + if test "$GXX" = "yes"; then _svn_xxflags__save="$CXXFLAGS" ac_ext=cpp @@ -3967,13 +4027,62 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + fi CXXMODEFLAGS="$CXXFLAGS" + CXXFLAGS="" + + if test "$GXX" = "yes"; then + + _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 -w" >&5 +$as_echo_n "checking if $CXX accepts -w... " >&6; } + CXXFLAGS="-w $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 + + CXXNOWARNFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS_KEEP" + + + if test "$GXX" = "yes"; then + _svn_xxflags__save="$CXXFLAGS" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' @@ -4011,6 +4120,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + fi # Look for a C pre-processor @@ -4588,7 +4698,7 @@ 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_VER_REGEXES="1\.[3-9]\. 2\." APR_WANTED_REGEXES="$APR_VER_REGEXES" @@ -4606,7 +4716,7 @@ $as_echo "$as_me: Apache Portable Runtime (APR) library configuration" >&6;} TEST_X="test -x" fi - acceptable_majors="1 0" + acceptable_majors="2 1 0" apr_temp_acceptable_apr_config="" for apr_temp_major in $acceptable_majors @@ -4836,9 +4946,10 @@ if test `expr $apr_version : 2` -ne 0; then SVN_APRUTIL_CONFIG="$apu_config" + SVN_APR_MAJOR_VERSION=2 else svn_lib_ver=0 - APU_VER_REGEXES="0\.9\.[7-9] 0\.9\.1[0-9] 1\." + APU_VER_REGEXES="1\.[3-9]\." APRUTIL_WANTED_REGEXES="$APU_VER_REGEXES" @@ -5072,7 +5183,9 @@ $as_echo "#define SVN_HAVE_OLD_EXPAT 1" >>confdefs.h fi + SVN_APR_MAJOR_VERSION=1 fi + SVN_LT_SOVERSION="-version-info $svn_lib_ver" @@ -5258,9 +5371,9 @@ done serf_skip=no serf_check_major="1" - serf_check_minor="2" - serf_check_patch="1" - serf_check_version="1.2.1" + serf_check_minor="3" + serf_check_patch="4" + serf_check_version="1.3.4" # Check whether --with-serf was given. @@ -5271,11 +5384,70 @@ if test "${with_serf+set}" = set; then : elif test "$withval" = "no" ; then serf_skip=yes else + serf_required=yes + serf_prefix="$withval" + fi + +fi + + + if test "$serf_skip" = "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 test -n "$serf_prefix" ; then + if test -e "$serf_prefix/$serf_major.pc" ; then + serf_pc_arg="$serf_prefix/$serf_major.pc" + elif test -e "$serf_prefix/lib/pkgconfig/$serf_major.pc" ; then + serf_pc_arg="$serf_prefix/lib/pkgconfig/$serf_major.pc" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + continue + fi + else + serf_pc_arg="$serf_major" + fi + if $PKG_CONFIG $serf_pc_arg --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_pc_arg --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_pc_arg --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_pc_arg --cflags | $SED -e 's/-D[^ ]*//g'` + SVN_SERF_LIBS=`$PKG_CONFIG $serf_pc_arg --libs-only-l` + LDFLAGS="$LDFLAGS `$PKG_CONFIG $serf_pc_arg --libs | $SED -e 's/-l[^ ]*//g'`" + 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 + + if test -n "$serf_prefix" && test "$serf_found" = "no" ; then { $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" @@ -5417,50 +5589,6 @@ done 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 @@ -5650,7 +5778,7 @@ fi -HTTPD_WANTED_MMN="20020903" +HTTPD_WANTED_MMN="20051115" { $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; } @@ -5726,6 +5854,37 @@ else $as_echo "no" >&6; } 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; } + HTTPD_MAJOR=`$SED -ne '/^#define AP_SERVER_MAJORVERSION_NUMBER/p' "$APXS_INCLUDE/ap_release.h" | $SED -e 's/^.*NUMBER *//'` + HTTPD_MINOR=`$SED -ne '/^#define AP_SERVER_MINORVERSION_NUMBER/p' "$APXS_INCLUDE/ap_release.h" | $SED -e 's/^.*NUMBER *//'` + HTTPD_PATCH=`$SED -ne '/^#define AP_SERVER_PATCHLEVEL_NUMBER/p' "$APXS_INCLUDE/ap_release.h" | $SED -e 's/^.*NUMBER *//'` + HTTPD_VERSION="${HTTPD_MAJOR}.${HTTPD_MINOR}.${HTTPD_PATCH}" + case "$HTTPD_VERSION" in + 2.2.25 | 2.4.[5-6]) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: broken" >&5 +$as_echo "broken" >&6; } + as_fn_error $? "Apache httpd version $HTTPD_VERSION includes a broken mod_dav; use a newer version of httpd" "$LINENO" 5 + ;; + 2.[0-9]*.[0-9]*) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: acceptable" >&5 +$as_echo "acceptable" >&6; } + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unrecognised" >&5 +$as_echo "unrecognised" >&6; } + as_fn_error $? "Apache httpd version $HTTPD_VERSION not recognised" "$LINENO" 5 + ;; + esac +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; } @@ -5744,58 +5903,17 @@ $as_echo_n "checking whether Apache version is compatible with APR version... " 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 + case $HTTPD_MINOR in + $apache_minor_version_wanted_regex) + { $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 "$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" + as_fn_error $? "Apache version $HTTPD_VERSION incompatible with APR version $apr_version" "$LINENO" 5 + ;; + esac fi @@ -5818,29 +5936,85 @@ if test -n "$APXS" && test "$APXS" != "no"; 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. + for ac_header in unistd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_unistd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_UNISTD_H 1 +_ACEOF + for ac_func in getpid +do : + ac_fn_c_check_func "$LINENO" "getpid" "ac_cv_func_getpid" +if test "x$ac_cv_func_getpid" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETPID 1 +_ACEOF + +fi +done + +fi + +done + + + MMN_MAJOR=`$SED -ne '/^#define MODULE_MAGIC_NUMBER_MAJOR/p' "$APXS_INCLUDE/ap_mmn.h" | $SED -e 's/^.*MAJOR *//'` + MMN_MINOR=`$SED -ne '/^#define MODULE_MAGIC_NUMBER_MINOR/p' "$APXS_INCLUDE/ap_mmn.h" | $SED -e 's/^.*MINOR *//' | $SED -e 's/ .*//'` + if test "$MMN_MAJOR" = "20120211" && test "$MMN_MINOR" -lt "47" ; then + # This is httpd 2.4 and it doesn't appear to have the required + # API but the installation may have been patched. + # 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 "$as_me:${as_lineno-$LINENO}: checking for ap_some_authn_required" >&5 +$as_echo_n "checking for ap_some_authn_required... " >&6; } + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $APACHE_INCLUDES $SVN_APR_INCLUDES" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include "http_request.h" +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "int.*\sap_some_authn_required\s*\(" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + working_auth=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f conftest* + + CPPFLAGS="$old_CPPFLAGS" + if test "$working_auth" = "yes" ; then + +$as_echo "#define SVN_USE_FORCE_AUTHN 1" >>confdefs.h + + elif test "$enable_broken_httpd_auth" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ==============================================" >&5 +$as_echo "$as_me: WARNING: ==============================================" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Apache httpd $HTTPD_VERSION MMN $MMN_MAJOR.$MMN_MINOR" >&5 +$as_echo "$as_me: WARNING: Apache httpd $HTTPD_VERSION MMN $MMN_MAJOR.$MMN_MINOR" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion will be vulnerable to CVE-2015-3184" >&5 +$as_echo "$as_me: WARNING: Subversion will be vulnerable to CVE-2015-3184" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ==============================================" >&5 +$as_echo "$as_me: WARNING: ==============================================" >&2;} $as_echo "#define SVN_ALLOW_BROKEN_HTTPD_AUTH 1" >>confdefs.h + else + as_fn_error $? "Apache httpd $HTTPD_VERSION MMN $MMN_MAJOR.$MMN_MINOR has broken auth (CVE-2015-3184)" "$LINENO" 5 + fi fi + BUILD_APACHE_RULE=apache-mod + INSTALL_APACHE_RULE=install-mods-shared + INSTALL_APACHE_MODS=true case $host in *-*-cygwin*) APACHE_LDFLAGS="-shrext .so" @@ -7215,7 +7389,6 @@ 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 @@ -7280,8 +7453,8 @@ esac -macro_version='2.4.2.458.26-92994' -macro_revision='2.4.3' +macro_version='2.4.6' +macro_revision='2.4.6' @@ -8441,6 +8614,9 @@ sysv4 | sysv4.3*) tpf*) lt_cv_deplibs_check_method=pass_all ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; esac fi @@ -9530,6 +9706,21 @@ $as_echo "$lt_cv_truncate_bin" >&6; } + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; @@ -10537,6 +10728,41 @@ $as_echo "$lt_cv_ld_force_load" >&6; } ;; esac +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + 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 @@ -10696,6 +10922,58 @@ fi + shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[5-9]*,yes) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 +$as_echo_n "checking which variant of shared library versioning to provide... " >&6; } + +# Check whether --with-aix-soname was given. +if test "${with_aix_soname+set}" = set; then : + withval=$with_aix_soname; case $withval in + aix|svr4|both) + ;; + *) + as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname +else + if ${lt_cv_with_aix_soname+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_with_aix_soname=aix +fi + + with_aix_soname=$lt_cv_with_aix_soname +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 +$as_echo "$with_aix_soname" >&6; } + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + + + + + + + @@ -10815,15 +11093,8 @@ 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-%%"` +func_cc_basename $compiler +cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it @@ -11134,6 +11405,11 @@ lt_prog_compiler_static= # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac ;; darwin* | rhapsody*) @@ -11230,6 +11506,11 @@ lt_prog_compiler_static= # 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' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac ;; hpux9* | hpux10* | hpux11*) @@ -11870,6 +12151,34 @@ _LT_EOF link_all_deplibs=yes ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no @@ -11943,6 +12252,9 @@ _LT_EOF fi case $cc_basename in + tcc*) + export_dynamic_flag_spec='-rdynamic' + ;; 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' @@ -12072,19 +12384,35 @@ _LT_EOF 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". + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. 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' + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { 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' + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | 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. + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then @@ -12092,6 +12420,13 @@ _LT_EOF break fi done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi ;; esac @@ -12111,6 +12446,14 @@ _LT_EOF hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct=no + hardcode_direct_absolute=no + ;; + esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) @@ -12138,6 +12481,11 @@ _LT_EOF if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then @@ -12150,6 +12498,8 @@ _LT_EOF else shared_flag='$wl-bM:SRE' fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' fi fi @@ -12157,7 +12507,7 @@ _LT_EOF # 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 yes = "$aix_use_runtimelinking"; then + if test aix,yes = "$with_aix_soname,$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' @@ -12272,8 +12622,20 @@ fi 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='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; @@ -12592,6 +12954,16 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; } link_all_deplibs=yes ;; + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + ld_shlibs=yes + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out @@ -12637,8 +13009,28 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; } 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' + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes ;; osf3*) @@ -13161,6 +13553,8 @@ hardcode_into_libs=no # 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 @@ -13197,20 +13591,70 @@ aix[4-9]*) fi ;; esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in # 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 yes = "$aix_use_runtimelinking"; then + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.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' - else + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' # 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' - fi + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac shlibpath_var=LIBPATH fi ;; @@ -13398,7 +13842,8 @@ freebsd* | dragonfly*) 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$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; @@ -13458,10 +13903,11 @@ hpux9* | hpux10* | hpux11*) 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" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' @@ -13613,7 +14059,12 @@ fi # before this can be enabled. hardcode_into_libs=yes - # Append ld.so.conf contents to the search path + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) 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" @@ -13682,11 +14133,32 @@ openbsd* | bitrig*) os2*) libname_spec='$name' + version_type=windows shrext_cmds=.dll + need_version=no need_lib_prefix=no - library_names_spec='$libname$shared_ext $libname.a' + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + 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' ;; osf3* | osf4* | osf5*) @@ -13762,7 +14234,7 @@ sysv4*MP*) ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf + version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' @@ -13817,10 +14289,25 @@ fi 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 set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + @@ -14291,7 +14778,7 @@ else # endif #endif -/* When -fvisbility=hidden is used, assume the code has been annotated +/* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); @@ -14397,7 +14884,7 @@ else # endif #endif -/* When -fvisbility=hidden is used, assume the code has been annotated +/* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); @@ -14546,8 +15033,12 @@ $as_echo_n "checking whether to build shared libraries... " >&6; } ;; aix[4-9]*) - if test ia64 != "$host_cpu" && test no = "$aix_use_runtimelinking"; then - test yes = "$enable_shared" && enable_static=no + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac fi ;; esac @@ -14815,15 +15306,8 @@ $RM -r conftest* 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-%%"` + func_cc_basename $compiler +cc_basename=$func_cc_basename_result if test -n "$compiler"; then @@ -15014,7 +15498,19 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie # 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. + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in @@ -15024,6 +15520,13 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie ;; esac done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi ;; esac @@ -15043,6 +15546,14 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct_CXX=no + hardcode_direct_absolute_CXX=no + ;; + esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) @@ -15069,6 +15580,11 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then @@ -15081,6 +15597,8 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie else shared_flag='$wl-bM:SRE' fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' fi fi @@ -15089,10 +15607,11 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes - if test yes = "$aix_use_runtimelinking"; then + if test aix,yes = "$with_aix_soname,$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' + # The "-G" linker flag allows undefined symbols. + no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then @@ -15205,9 +15724,21 @@ fi 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='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; @@ -15339,6 +15870,34 @@ fi ;; + os2*) + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_minus_L_CXX=yes + allow_undefined_flag_CXX=unsupported + shrext_cmds=.dll + archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes_CXX=yes + ;; + dgux*) case $cc_basename in ec++*) @@ -16135,51 +16694,6 @@ interix[3-9]*) 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 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 yes != "$solaris_use_stlport4"; then - postdeps_CXX='-library=Cstd -library=Crun' - fi - ;; - esac - ;; esac @@ -16265,6 +16779,11 @@ lt_prog_compiler_static_CXX= # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static_CXX='$wl-static' + ;; + esac ;; darwin* | rhapsody*) # PIC is the default on this platform @@ -16802,13 +17321,17 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie 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". + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. 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' + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { 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' + export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) @@ -16994,6 +17517,8 @@ hardcode_into_libs=no # 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 @@ -17030,20 +17555,70 @@ aix[4-9]*) fi ;; esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in # 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 yes = "$aix_use_runtimelinking"; then + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.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' - else + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' # 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' - fi + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac shlibpath_var=LIBPATH fi ;; @@ -17229,7 +17804,8 @@ freebsd* | dragonfly*) 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$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; @@ -17289,10 +17865,11 @@ hpux9* | hpux10* | hpux11*) 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" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' @@ -17444,7 +18021,12 @@ fi # before this can be enabled. hardcode_into_libs=yes - # Append ld.so.conf contents to the search path + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) 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" @@ -17513,11 +18095,32 @@ openbsd* | bitrig*) os2*) libname_spec='$name' + version_type=windows shrext_cmds=.dll + need_version=no need_lib_prefix=no - library_names_spec='$libname$shared_ext $libname.a' + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + 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' ;; osf3* | osf4* | osf5*) @@ -17593,7 +18196,7 @@ sysv4*MP*) ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf + version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' @@ -17648,10 +18251,22 @@ fi 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 set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + @@ -17790,7 +18405,7 @@ if test "$experimental_libtool" = "yes"; then SVN_LIBTOOL="$sh_libtool" else sh_libtool="$abs_builddir/libtool" - SVN_LIBTOOL="\$(SHELL) $sh_libtool" + SVN_LIBTOOL="\$(SHELL) \"$sh_libtool\"" fi @@ -17890,22 +18505,6 @@ $as_echo "no" >&6; } 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. @@ -18912,18 +19511,27 @@ $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 + case "$host" in + *-*-darwin*) + if test "$with_gnome_keyring" = "yes"; then + as_fn_error $? "--with-gnome-keyring is not supported on Mac OS X." "$LINENO" 5 + else + with_gnome_keyring=no + fi + ;; + *) + 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 + 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" + 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 @@ -18931,53 +19539,55 @@ else fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNOME Keyring" >&5 + { $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 + 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`" + 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 GNOME Keyring" "$LINENO" 5 + as_fn_error $? "cannot find GLib and GNOME Keyring .pc files." "$LINENO" 5 + else + with_gnome_keyring=no 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 + 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 $? "cannot find pkg-config. GNOME Keyring requires this." "$LINENO" 5 + 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 $? "APR does not have support for DSOs. GNOME Keyring requires this." "$LINENO" 5 + as_fn_error $? "--with-gnome-keyring conflicts with --disable-shared" "$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 + ;; + esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -18985,6 +19595,34 @@ fi +# Check whether --enable-gmock was given. +if test "${enable_gmock+set}" = set; then : + enableval=$enable_gmock; +else + enable_gmock=yes +fi + + +GMOCK_SRCDIR=$abs_srcdir/gmock-fused + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether use Googlemock" >&5 +$as_echo_n "checking whether use Googlemock... " >&6; } +if test "$enable_gmock" != "no"; then + if test -d "$GMOCK_SRCDIR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SVN_USE_GMOCK=true + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SVN_USE_GMOCK=false + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SVN_USE_GMOCK_SOURCES=false +fi + # Check whether --enable-ev2-impl was given. if test "${enable_ev2_impl+set}" = set; then : @@ -19010,6 +19648,7 @@ fi USE_NLS="no" +SVN_INTL_LIBS="" 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 @@ -19189,6 +19828,13 @@ ac_res=$ac_cv_search_bindtextdomain if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + # in case libintl needs to be linked explicitly, + # $ac_cv_search_bindtextdomain contains -l linker flags + if echo "$ac_cv_search_bindtextdomain" | grep '^-l' >/dev/null + then + SVN_INTL_LIBS="$ac_cv_search_bindtextdomain" + fi + else enable_nls="no" @@ -19255,6 +19901,10 @@ if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" enable_nls="yes" + if echo "$ac_cv_search_bindtextdomain" | grep '^-l' >/dev/null + then + SVN_INTL_LIBS="$ac_cv_search_bindtextdomain" + fi # 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 @@ -19323,6 +19973,8 @@ fi + + GETTEXT_CODESET=\# NO_GETTEXT_CODESET=\# if test $USE_NLS = "yes"; then @@ -19582,29 +20234,34 @@ $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 + case "$host" in + *-*-darwin*) + as_fn_error $? "--with-kwallet is not supported on Mac OS X." "$LINENO" 5 + ;; + *) + 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 + 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 + 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 + 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 + 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. + 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; } @@ -19644,29 +20301,29 @@ $as_echo "no" >&6; } fi - fi - if test -n "$KDE4_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for KWallet" >&5 + 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" + 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_incdir="`$KDE4_CONFIG --install include`" + SVN_KWALLET_INCLUDES="$DBUS_CPPFLAGS $qt_include_dirs -I$kde_incdir" + 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_libdir="`$KDE4_CONFIG --install lib`" + LDFLAGS="$old_LDFLAGS ` + input_flags="$qt_lib_dirs -L$kde_libdir" output_flags="" filtered_dirs="/lib /lib64 /usr/lib /usr/lib64" for flag in $input_flags; do @@ -19685,13 +20342,13 @@ $as_echo_n "checking for KWallet... " >&6; } printf "%s" "${output_flags# }" fi `" - ac_ext=cpp + 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 + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -19705,42 +20362,44 @@ else fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - ac_ext=c + 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 + 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 + 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 + as_fn_error $? "cannot find KWallet" "$LINENO" 5 + fi + else + as_fn_error $? "cannot find kde4-config" "$LINENO" 5 fi else - as_fn_error $? "cannot find kde4-config" "$LINENO" 5 + { $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_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - as_fn_error $? "cannot find QtCore, QtDBus, QtGui" "$LINENO" 5 + as_fn_error $? "cannot find D-Bus" "$LINENO" 5 fi else - as_fn_error $? "cannot find D-Bus" "$LINENO" 5 + as_fn_error $? "cannot find pkg-config" "$LINENO" 5 fi else - as_fn_error $? "cannot find pkg-config" "$LINENO" 5 + as_fn_error $? "APR does not have support for DSOs" "$LINENO" 5 fi else - as_fn_error $? "APR does not have support for DSOs" "$LINENO" 5 + as_fn_error $? "--with-kwallet conflicts with --disable-shared" "$LINENO" 5 fi - else - as_fn_error $? "--with-kwallet conflicts with --disable-shared" "$LINENO" 5 - fi + ;; + esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -20167,13 +20826,6 @@ 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; @@ -20587,10 +21239,48 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu - CMAINTAINERFLAGS="$CFLAGS $CMAINTAINERFLAGS" + _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 -Wmissing-variable-declarations" >&5 +$as_echo_n "checking if $CC accepts -Wmissing-variable-declarations... " >&6; } + CFLAGS="-Wmissing-variable-declarations $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" 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" + 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 -Wbad-function-cast $CMAINTAINERFLAGS" fi if test "$GXX" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: maintainer-mode: adding G++ warning flags" >&5 @@ -20752,7 +21442,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu - CXXMAINTAINERFLAGS="$CXXFLAGS $CXXMAINTAINERFLAGS" + CXXMAINTAINERFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS_KEEP" CXXMAINTAINERFLAGS="-Wall -Wpointer-arith -Wwrite-strings -Wshadow -Wunused -Wunreachable-code $CXXMAINTAINERFLAGS" @@ -22145,8 +22835,8 @@ fi 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: Python 2.7 or later is required to run the testsuite" >&5 +$as_echo "$as_me: WARNING: Python 2.7 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 @@ -22206,7 +22896,7 @@ test -n "$PYTHON" || PYTHON="none" # The minimum version for the JVM runtime for our Java bytecode. -JAVA_OLDEST_WORKING_VER='1.5' +JAVA_OLDEST_WORKING_VER='1.6' # SVN_CHECK_JDK sets $JAVA_CLASSPATH JAVA_OLDEST_WORKING_VER="$JAVA_OLDEST_WORKING_VER" @@ -22235,8 +22925,10 @@ if test "${with_jdk+set}" = set; then : { $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 + if test -x "$JAVA_HOME/bin/java"; then JDK="$JAVA_HOME" + elif test -x "/usr/libexec/java_home"; then + JDK=`/usr/libexec/java_home` elif test -x "/Library/Java/Home/bin/java"; then JDK="/Library/Java/Home" elif test -x "/usr/bin/java"; then @@ -22351,9 +23043,12 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; # 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" + JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.6" if test "$enable_debugging" = "yes"; then - JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS" + JAVAC_FLAGS="-g -Xlint -Xlint:unchecked -Xlint:serial -Xlint:path $JAVAC_FLAGS" + if test -z "$JAVAC_COMPAT_FLAGS"; then + JAVAC_COMPAT_FLAGS="$JAVAC_FLAGS -Xlint:-unchecked -Xlint:-deprecation -Xlint:-dep-ann -Xlint:-rawtypes" + fi fi fi @@ -22373,6 +23068,7 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; + ;; *) @@ -22391,8 +23087,10 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; { $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 + if test -x "$JAVA_HOME/bin/java"; then JDK="$JAVA_HOME" + elif test -x "/usr/libexec/java_home"; then + JDK=`/usr/libexec/java_home` elif test -x "/Library/Java/Home/bin/java"; then JDK="/Library/Java/Home" elif test -x "/usr/bin/java"; then @@ -22507,9 +23205,12 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; # 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" + JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.6" if test "$enable_debugging" = "yes"; then - JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS" + JAVAC_FLAGS="-g -Xlint -Xlint:unchecked -Xlint:serial -Xlint:path $JAVAC_FLAGS" + if test -z "$JAVAC_COMPAT_FLAGS"; then + JAVAC_COMPAT_FLAGS="$JAVAC_FLAGS -Xlint:-unchecked -Xlint:-deprecation -Xlint:-dep-ann -Xlint:-rawtypes" + fi fi fi @@ -22529,6 +23230,7 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; + ;; esac @@ -22550,8 +23252,10 @@ else { $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 + if test -x "$JAVA_HOME/bin/java"; then JDK="$JAVA_HOME" + elif test -x "/usr/libexec/java_home"; then + JDK=`/usr/libexec/java_home` elif test -x "/Library/Java/Home/bin/java"; then JDK="/Library/Java/Home" elif test -x "/usr/bin/java"; then @@ -22666,9 +23370,12 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; # 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" + JAVAC_FLAGS="-target $JAVA_OLDEST_WORKING_VER -source 1.6" if test "$enable_debugging" = "yes"; then - JAVAC_FLAGS="-g -Xlint:unchecked $JAVAC_FLAGS" + JAVAC_FLAGS="-g -Xlint -Xlint:unchecked -Xlint:serial -Xlint:path $JAVAC_FLAGS" + if test -z "$JAVAC_COMPAT_FLAGS"; then + JAVAC_COMPAT_FLAGS="$JAVAC_FLAGS -Xlint:-unchecked -Xlint:-deprecation -Xlint:-dep-ann -Xlint:-rawtypes" + fi fi fi @@ -22689,6 +23396,7 @@ $as_echo "$as_me: WARNING: --with-jikes PATH was invalid, substitute found" >&2; + fi @@ -23077,14 +23785,15 @@ $as_echo "$SWIG_VERSION_RAW" >&6; } # 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 + if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024" && \ + test "$SWIG_VERSION" -lt "300000"; 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;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&5 +$as_echo "$as_me: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&2;} fi fi @@ -23565,14 +24274,15 @@ $as_echo "$SWIG_VERSION_RAW" >&6; } # 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 + if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024" && \ + test "$SWIG_VERSION" -lt "300000"; 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;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&5 +$as_echo "$as_me: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&2;} fi fi @@ -24053,14 +24763,15 @@ $as_echo "$SWIG_VERSION_RAW" >&6; } # 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 + if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024" && \ + test "$SWIG_VERSION" -lt "300000"; 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;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&5 +$as_echo "$as_me: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&2;} fi fi @@ -24544,14 +25255,15 @@ $as_echo "$SWIG_VERSION_RAW" >&6; } # 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 + if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103024" && \ + test "$SWIG_VERSION" -lt "300000"; 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;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&5 +$as_echo "$as_me: WARNING: Subversion requires SWIG >= 1.3.24 and < 3.0.0 " >&2;} fi fi @@ -25303,6 +26015,12 @@ $as_echo "#define SVN_LIBSVN_FS_LINKS_FS_FS 1" >>confdefs.h svn_fs_lib_install_deps="install-fsmod-lib" svn_fs_lib_link="\$(FS_FS_LINK)" + +$as_echo "#define SVN_LIBSVN_FS_LINKS_FS_X 1" >>confdefs.h + + svn_fs_lib_deps="$svn_fs_lib_deps \$(FS_X_DEPS)" + svn_fs_lib_link="$svn_fs_lib_link \$(FS_X_LINK)" + if test "$svn_lib_berkeley_db" = "yes"; then $as_echo "#define SVN_LIBSVN_FS_LINKS_FS_BASE 1" >>confdefs.h @@ -25363,9 +26081,17 @@ if test "$do_javahl_build" = "yes"; then { $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 + case "$host" in + *freebsd10*) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } + ;; + *) + LT_CXX_LIBADD="-lstdc++" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LT_CXX_LIBADD" >&5 $as_echo "$LT_CXX_LIBADD" >&6; } + ;; + esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } @@ -25398,6 +26124,20 @@ fi # ==== Miscellaneous bits ==================================================== +for ac_header in stdbool.h inttypes.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$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 + + # 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 @@ -25461,6 +26201,14 @@ ac_config_commands="$ac_config_commands svn_private_config.h.tmp" ac_config_files="$ac_config_files Makefile" +# Create pkg-config .pc files from .pc.in files +for pc_in_file in "${abs_srcdir}"/subversion/libsvn_*/*.pc.in; do + pc_file=${pc_in_file#${abs_srcdir}/} + pc_file=${pc_file%.in} + ac_config_files="$ac_config_files ${pc_file}" + +done + SVN_CONFIG_SCRIPT_FILES="$SVN_CONFIG_SCRIPT_FILES tools/backup/hot-backup.py" ac_config_files="$ac_config_files tools/backup/hot-backup.py" @@ -26002,7 +26750,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # 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.14, which was +This file was extended by subversion $as_me 1.9.4, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -26068,7 +26816,7 @@ _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.14 +subversion config.status 1.9.4 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -26201,6 +26949,7 @@ 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"`' +shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $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"`' @@ -26321,7 +27070,8 @@ 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"`' +configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' +configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $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"`' @@ -26524,7 +27274,8 @@ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ -sys_lib_dlsearch_path_spec \ +configure_time_dlsearch_path \ +configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ @@ -26579,6 +27330,7 @@ do "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" ;; + "${pc_file}") CONFIG_FILES="$CONFIG_FILES ${pc_file}" ;; "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" ;; @@ -27185,6 +27937,9 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} # The names of the tagged configurations supported by this script. available_tags='CXX ' +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? @@ -27203,6 +27958,9 @@ pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install +# Shared archive member basename,for filename based shared library versioning on AIX. +shared_archive_member_spec=$shared_archive_member_spec + # Shell to use when invoking shell scripts. SHELL=$lt_SHELL @@ -27431,8 +28189,11 @@ 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 +# Detected run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path + +# Explicit LT_SYS_LIBRARY_PATH set during ./configure time. +configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen @@ -27595,6 +28356,65 @@ compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + + +# ### END FUNCTIONS SHARED WITH CONFIGURE + _LT_EOF case $host_os in diff --git a/contrib/subversion/configure.ac b/contrib/subversion/configure.ac index b198c041a..4ed66d4fe 100644 --- a/contrib/subversion/configure.ac +++ b/contrib/subversion/configure.ac @@ -49,13 +49,11 @@ 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 @@ -92,13 +90,8 @@ AC_SUBST([MKDIR]) 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\."] +APR_VER_REGEXES=["1\.[3-9]\. 2\."] SVN_LIB_APR($APR_VER_REGEXES) @@ -111,11 +104,14 @@ if test `expr $apr_version : 2` -ne 0; then AC_SUBST(SVN_APRUTIL_INCLUDES) AC_SUBST(SVN_APRUTIL_CONFIG, ["$apu_config"]) AC_SUBST(SVN_APRUTIL_LIBS) + SVN_APR_MAJOR_VERSION=2 else svn_lib_ver=0 - APU_VER_REGEXES=["0\.9\.[7-9] 0\.9\.1[0-9] 1\."] + APU_VER_REGEXES=["1\.[3-9]\."] SVN_LIB_APRUTIL($APU_VER_REGEXES) + SVN_APR_MAJOR_VERSION=1 fi +AC_SUBST(SVN_APR_MAJOR_VERSION) SVN_LT_SOVERSION="-version-info $svn_lib_ver" AC_SUBST(SVN_LT_SOVERSION) AC_DEFINE_UNQUOTED(SVN_SOVERSION, $svn_lib_ver, @@ -125,7 +121,7 @@ dnl Search for pkg-config AC_PATH_PROG(PKG_CONFIG, pkg-config) dnl Search for serf -SVN_LIB_SERF(1,2,1) +SVN_LIB_SERF(1,3,4) if test "$svn_lib_serf" = "yes"; then AC_DEFINE([SVN_HAVE_SERF], 1, @@ -142,7 +138,7 @@ fi dnl Find Apache with a recent-enough magic module number -SVN_FIND_APACHE(20020903) +SVN_FIND_APACHE(20051115) 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 @@ -167,7 +163,6 @@ if test -n "$sqlite_compat_ver" && test "$sqlite_compat_ver" != no; then 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 @@ -227,7 +222,7 @@ if test "$experimental_libtool" = "yes"; then SVN_LIBTOOL="$sh_libtool" else sh_libtool="$abs_builddir/libtool" - SVN_LIBTOOL="\$(SHELL) $sh_libtool" + SVN_LIBTOOL="\$(SHELL) \"$sh_libtool\"" fi AC_SUBST(SVN_LIBTOOL) @@ -324,18 +319,6 @@ case $host in 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, @@ -570,64 +553,97 @@ 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 + case "$host" in + *-*-darwin*) + if test "$with_gnome_keyring" = "yes"; then + AC_MSG_ERROR([--with-gnome-keyring is not supported on Mac OS X.]) + else + with_gnome_keyring=no + fi + ;; + *) + 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]) - 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`" + 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 GNOME Keyring]) + AC_MSG_ERROR([cannot find GLib and GNOME Keyring .pc files.]) + else + with_gnome_keyring=no 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.]) + 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([cannot find pkg-config. GNOME Keyring requires this.]) + 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([APR does not have support for DSOs. GNOME Keyring requires this.]) + AC_MSG_ERROR([--with-gnome-keyring conflicts with --disable-shared]) 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 + ;; + esac else AC_MSG_RESULT([no]) fi AC_SUBST(SVN_GNOME_KEYRING_INCLUDES) AC_SUBST(SVN_GNOME_KEYRING_LIBS) +dnl Googlemock ----------------- +AC_ARG_ENABLE([gmock], + AS_HELP_STRING([--disable-gmock], + [Do not use the Googlemock testing framework]), + [], + [enable_gmock=yes]) + +AC_SUBST([GMOCK_SRCDIR], [$abs_srcdir/gmock-fused]) +AC_MSG_CHECKING([whether use Googlemock]) +if test "$enable_gmock" != "no"; then + if test -d "$GMOCK_SRCDIR"; then + AC_MSG_RESULT([yes]) + SVN_USE_GMOCK=true + else + AC_MSG_RESULT([no]) + SVN_USE_GMOCK=false + fi +else + AC_MSG_RESULT([no]) + SVN_USE_GMOCK_SOURCES=false +fi +AC_SUBST([SVN_USE_GMOCK]) dnl Ev2 experimental features ---------------------- dnl Note: The Ev2 implementations will be built unconditionally, but by @@ -651,13 +667,22 @@ AC_ARG_ENABLE(nls, [enable_nls=$enableval],[enable_nls=yes]) USE_NLS="no" +SVN_INTL_LIBS="" 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], [], + AC_SEARCH_LIBS(bindtextdomain, [intl], + [ + # in case libintl needs to be linked explicitly, + # $ac_cv_search_bindtextdomain contains -l linker flags + if echo "$ac_cv_search_bindtextdomain" | grep '^-l' >/dev/null + then + SVN_INTL_LIBS="$ac_cv_search_bindtextdomain" + fi + ], [ enable_nls="no" ]) @@ -669,6 +694,10 @@ if test "$enable_nls" = "yes"; then AC_SEARCH_LIBS(bindtextdomain, [intl], [ enable_nls="yes" + if echo "$ac_cv_search_bindtextdomain" | grep '^-l' >/dev/null + then + SVN_INTL_LIBS="$ac_cv_search_bindtextdomain" + fi # This is here so that -liconv ends up in LIBS # if it worked with -liconv. AC_CHECK_LIB(iconv, libiconv_open) @@ -687,6 +716,8 @@ if test "$enable_nls" = "yes"; then fi fi +AC_SUBST(SVN_INTL_LIBS) + 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 @@ -711,6 +742,30 @@ AH_BOTTOM([ #define gettext(x) (x) #define dgettext(domain, x) (x) #endif + +/* compiler hints */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +# define SVN__PREDICT_FALSE(x) (__builtin_expect(x, 0)) +# define SVN__PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +# define SVN__PREDICT_FALSE(x) (x) +# define SVN__PREDICT_TRUE(x) (x) +#endif + +#if defined(SVN_DEBUG) +# define SVN__FORCE_INLINE +# define SVN__PREVENT_INLINE +#elif defined(__GNUC__) +# define SVN__FORCE_INLINE APR_INLINE __attribute__ ((always_inline)) +# define SVN__PREVENT_INLINE __attribute__ ((noinline)) +#else +# define SVN__FORCE_INLINE APR_INLINE +# define SVN__PREVENT_INLINE +#endif + +/* Macro used to specify that a variable is intentionally left unused. + Supresses compiler warnings about the variable being unused. */ +#define SVN_UNUSED(v) ( (void)(v) ) ]) dnl Used to simulate makefile conditionals. @@ -887,16 +942,6 @@ AC_CHECK_HEADER(termios.h,[ 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]), @@ -989,7 +1034,7 @@ AS_HELP_STRING([--enable-maintainer-mode], 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 ./configure CFLAGS="-Wswitch-enum -Wswitch-default" dnl Add each of the following flags only if the C compiler accepts it. CFLAGS_KEEP="$CFLAGS" @@ -1003,12 +1048,13 @@ AS_HELP_STRING([--enable-maintainer-mode], SVN_CFLAGS_ADD_IFELSE([-Wold-style-definition]) SVN_CFLAGS_ADD_IFELSE([-Wno-system-headers]) SVN_CFLAGS_ADD_IFELSE([-Wno-format-nonliteral]) + SVN_CFLAGS_ADD_IFELSE([-Wmissing-variable-declarations]) - CMAINTAINERFLAGS="$CFLAGS $CMAINTAINERFLAGS" + CMAINTAINERFLAGS="$CFLAGS" 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" + 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 -Wbad-function-cast $CMAINTAINERFLAGS" fi if test "$GXX" = "yes"; then AC_MSG_NOTICE([maintainer-mode: adding G++ warning flags]) @@ -1022,7 +1068,7 @@ AS_HELP_STRING([--enable-maintainer-mode], SVN_CXXFLAGS_ADD_IFELSE([-Wshorten-64-to-32]) SVN_CXXFLAGS_ADD_IFELSE([-Wno-system-headers]) - CXXMAINTAINERFLAGS="$CXXFLAGS $CXXMAINTAINERFLAGS" + CXXMAINTAINERFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS_KEEP" dnl Add flags that all versions of G++ (should) support @@ -1227,7 +1273,7 @@ AS_HELP_STRING([--enable-gprof], 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([Python 2.7 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]) @@ -1237,7 +1283,7 @@ fi AC_PATH_PROGS(PYTHON, "$PYTHON", none) # The minimum version for the JVM runtime for our Java bytecode. -JAVA_OLDEST_WORKING_VER='1.5' +JAVA_OLDEST_WORKING_VER='1.6' # SVN_CHECK_JDK sets $JAVA_CLASSPATH SVN_CHECK_JDK($JAVA_OLDEST_WORKING_VER) @@ -1341,6 +1387,13 @@ if test "$svn_enable_shared" = "no" || test "$use_dso" != "yes"; then svn_fs_lib_install_deps="install-fsmod-lib" svn_fs_lib_link="\$(FS_FS_LINK)" + AC_DEFINE(SVN_LIBSVN_FS_LINKS_FS_X, 1, + [Defined if libsvn_fs should link against libsvn_fs_x]) + svn_fs_lib_deps="$svn_fs_lib_deps \$(FS_X_DEPS)" + svn_fs_lib_link="$svn_fs_lib_link \$(FS_X_LINK)" +dnl FSFS already installs fsmod +dnl svn_fs_lib_install_deps="$svn_fs_lib_install_deps install-fsmod-lib" + 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]) @@ -1402,8 +1455,15 @@ if test "$do_javahl_build" = "yes"; then # 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]) + case "$host" in + *freebsd10*) + AC_MSG_RESULT([none needed]) + ;; + *) + LT_CXX_LIBADD="-lstdc++" + AC_MSG_RESULT([$LT_CXX_LIBADD]) + ;; + esac else AC_MSG_RESULT([none needed]) fi @@ -1433,6 +1493,8 @@ AC_SUBST(JAVAHL_COMPAT_TESTS_TARGET) # ==== Miscellaneous bits ==================================================== +AC_CHECK_HEADERS([stdbool.h inttypes.h]) + # 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 @@ -1484,6 +1546,13 @@ AC_CONFIG_COMMANDS([svn_private_config.h.tmp], SVN_DB_HEADER="$SVN_DB_HEADER"]) AC_CONFIG_FILES([Makefile]) +# Create pkg-config .pc files from .pc.in files +for pc_in_file in "${abs_srcdir}"/subversion/libsvn_*/*.pc.in; do + pc_file=${pc_in_file#${abs_srcdir}/} + pc_file=${pc_file%.in} + AC_CONFIG_FILES([${pc_file}]) +done + 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) diff --git a/contrib/subversion/doc/programmer/gtest-guide.txt b/contrib/subversion/doc/programmer/gtest-guide.txt new file mode 100644 index 000000000..225690c2d --- /dev/null +++ b/contrib/subversion/doc/programmer/gtest-guide.txt @@ -0,0 +1,30 @@ +Googlemock and Googletest Suite for Subversion +--------------------------------------------- + +Googlemock and Googletest are external packages that are downloaded +and compiled on request, as part of regular compilations. + +Googlemock is available here: + + https://code.google.com/p/googlemock/ + +The source packages already include Googletest, which is available +here: + + https://code.google.com/p/googletest/ + +Subversion uses Googlemock and Googletest for the C++HL test suite. + + + +To configure Subversion to use Googlemock, type + + ./get-dep.sh gmock + +This will download Googlemock and put the fused source into the +'gmock-fused' directory. Once it's there, configure will pick it up +automatically (but you do have to re-run autogen.sh and configure). + +You can disable building Googlemock (and hence the C++HL test suite) +with the --disable-gmock configure option. This will tell configure to +ignore the gmock-fused directory. diff --git a/contrib/subversion/doc/user/svn-best-practices.html b/contrib/subversion/doc/user/svn-best-practices.html index 61ba1c6e5..b168786cf 100644 --- a/contrib/subversion/doc/user/svn-best-practices.html +++ b/contrib/subversion/doc/user/svn-best-practices.html @@ -53,7 +53,7 @@ root" contains exactly three subdirectories: /trunk, only one project root, or it may contain a number of them.

Book reference: Choosing + href="http://svnbook.red-bean.com/nightly/en/svn.reposadmin.planning.html#svn.reposadmin.projects.chooselayout">Choosing a Repository Layout.

@@ -70,10 +70,8 @@ change. You can mention this revision number in bug databases, or use it as an argument to svn merge should you want to undo the change or port it to another branch.

-

Book reference: "Subversion and Changesets" sidebar, - within chapter - 4.

+

Book reference: Changesets.

@@ -91,8 +89,11 @@ and your issue-tracking database as possible:

+

Track merges manually

+

### OBSOLETE RECOMMENDATION ###

+

When committing the result of a merge, be sure to write a descriptive log message that explains what was merged, something like:

@@ -103,6 +104,7 @@ like:

href="http://svnbook.red-bean.com/svnbook/ch04s03.html#svn-ch-4-sect-3.2">Tracking merges manually, and Merging a whole branch to another.

+
@@ -134,8 +136,7 @@ revision, and everything else is at an older revision.

Book reference: The - limitation of mixed revisions.

+ href="http://svnbook.red-bean.com/nightly/en/svn.basic.in-action.html#svn.basic.in-action.mixedrevs">Mixed-revision working copies.

@@ -180,39 +181,6 @@ of patient waiting while your client chugs away. You can rest assured, however, that unlike CVS, your large files won't incapacitate the server or affect other users.

- - -

Work around commands that don't understand copies/renames

- -

When a file or directory is copied or renamed, the Subversion repository -tracks that history. Unfortunately in Subversion 1.0, the only client -subcommand which actually takes advantage of this feature is svn -log. A number of other commands (such as svn diff and -svn cat) ought to be automatically following rename-history, -but aren't doing so yet.

- -

In all of these cases, a basic workaround is to use 'svn log --v' to discover the proper path within the older revision.

- -

For example, suppose you copied /trunk to -/branches/mybranch in revision 200, and then committed some -changes to /branches/mybranch/foo.c in subsequent revisions. -Now you'd like to compare revisions 80 and 250 of the file.

- -

If you have a working copy of the branch and run svn diff --r80:250 foo.c, you'll see an error about -/branches/mybranch/foo.c not existing in revision 80. To -remedy, you would run svn log -v on your branch or file to -discover that it was named /trunk/foo.c prior to revision 200, -and then compare the two URLs directly:

- -
-   $ svn diff http://.../trunk/foo.c@80 \
-              http://.../branches/mybranch/foo.c@200
-
- - -

Know when to create branches

diff --git a/contrib/subversion/gen-make.py b/contrib/subversion/gen-make.py index 2b49b5efb..2e5557bea 100755 --- a/contrib/subversion/gen-make.py +++ b/contrib/subversion/gen-make.py @@ -25,7 +25,9 @@ import os +import traceback import sys + import getopt try: my_getopt = getopt.gnu_getopt @@ -64,6 +66,8 @@ def main(fname, gentype, verfname=None, generator.write() generator.write_sqlite_headers() + generator.write_errno_table() + generator.write_config_keys() if ('--debug', '') in other_options: for dep_type, target_dict in generator.graph.deps.items(): @@ -192,12 +196,6 @@ def _usage_exit(err=None): print(" --enable-nls") print(" add support for gettext localization") print("") - print(" --enable-bdb-in-apr-util") - print(" configure APR-Util to use Berkeley DB") - print("") - print(" --enable-ml") - print(" enable use of ML assembler with zlib") - print("") print(" --disable-shared") print(" only build static libraries") print("") @@ -208,11 +206,18 @@ def _usage_exit(err=None): print(" Use static openssl") print("") print(" --vsnet-version=VER") - print(" generate for VS.NET version VER (2002, 2003, 2005, 2008, 2010 or 2012)") + print(" generate for VS.NET version VER (2002, 2003, 2005, 2008,") + print(" 2010, 2012, 2013 or 2015)") + print(" [only valid in combination with '-t vcproj']") + print("") + print(" -D NAME[=value]") + print(" define NAME macro during compilation") print(" [only valid in combination with '-t vcproj']") print("") print(" --with-apr_memcache=DIR") print(" the apr_memcache sources are in DIR") + print(" --disable-gmock") + print(" do not use Googlemock") sys.exit(1) @@ -221,16 +226,17 @@ def __init__(self): self.list = [] self.dict = {} - def add(self, opt, val): + def add(self, opt, val, overwrite=True): if opt in self.dict: - self.list[self.dict[opt]] = (opt, val) + if overwrite: + self.list[self.dict[opt]] = (opt, val) else: self.dict[opt] = len(self.list) self.list.append((opt, val)) if __name__ == '__main__': try: - opts, args = my_getopt(sys.argv[1:], 'st:', + opts, args = my_getopt(sys.argv[1:], 'st:D:', ['debug', 'release', 'reload', @@ -256,23 +262,17 @@ def add(self, opt, val): 'enable-purify', 'enable-quantify', 'enable-nls', - 'enable-bdb-in-apr-util', - 'enable-ml', 'disable-shared', 'installed-libs=', 'vsnet-version=', - - # Keep distributions that help by adding a path - # working. On unix this would be filtered by - # configure, but on Windows gen-make.py is used - # directly. - 'with-neon=', - 'without-neon', + 'disable-gmock', ]) if len(args) > 1: _usage_exit("Too many arguments") - except getopt.GetoptError, e: - _usage_exit(str(e)) + except getopt.GetoptError: + typ, val, tb = sys.exc_info() + msg = ''.join(traceback.format_exception_only(typ, val)) + _usage_exit(msg) conf = 'build.conf' skip = 0 @@ -306,9 +306,12 @@ def add(self, opt, val): gentype = val else: if opt == '--with-httpd': - rest.add('--with-apr', os.path.join(val, 'srclib', 'apr')) - rest.add('--with-apr-util', os.path.join(val, 'srclib', 'apr-util')) - rest.add('--with-apr-iconv', os.path.join(val, 'srclib', 'apr-iconv')) + rest.add('--with-apr', os.path.join(val, 'srclib', 'apr'), + overwrite=False) + rest.add('--with-apr-util', os.path.join(val, 'srclib', 'apr-util'), + overwrite=False) + rest.add('--with-apr-iconv', os.path.join(val, 'srclib', 'apr-iconv'), + overwrite=False) # Remember all options so that --reload and other scripts can use them opt_conf = open('gen-make.opts', 'w') diff --git a/contrib/subversion/get-deps.sh b/contrib/subversion/get-deps.sh index 16ce5f6ca..d371fa090 100755 --- a/contrib/subversion/get-deps.sh +++ b/contrib/subversion/get-deps.sh @@ -36,7 +36,7 @@ APU_VERSION=${APU_VERSION:-"1.5.1"} 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"} +GMOCK_VERSION=${GMOCK_VERSION:-"1.6.0"} HTTPD_VERSION=${HTTPD_VERSION:-"2.4.10"} APR_ICONV_VERSION=${APR_ICONV_VERSION:-"1.2.1"} @@ -46,8 +46,8 @@ 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/ +GMOCK=gmock-${GMOCK_VERSION} +GMOCK_URL=https://googlemock.googlecode.com/files/ HTTPD=httpd-${HTTPD_VERSION} APR_ICONV=apr-iconv-${APR_ICONV_VERSION} @@ -67,7 +67,7 @@ APACHE_MIRROR=http://archive.apache.org/dist # helpers usage() { echo "Usage: $0" - echo "Usage: $0 [ apr | serf | zlib | sqlite | gtest ] ..." + echo "Usage: $0 [ apr | serf | zlib | sqlite | gmock ] ..." exit $1 } @@ -122,23 +122,24 @@ get_sqlite() { } -get_gtest() { - test -d $BASEDIR/gtest && return +get_gmock() { + test -d $BASEDIR/gmock-fused && return cd $TEMPDIR - $HTTP_FETCH ${GTEST_URL}/${GTEST}.zip + $HTTP_FETCH ${GMOCK_URL}/${GMOCK}.zip cd $BASEDIR - unzip -q $TEMPDIR/$GTEST.zip + unzip -q $TEMPDIR/$GMOCK.zip - mv $GTEST gtest + mv $GMOCK/fused-src gmock-fused + rm -fr $GMOCK } # main() get_deps() { mkdir -p $TEMPDIR - for i in zlib serf sqlite-amalgamation apr apr-util gtest; do + for i in zlib serf sqlite-amalgamation apr apr-util gmock-fused; do if [ -d $i ]; then echo "Local directory '$i' already exists; the downloaded copy won't be used" >&2 fi diff --git a/contrib/subversion/subversion/include/mod_dav_svn.h b/contrib/subversion/subversion/include/mod_dav_svn.h index c498c5e97..223f2f1c6 100644 --- a/contrib/subversion/subversion/include/mod_dav_svn.h +++ b/contrib/subversion/subversion/include/mod_dav_svn.h @@ -40,7 +40,7 @@ extern "C" { /** Given an apache request @a r, a @a uri, and a @a root_path to the svn location block, process @a uri and return many things, allocated in - @a r->pool: + @a pool: - @a cleaned_uri: The uri with duplicate and trailing slashes removed. @@ -74,7 +74,25 @@ extern "C" { - @a relative_path: /!svn/blah/13/A/B/alpha - @a repos_path: A/B/alpha - @a trailing_slash: FALSE + + NOTE: The returned dav_error will be also allocated in @a pool, not + in @a r->pool. + + @since New in 1.9 */ +AP_MODULE_DECLARE(dav_error *) dav_svn_split_uri2(request_rec *r, + const char *uri_to_split, + const char *root_path, + const char **cleaned_uri, + int *trailing_slash, + const char **repos_basename, + const char **relative_path, + const char **repos_path, + apr_pool_t *pool); + +/** + * Same as dav_svn_split_uri2() but allocates the result in @a r->pool. + */ AP_MODULE_DECLARE(dav_error *) dav_svn_split_uri(request_rec *r, const char *uri, const char *root_path, @@ -87,7 +105,22 @@ AP_MODULE_DECLARE(dav_error *) dav_svn_split_uri(request_rec *r, /** * Given an apache request @a r and a @a root_path to the svn location - * block, set @a *repos_path to the path of the repository on disk. */ + * block, set @a *repos_path to the path of the repository on disk. + * Perform all allocations in @a pool. + * + * NOTE: The returned dav_error will be also allocated in @a pool, not + * in @a r->pool. + * + * @since New in 1.9 + */ +AP_MODULE_DECLARE(dav_error *) dav_svn_get_repos_path2(request_rec *r, + const char *root_path, + const char **repos_path, + apr_pool_t *pool); + +/** + * Same as dav_svn_get_repos_path2() but allocates the result in@a r->pool. + */ AP_MODULE_DECLARE(dav_error *) dav_svn_get_repos_path(request_rec *r, const char *root_path, const char **repos_path); diff --git a/contrib/subversion/subversion/include/private/svn_atomic.h b/contrib/subversion/subversion/include/private/svn_atomic.h index 187703b05..6a6b2dca6 100644 --- a/contrib/subversion/subversion/include/private/svn_atomic.h +++ b/contrib/subversion/subversion/include/private/svn_atomic.h @@ -46,39 +46,19 @@ extern "C" { */ /** The type used by all the other atomic operations. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_t apr_uint32_t -#else -#define svn_atomic_t apr_atomic_t -#endif /** Atomically read an #svn_atomic_t from memory. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_read(mem) apr_atomic_read32((mem)) -#else -#define svn_atomic_read(mem) apr_atomic_read((mem)) -#endif /** Atomically set an #svn_atomic_t in memory. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_set(mem, val) apr_atomic_set32((mem), (val)) -#else -#define svn_atomic_set(mem, val) apr_atomic_set((mem), (val)) -#endif /** Atomically increment an #svn_atomic_t. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_inc(mem) apr_atomic_inc32(mem) -#else -#define svn_atomic_inc(mem) apr_atomic_inc(mem) -#endif /** Atomically decrement an #svn_atomic_t. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_dec(mem) apr_atomic_dec32(mem) -#else -#define svn_atomic_dec(mem) apr_atomic_dec(mem) -#endif /** * Atomic compare-and-swap. @@ -91,13 +71,8 @@ extern "C" { * that on some platforms, the CAS function is implemented in a * way that is incompatible with the other atomic operations. */ -#if APR_VERSION_AT_LEAST(1, 0, 0) #define svn_atomic_cas(mem, with, cmp) \ apr_atomic_cas32((mem), (with), (cmp)) -#else -#define svn_atomic_cas(mem, with, cmp) \ - apr_atomic_cas((mem), (with), (cmp)) -#endif /** @} */ /** diff --git a/contrib/subversion/subversion/include/private/svn_auth_private.h b/contrib/subversion/subversion/include/private/svn_auth_private.h index 6c32688a3..89146b9ae 100644 --- a/contrib/subversion/subversion/include/private/svn_auth_private.h +++ b/contrib/subversion/subversion/include/private/svn_auth_private.h @@ -231,6 +231,16 @@ svn_auth__ssl_client_cert_pw_set(svn_boolean_t *done, svn_boolean_t non_interactive, apr_pool_t *pool); +/* Apply the specified configuration for connecting with SERVER_NAME + to the auth baton */ +svn_error_t * +svn_auth__make_session_auth(svn_auth_baton_t **session_auth_baton, + const svn_auth_baton_t *auth_baton, + apr_hash_t *config, + const char *server_name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + #if (defined(WIN32) && !defined(__MINGW32__)) || defined(DOXYGEN) /** * Set @a *provider to an authentication provider that implements diff --git a/contrib/subversion/subversion/include/private/svn_cache.h b/contrib/subversion/subversion/include/private/svn_cache.h index 08d2f09cb..166295d6c 100644 --- a/contrib/subversion/subversion/include/private/svn_cache.h +++ b/contrib/subversion/subversion/include/private/svn_cache.h @@ -173,6 +173,12 @@ typedef struct svn_cache__info_t * May be 0 if that information is not available. */ apr_uint64_t total_entries; + + /** Number of index buckets with the given number of entries. + * Bucket sizes larger than the array will saturate into the + * highest array index. + */ + apr_uint64_t histogram[32]; } svn_cache__info_t; /** @@ -251,7 +257,8 @@ svn_cache__create_memcache(svn_cache__t **cache_p, * Given @a config, returns an APR memcached interface in @a * *memcache_p allocated in @a result_pool if @a config contains entries in * the SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS section describing - * memcached servers; otherwise, sets @a *memcache_p to NULL. + * memcached servers; otherwise, sets @a *memcache_p to NULL. Use + * @a scratch_pool for temporary allocations. * * If Subversion was not built with apr_memcache_support, then raises * SVN_ERR_NO_APR_MEMCACHE if and only if @a config is configured to @@ -260,7 +267,8 @@ svn_cache__create_memcache(svn_cache__t **cache_p, svn_error_t * svn_cache__make_memcache_from_config(svn_memcache_t **memcache_p, svn_config_t *config, - apr_pool_t *result_pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** * Creates a new membuffer cache object in @a *cache. It will contain @@ -302,6 +310,33 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, svn_boolean_t allow_blocking_writes, apr_pool_t *result_pool); +/** + * @defgroup Standard priority classes for #svn_cache__create_membuffer_cache. + * @{ + */ + +/** + * Data in this priority class should not be removed from the cache unless + * absolutely necessary. Use of this should be very restricted. + */ +#define SVN_CACHE__MEMBUFFER_HIGH_PRIORITY 10000 + +/** + * Data in this priority class has a good chance to remain in cache unless + * there is more data in this class than the cache's capacity. Use of this + * as the default for all information that is costly to fetch from disk. + */ +#define SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY 1000 + +/** + * Data in this priority class will be removed as soon as the cache starts + * filling up. Use of this for ephemeral data that can easily be acquired + * again from other sources. + */ +#define SVN_CACHE__MEMBUFFER_LOW_PRIORITY 100 + +/** @} */ + /** * Creates a new cache in @a *cache_p, storing the data in a potentially * shared @a membuffer object. The elements in the cache will be indexed @@ -310,7 +345,10 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, * serialize_func and deserialized using @a deserialize_func. Because * the same memcache object may cache many different kinds of values * form multiple caches, @a prefix should be specified to differentiate - * this cache from other caches. @a *cache_p will be allocated in @a result_pool. + * this cache from other caches. All entries written through this cache + * interface will be assigned into the given @a priority class. @a *cache_p + * will be allocated in @a result_pool. @a scratch_pool is used for + * temporary allocations. * * If @a deserialize_func is NULL, then the data is returned as an * svn_stringbuf_t; if @a serialize_func is NULL, then the data is @@ -329,8 +367,10 @@ svn_cache__create_membuffer_cache(svn_cache__t **cache_p, svn_cache__deserialize_func_t deserialize, apr_ssize_t klen, const char *prefix, + apr_uint32_t priority, svn_boolean_t thread_safe, - apr_pool_t *result_pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** * Sets @a handler to be @a cache's error handling routine. If any @@ -374,6 +414,18 @@ svn_cache__get(void **value, const void *key, apr_pool_t *result_pool); +/** + * Looks for an entry indexed by @a key in @a cache, setting @a *found + * to TRUE if an entry has been found and FALSE otherwise. @a key may be + * NULL in which case @a *found will be FALSE. Temporary allocations will + * be made from @a scratch_pool. + */ +svn_error_t * +svn_cache__has_key(svn_boolean_t *found, + svn_cache__t *cache, + const void *key, + apr_pool_t *scratch_pool); + /** * Stores the value @a value under the key @a key in @a cache. Uses @a * scratch_pool for temporary allocations. The cache makes copies of @@ -465,13 +517,16 @@ svn_cache__get_info(svn_cache__t *cache, /** * Return the information given in @a info formatted as a multi-line string. - * Allocations take place in @a result_pool. + * If @a access_only has been set, size and fill-level statistics will be + * omitted. Allocations take place in @a result_pool. */ svn_string_t * svn_cache__format_info(const svn_cache__info_t *info, + svn_boolean_t access_only, apr_pool_t *result_pool); -/* Access the process-global (singleton) membuffer cache. The first call +/** + * Access the process-global (singleton) membuffer cache. The first call * will automatically allocate the cache using the current cache config. * NULL will be returned if the desired cache size is 0. * @@ -480,6 +535,22 @@ svn_cache__format_info(const svn_cache__info_t *info, struct svn_membuffer_t * svn_cache__get_global_membuffer_cache(void); +/** + * Return total access and size stats over all membuffer caches as they + * share the underlying data buffer. The result will be allocated in POOL. + */ +svn_cache__info_t * +svn_cache__membuffer_get_global_info(apr_pool_t *pool); + +/** + * Remove all current contents from CACHE. + * + * NOTE: In a multi-threaded environment, new contents may have been put + * into the cache by the time this function returns. + */ +svn_error_t * +svn_cache__membuffer_clear(svn_membuffer_t *cache); + /** @} */ diff --git a/contrib/subversion/subversion/include/private/svn_client_mtcc.h b/contrib/subversion/subversion/include/private/svn_client_mtcc.h new file mode 100644 index 000000000..fe670b04e --- /dev/null +++ b/contrib/subversion/subversion/include/private/svn_client_mtcc.h @@ -0,0 +1,226 @@ +/** + * @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_client_mtcc.h + * @brief Subversion multicommand client support + * + * Requires: The working copy library and client library. + * Provides: High level multicommand api. + * Used By: Client programs, svnmucc. + */ + +#ifndef SVN_CLIENT_MTCC_H +#define SVN_CLIENT_MTCC_H + +#include "svn_client.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/** + * + * @defgroup clnt_mtcc Multi Command Context related functions + * + * @{ + * + */ + +/** This is a structure which stores a list of repository commands + * that can be played to a repository as a single operation + * + * Use svn_client__mtcc_create() to create instances + * + * @since New in 1.9. + */ +typedef struct svn_client__mtcc_t svn_client__mtcc_t; + +/** Creates a new multicommand context for an operation on @a anchor_url and + * its descendants. + * + * Allocate the context in @a result_pool and perform temporary allocations in + * @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_create(svn_client__mtcc_t **mtcc, + const char *anchor_url, + svn_revnum_t base_revision, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Adds a file add operation of @a relpath to @a mtcc. If @a src_checksum + * is not null it will be provided to the repository to verify if the file + * was transferred successfully. + * + * Perform temporary allocations in @a scratch_pool. + * + * @note The current implementation keeps @a src_stream open until @a mtcc + * is committed. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_add_file(const char *relpath, + svn_stream_t *src_stream, + const svn_checksum_t *src_checksum, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Adds a copy operation of the node @a src_relpath at revision @a revision + * to @a dst_relpath to @a mtcc. + * + * Perform temporary allocations in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_copy(const char *src_relpath, + svn_revnum_t revision, + const char *dst_relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Adds a delete of @a relpath to @a mtcc. + * + * Perform temporary allocations in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_delete(const char *relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Adds an mkdir operation of @a relpath to @a mtcc. + * + * Perform temporary allocations in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_mkdir(const char *relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + + +/** Adds a move operation of the node @a src_relpath to @a dst_relpath to + * @a mtcc. + * + * Perform temporary allocations in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_move(const char *src_relpath, + const char *dst_relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Adds a propset operation for the property @a propname to @a propval + * (which can be NULL for a delete) on @a relpath to @a mtcc. + * + * If @a skip_checks is not FALSE Subversion defined properties are verified + * for correctness like svn_client_propset_remote() + * + * Perform temporary allocations in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_propset(const char *relpath, + const char *propname, + const svn_string_t *propval, + svn_boolean_t skip_checks, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + + +/** Adds an update file operation for @a relpath to @a mtcc. + * + * The final version of the file is provided with @a src_stream. If @a + * src_checksum is provided it will be provided to the repository to verify + * the final result. + * + * If @a base_checksum is provided it will be used by the repository to verify + * if the base file matches this checksum. + * + * If @a base_stream is not NULL only the binary diff from @a base_stream to + * @a src_stream is written to the repository. + * + * Perform temporary allocations in @a scratch_pool. + * + * @note Callers should assume that the mtcc requires @a src_stream and @a + * base_stream to be valid until @a mtcc is committed. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_add_update_file(const char *relpath, + svn_stream_t *src_stream, + const svn_checksum_t *src_checksum, + svn_stream_t *base_stream, + const svn_checksum_t *base_checksum, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Obtains the kind of node at @a relpath in the current state of @a mtcc. + * This value might be from the cache (in case of modifications, copies) + * or fetched from the repository. + * + * If @a check_repository is TRUE, verify the node type with the repository at + * least once and cache the result for further checks. + * + * When a node does not exist this functions sets @a *kind to @c svn_node_node. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_check_path(svn_node_kind_t *kind, + const char *relpath, + svn_boolean_t check_repository, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + +/** Commits all operations stored in @a mtcc as a new revision and destroys + * @a mtcc. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client__mtcc_commit(apr_hash_t *revprop_table, + svn_commit_callback2_t commit_callback, + void *commit_baton, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool); + + +/** @} end group: Multi Command Context related functions */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_CLIENT_MTCC_H */ diff --git a/contrib/subversion/subversion/include/private/svn_client_private.h b/contrib/subversion/subversion/include/private/svn_client_private.h index 91ea6476b..892fc4b0b 100644 --- a/contrib/subversion/subversion/include/private/svn_client_private.h +++ b/contrib/subversion/subversion/include/private/svn_client_private.h @@ -33,11 +33,58 @@ #include "svn_client.h" #include "svn_types.h" +#include "private/svn_diff_tree.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/* Set *REVNUM to the revision number identified by REVISION. + + If REVISION->kind is svn_opt_revision_number, just use + REVISION->value.number, ignoring LOCAL_ABSPATH and RA_SESSION. + + Else if REVISION->kind is svn_opt_revision_committed, + svn_opt_revision_previous, or svn_opt_revision_base, or + svn_opt_revision_working, then the revision can be identified + purely based on the working copy's administrative information for + LOCAL_ABSPATH, so RA_SESSION is ignored. If LOCAL_ABSPATH is not + under revision control, return SVN_ERR_UNVERSIONED_RESOURCE, or if + LOCAL_ABSPATH is null, return SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED. + + Else if REVISION->kind is svn_opt_revision_date or + svn_opt_revision_head, then RA_SESSION is used to retrieve the + revision from the repository (using REVISION->value.date in the + former case), and LOCAL_ABSPATH is ignored. If RA_SESSION is null, + return SVN_ERR_CLIENT_RA_ACCESS_REQUIRED. + + Else if REVISION->kind is svn_opt_revision_unspecified, set + *REVNUM to SVN_INVALID_REVNUM. + + If YOUNGEST_REV is non-NULL, it is an in/out parameter. If + *YOUNGEST_REV is valid, use it as the youngest revision in the + repository (regardless of reality) -- don't bother to lookup the + true value for HEAD, and don't return any value in *REVNUM greater + than *YOUNGEST_REV. If *YOUNGEST_REV is not valid, and a HEAD + lookup is required to populate *REVNUM, then also populate + *YOUNGEST_REV with the result. This is useful for making multiple + serialized calls to this function with a basically static view of + the repository, avoiding race conditions which could occur between + multiple invocations with HEAD lookup requests. + + Else return SVN_ERR_CLIENT_BAD_REVISION. + + Use SCRATCH_POOL for any temporary allocation. */ +svn_error_t * +svn_client__get_revision_number(svn_revnum_t *revnum, + svn_revnum_t *youngest_rev, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_ra_session_t *ra_session, + const svn_opt_revision_t *revision, + apr_pool_t *scratch_pool); + /* Return true if KIND is a revision kind that is dependent on the working * copy. Otherwise, return false. */ #define SVN_CLIENT__REVKIND_NEEDS_WC(kind) \ @@ -200,29 +247,6 @@ svn_client__create_status(svn_client_status_t **cst, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Set *ANCESTOR_URL and *ANCESTOR_REVISION to the URL and revision, - * respectively, of the youngest common ancestor of the two locations - * PATH_OR_URL1@REV1 and PATH_OR_URL2@REV2. Set *ANCESTOR_RELPATH to - * NULL and *ANCESTOR_REVISION to SVN_INVALID_REVNUM if they have no - * common ancestor. This function assumes that PATH_OR_URL1@REV1 and - * PATH_OR_URL2@REV2 both refer to the same repository. - * - * Use the authentication baton cached in CTX to authenticate against - * the repository. - * - * See also svn_client__get_youngest_common_ancestor(). - */ -svn_error_t * -svn_client__youngest_common_ancestor(const char **ancestor_url, - svn_revnum_t *ancestor_rev, - const char *path_or_url1, - const svn_opt_revision_t *revision1, - const char *path_or_url2, - const svn_opt_revision_t *revision2, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* Get the repository location of the base node at LOCAL_ABSPATH. * * A pathrev_t wrapper around svn_wc__node_get_base(). @@ -257,20 +281,6 @@ svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Produce a diff with depth DEPTH between two files or two directories at - * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks to - * show changes in files. The files and directories involved may be part of - * a working copy or they may be unversioned. For versioned files, show - * property changes, too. */ -svn_error_t * -svn_client__arbitrary_nodes_diff(const char *local_abspath1, - const char *local_abspath2, - svn_depth_t depth, - const svn_wc_diff_callbacks4_t *callbacks, - void *callback_baton, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool); - /* Copy the file or directory on URL in some repository to DST_ABSPATH, * copying node information and properties. Resolve URL using PEG_REV and * REVISION. diff --git a/contrib/subversion/subversion/include/private/svn_cmdline_private.h b/contrib/subversion/subversion/include/private/svn_cmdline_private.h index ad16b66cd..f21a5d2e9 100644 --- a/contrib/subversion/subversion/include/private/svn_cmdline_private.h +++ b/contrib/subversion/subversion/include/private/svn_cmdline_private.h @@ -88,11 +88,15 @@ typedef struct svn_cmdline__config_argument_t * containing svn_cmdline__config_argument_t* elements, allocating the option * data in @a pool * + * [Since 1.9/1.10:] If the file, section, or option value is not recognized, + * warn to @c stderr, using @a prefix as in svn_handle_warning2(). + * * @since New in 1.7. */ svn_error_t * svn_cmdline__parse_config_option(apr_array_header_t *config_options, const char *opt_arg, + const char *prefix, apr_pool_t *pool); /** Sets the config options in @a config_options, an apr array containing @@ -220,6 +224,20 @@ svn_boolean_t svn_cmdline__be_interactive(svn_boolean_t non_interactive, svn_boolean_t force_interactive); +/* Parses the argument value of '--trust-server-cert-failures' OPT_ARG into + * the expected booleans for passing to svn_cmdline_create_auth_baton2() + * + * @since New in 1.9. + */ +svn_error_t * +svn_cmdline__parse_trust_options( + svn_boolean_t *trust_server_cert_unknown_ca, + svn_boolean_t *trust_server_cert_cn_mismatch, + svn_boolean_t *trust_server_cert_expired, + svn_boolean_t *trust_server_cert_not_yet_valid, + svn_boolean_t *trust_server_cert_other_failure, + const char *opt_arg, + apr_pool_t *scratch_pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/include/private/svn_delta_private.h b/contrib/subversion/subversion/include/private/svn_delta_private.h index 4de85a94b..260327c43 100644 --- a/contrib/subversion/subversion/include/private/svn_delta_private.h +++ b/contrib/subversion/subversion/include/private/svn_delta_private.h @@ -101,25 +101,12 @@ svn_delta__delta_from_editor(const svn_delta_editor_t **deditor, struct svn_delta__extra_baton *exb, apr_pool_t *pool); -/** - * Get the data from IN, compress it according to the specified - * COMPRESSION_LEVEL and write the result to OUT. - * SVN_DELTA_COMPRESSION_LEVEL_NONE is valid for COMPRESSION_LEVEL. - */ +/** Read the txdelta window header from @a stream and return the total + length of the unparsed window data in @a *window_len. */ svn_error_t * -svn__compress(svn_string_t *in, - svn_stringbuf_t *out, - int compression_level); - -/** - * Get the compressed data from IN, decompress it and write the result to - * OUT. Return an error if the decompressed size is larger than LIMIT. - */ -svn_error_t * -svn__decompress(svn_string_t *in, - svn_stringbuf_t *out, - apr_size_t limit); - +svn_txdelta__read_raw_window_len(apr_size_t *window_len, + svn_stream_t *stream, + apr_pool_t *pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/include/private/svn_dep_compat.h b/contrib/subversion/subversion/include/private/svn_dep_compat.h index 108b67cd6..729cf7e4d 100644 --- a/contrib/subversion/subversion/include/private/svn_dep_compat.h +++ b/contrib/subversion/subversion/include/private/svn_dep_compat.h @@ -20,7 +20,7 @@ * ==================================================================== * @endcopyright * - * @file svn_compat.h + * @file svn_dep_compat.h * @brief Compatibility macros and functions. * @since New in 1.5.0. */ @@ -35,71 +35,30 @@ extern "C" { #endif /* __cplusplus */ /** - * Check at compile time if the APR version is at least a certain - * level. - * @param major The major version component of the version checked - * for (e.g., the "1" of "1.3.0"). - * @param minor The minor version component of the version checked - * for (e.g., the "3" of "1.3.0"). - * @param patch The patch level component of the version checked - * for (e.g., the "0" of "1.3.0"). + * We assume that 'int' and 'unsigned' are at least 32 bits wide. + * This also implies that long (rev numbers) is 32 bits or wider. * - * @since New in 1.5. - */ -#ifndef APR_VERSION_AT_LEAST /* Introduced in APR 1.3.0 */ -#define APR_VERSION_AT_LEAST(major,minor,patch) \ -(((major) < APR_MAJOR_VERSION) \ - || ((major) == APR_MAJOR_VERSION && (minor) < APR_MINOR_VERSION) \ - || ((major) == APR_MAJOR_VERSION && (minor) == APR_MINOR_VERSION && \ - (patch) <= APR_PATCH_VERSION)) -#endif /* APR_VERSION_AT_LEAST */ - -/** - * If we don't have a recent enough APR, emulate the behavior of the - * apr_array_clear() API. + * @since New in 1.9. */ -#if !APR_VERSION_AT_LEAST(1,3,0) -#define apr_array_clear(arr) (arr)->nelts = 0 +#if defined(APR_HAVE_LIMITS_H) \ + && !defined(SVN_ALLOW_SHORT_INTS) \ + && (INT_MAX < 0x7FFFFFFFl) +#error int is shorter than 32 bits and may break Subversion. Define SVN_ALLOW_SHORT_INTS to skip this check. #endif -#if !APR_VERSION_AT_LEAST(1,3,0) -/* Equivalent to the apr_hash_clear() function in APR >= 1.3.0. Used to - * implement the 'apr_hash_clear' macro if the version of APR that - * we build against does not provide the apr_hash_clear() function. */ -void svn_hash__clear(struct apr_hash_t *ht); - /** - * If we don't have a recent enough APR, emulate the behavior of the - * apr_hash_clear() API. + * We assume that 'char' is 8 bits wide. The critical interfaces are + * our repository formats and RA encodings. E.g. a 32 bit wide char may + * mess up UTF8 parsing, how we interpret size values etc. + * + * @since New in 1.9. */ -#define apr_hash_clear(ht) svn_hash__clear(ht) +#if defined(CHAR_BIT) \ + && !defined(SVN_ALLOW_NON_8_BIT_CHARS) \ + && (CHAR_BIT != 8) +#error char is not 8 bits and may break Subversion. Define SVN_ALLOW_NON_8_BIT_CHARS to skip this check. #endif -#if !APR_VERSION_AT_LEAST(1,0,0) -#define APR_UINT64_C(val) UINT64_C(val) -#define APR_FPROT_OS_DEFAULT APR_OS_DEFAULT -#define apr_hash_make_custom(pool,hash_func) apr_hash_make(pool) -#endif - -#if !APR_VERSION_AT_LEAST(1,3,0) -#define APR_UINT16_MAX 0xFFFFU -#define APR_INT16_MAX 0x7FFF -#define APR_INT16_MIN (-APR_INT16_MAX-1) -#define APR_UINT32_MAX 0xFFFFFFFFU -#define APR_INT32_MAX 0x7FFFFFFF -#define APR_INT32_MIN (-APR_INT32_MAX-1) -#define APR_UINT64_MAX APR_UINT64_C(0xFFFFFFFFFFFFFFFF) -#define APR_INT64_MAX APR_INT64_C(0x7FFFFFFFFFFFFFFF) -#define APR_INT64_MIN (-APR_INT64_MAX-1) -#define APR_SIZE_MAX (~(apr_size_t)0) - -#if APR_SIZEOF_VOIDP == 8 -typedef apr_uint64_t apr_uintptr_t; -#else -typedef apr_uint32_t apr_uintptr_t; -#endif -#endif /* !APR_VERSION_AT_LEAST(1,3,0) */ - /** * Work around a platform dependency issue. apr_thread_rwlock_trywrlock() * will make APR_STATUS_IS_EBUSY() return TRUE if the lock could not be @@ -115,6 +74,19 @@ typedef apr_uint32_t apr_uintptr_t; #define SVN_LOCK_IS_BUSY(x) APR_STATUS_IS_EBUSY(x) #endif +/** + * APR keeps a few interesting defines hidden away in its private + * headers apr_arch_file_io.h, so we redefined them here. + * + * @since New in 1.9 + */ +#ifndef APR_FREADONLY +#define APR_FREADONLY 0x10000000 +#endif +#ifndef APR_OPENINFO +#define APR_OPENINFO 0x00100000 +#endif + #if !APR_VERSION_AT_LEAST(1,4,0) #ifndef apr_time_from_msec #define apr_time_from_msec(msec) ((apr_time_t)(msec) * 1000) diff --git a/contrib/subversion/subversion/include/private/svn_diff_private.h b/contrib/subversion/subversion/include/private/svn_diff_private.h index bb17c1717..48b4d5266 100644 --- a/contrib/subversion/subversion/include/private/svn_diff_private.h +++ b/contrib/subversion/subversion/include/private/svn_diff_private.h @@ -97,6 +97,9 @@ svn_diff__unidiff_write_header(svn_stream_t *output_stream, * merged or reverse merged; otherwise (or if the mergeinfo property values * don't parse correctly) display them just like any other property. * + * Pass @a context_size, @a cancel_func and @a cancel_baton to the diff + * output functions. + * * Use @a scratch_pool for temporary allocations. */ svn_error_t * @@ -105,6 +108,9 @@ svn_diff__display_prop_diffs(svn_stream_t *outstream, const apr_array_header_t *propchanges, apr_hash_t *original_props, svn_boolean_t pretty_print_mergeinfo, + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool); diff --git a/contrib/subversion/subversion/include/private/svn_diff_tree.h b/contrib/subversion/subversion/include/private/svn_diff_tree.h index 8cd4c0e71..4554da2b4 100644 --- a/contrib/subversion/subversion/include/private/svn_diff_tree.h +++ b/contrib/subversion/subversion/include/private/svn_diff_tree.h @@ -103,18 +103,22 @@ extern "C" { * Note that it is possible for nodes to be described as a delete followed by * an add at the same place within one parent. (Iff the diff is reversed you * can see an add followed by a delete!) + * ### "An add followed by a delete" sounds wrong. * * The directory batons live between the open and close events of a directory * and are thereby guaranteed to outlive the batons of their descendants. */ /* Describes the source of a merge */ +/* ### You mean a diff? + * ### How come many users don't set the 'repos_relpath' field? */ typedef struct svn_diff_source_t { /* Always available */ svn_revnum_t revision; /* Depending on the driver available for copyfrom */ + /* ### What? */ const char *repos_relpath; } svn_diff_source_t; @@ -137,9 +141,10 @@ typedef struct svn_diff_tree_processor_t { /** The value passed to svn_diff__tree_processor_create() as BATON. */ - void *baton; /* To avoid an additional in some places */ + void *baton; /* To avoid an additional in some places + * ### What? */ - /* Called before a directories children are processed. + /* Called before a directory's children are processed. * * Set *SKIP_CHILDREN to TRUE, to skip calling callbacks for all * children. @@ -319,7 +324,7 @@ svn_diff__tree_processor_filter_create(const svn_diff_tree_processor_t *processo apr_pool_t *result_pool); /** - * Create a new svn_diff_tree_processor_t instace with all function setup + * Create a new svn_diff_tree_processor_t instance with all function setup * to call into processor with all adds with copyfrom information transformed * to simple node changes. * diff --git a/contrib/subversion/subversion/include/private/svn_editor.h b/contrib/subversion/subversion/include/private/svn_editor.h index d714bb17a..aa8a2ac09 100644 --- a/contrib/subversion/subversion/include/private/svn_editor.h +++ b/contrib/subversion/subversion/include/private/svn_editor.h @@ -270,7 +270,6 @@ svn_delta_shim_callbacks_default(apr_pool_t *result_pool); * svn_editor_setcb_delete() \n * svn_editor_setcb_copy() \n * svn_editor_setcb_move() \n - * svn_editor_setcb_rotate() \n * svn_editor_setcb_complete() \n * svn_editor_setcb_abort() * @@ -293,7 +292,6 @@ svn_delta_shim_callbacks_default(apr_pool_t *result_pool); * svn_editor_delete() \n * svn_editor_copy() \n * svn_editor_move() \n - * svn_editor_rotate() * \n\n * Just before each callback invocation is carried out, the @a cancel_func * that was passed to svn_editor_create() is invoked to poll any @@ -325,7 +323,7 @@ svn_delta_shim_callbacks_default(apr_pool_t *result_pool); * In order to reduce complexity of callback receivers, the editor callbacks * must be driven in adherence to these rules: * - * - If any path is added (with add_*) or deleted/moved/rotated, then + * - If any path is added (with add_*) or deleted/moved, then * an svn_editor_alter_directory() call must be made for its parent * directory with the target/eventual set of children. * @@ -344,15 +342,13 @@ svn_delta_shim_callbacks_default(apr_pool_t *result_pool); * its children, if a directory) may be copied many times, and are * otherwise subject to the Once Rule. The destination path of a copy * or move may have alter_* operations applied, but not add_* or delete. - * If the destination path of a copy, move, or rotate is a directory, + * If the destination path of a copy or move is a directory, * then its children are subject to the Once Rule. The source path of * a move (and its child paths) may be referenced in add_*, or as the * destination of a copy (where these new or copied nodes are subject - * to the Once Rule). Paths listed in a rotation are both sources and - * destinations, so they may not be referenced again in an add_* or a - * deletion; these paths may have alter_* operations applied. + * to the Once Rule). * - * - The ancestor of an added, copied-here, moved-here, rotated, or + * - The ancestor of an added, copied-here, moved-here, or * modified node may not be deleted. The ancestor may not be moved * (instead: perform the move, *then* the edits). * @@ -375,10 +371,6 @@ svn_delta_shim_callbacks_default(apr_pool_t *result_pool); * by a delete... that is fine. It is simply that svn_editor_move() * should be used to describe a semantic move. * - * - Paths mentioned in svn_editor_rotate() may have their properties - * and contents edited (via alter_* calls) by a previous or later call, - * but they may not be subject to a later move, rotate, or deletion. - * * - One of svn_editor_complete() or svn_editor_abort() must be called * exactly once, which must be the final call the driver invokes. * Invoking svn_editor_complete() must imply that the set of changes has @@ -573,9 +565,9 @@ typedef svn_error_t *(*svn_editor_cb_alter_file_t)( void *baton, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const svn_checksum_t *checksum, svn_stream_t *contents, + apr_hash_t *props, apr_pool_t *scratch_pool); /** @see svn_editor_alter_symlink(), svn_editor_t. @@ -585,8 +577,8 @@ typedef svn_error_t *(*svn_editor_cb_alter_symlink_t)( void *baton, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const char *target, + apr_hash_t *props, apr_pool_t *scratch_pool); /** @see svn_editor_delete(), svn_editor_t. @@ -620,15 +612,6 @@ typedef svn_error_t *(*svn_editor_cb_move_t)( svn_revnum_t replaces_rev, apr_pool_t *scratch_pool); -/** @see svn_editor_rotate(), svn_editor_t. - * @since New in 1.8. - */ -typedef svn_error_t *(*svn_editor_cb_rotate_t)( - void *baton, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions, - apr_pool_t *scratch_pool); - /** @see svn_editor_complete(), svn_editor_t. * @since New in 1.8. */ @@ -790,17 +773,6 @@ svn_editor_setcb_move(svn_editor_t *editor, svn_editor_cb_move_t callback, apr_pool_t *scratch_pool); -/** Sets the #svn_editor_cb_rotate_t callback in @a editor - * to @a callback. - * @a scratch_pool is used for temporary allocations (if any). - * @see also svn_editor_setcb_many(). - * @since New in 1.8. - */ -svn_error_t * -svn_editor_setcb_rotate(svn_editor_t *editor, - svn_editor_cb_rotate_t callback, - apr_pool_t *scratch_pool); - /** Sets the #svn_editor_cb_complete_t callback in @a editor * to @a callback. * @a scratch_pool is used for temporary allocations (if any). @@ -841,7 +813,6 @@ typedef struct svn_editor_cb_many_t svn_editor_cb_delete_t cb_delete; svn_editor_cb_copy_t cb_copy; svn_editor_cb_move_t cb_move; - svn_editor_cb_rotate_t cb_rotate; svn_editor_cb_complete_t cb_complete; svn_editor_cb_abort_t cb_abort; @@ -1020,9 +991,9 @@ svn_error_t * svn_editor_alter_file(svn_editor_t *editor, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const svn_checksum_t *checksum, - svn_stream_t *contents); + svn_stream_t *contents, + apr_hash_t *props); /** Drive @a editor's #svn_editor_cb_alter_symlink_t callback. * @@ -1047,8 +1018,8 @@ svn_error_t * svn_editor_alter_symlink(svn_editor_t *editor, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, - const char *target); + const char *target, + apr_hash_t *props); /** Drive @a editor's #svn_editor_cb_delete_t callback. * @@ -1093,7 +1064,7 @@ svn_editor_copy(svn_editor_t *editor, * expect to find this node. That is, @a src_relpath at the start of * the whole edit and @a src_relpath at @a src_revision must lie within * the same node-rev (aka history-segment). This is just like the - * revisions specified to svn_editor_delete() and svn_editor_rotate(). + * revisions specified to svn_editor_delete(). * * For a description of @a replaces_rev, see svn_editor_add_file(). * @@ -1110,36 +1081,6 @@ svn_editor_move(svn_editor_t *editor, const char *dst_relpath, svn_revnum_t replaces_rev); -/** Drive @a editor's #svn_editor_cb_rotate_t callback. - * - * Perform a rotation among multiple nodes in the target tree. - * - * The @a relpaths and @a revisions arrays (pair-wise) specify nodes in the - * tree which are located at a path and expected to be at a specific - * revision. These nodes are simultaneously moved in a rotation pattern. - * For example, the node at index 0 of @a relpaths and @a revisions will - * be moved to the relpath specified at index 1 of @a relpaths. The node - * at index 1 will be moved to the location at index 2. The node at index - * N-1 will be moved to the relpath specified at index 0. - * - * The simplest form of this operation is to swap nodes A and B. One may - * think to move A to a temporary location T, then move B to A, then move - * T to B. However, this last move violations the Once Rule by moving T - * (which had already by edited by the move from A). In order to keep the - * restrictions against multiple moves of a single node, the rotation - * operation is needed for certain types of tree edits. - * - * ### what happens if one of the paths of the rotation is not "within" the - * ### receiver's set of paths? - * - * For all restrictions on driving the editor, see #svn_editor_t. - * @since New in 1.8. - */ -svn_error_t * -svn_editor_rotate(svn_editor_t *editor, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions); - /** Drive @a editor's #svn_editor_cb_complete_t callback. * * Send word that the edit has been completed successfully. diff --git a/contrib/subversion/subversion/include/private/svn_error_private.h b/contrib/subversion/subversion/include/private/svn_error_private.h index f8bd2bc51..2d35bc613 100644 --- a/contrib/subversion/subversion/include/private/svn_error_private.h +++ b/contrib/subversion/subversion/include/private/svn_error_private.h @@ -37,7 +37,7 @@ extern "C" { * Returns if @a err is a "tracing" error. */ svn_boolean_t -svn_error__is_tracing_link(svn_error_t *err); +svn_error__is_tracing_link(const svn_error_t *err); /** * Converts a zlib error to an svn_error_t. zerr is the error code, diff --git a/contrib/subversion/subversion/include/private/svn_fs_fs_private.h b/contrib/subversion/subversion/include/private/svn_fs_fs_private.h new file mode 100644 index 000000000..59aede1c7 --- /dev/null +++ b/contrib/subversion/subversion/include/private/svn_fs_fs_private.h @@ -0,0 +1,355 @@ +/** + * @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_fs_fs_private.h + * @brief Private API for tools that access FSFS internals and can't use + * the svn_fs_t API for that. + */ + + +#ifndef SVN_FS_FS_PRIVATE_H +#define SVN_FS_FS_PRIVATE_H + +#include +#include + +#include "svn_types.h" +#include "svn_error.h" +#include "svn_fs.h" +#include "svn_iter.h" +#include "svn_config.h" +#include "svn_string.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* Description of one large representation. It's content will be reused / + * overwritten when it gets replaced by an even larger representation. + */ +typedef struct svn_fs_fs__large_change_info_t +{ + /* size of the (deltified) representation */ + apr_uint64_t size; + + /* Revision of the representation. SVN_INVALID_REVNUM for unused entries. */ + svn_revnum_t revision; + + /* node path. "" for unused instances */ + svn_stringbuf_t *path; +} svn_fs_fs__large_change_info_t; + +/* Container for the largest representations found so far. The capacity + * is fixed and entries will be inserted by reusing the last one and + * reshuffling the entry pointers. + */ +typedef struct svn_fs_fs__largest_changes_t +{ + /* number of entries allocated in CHANGES */ + apr_size_t count; + + /* size of the smallest change */ + apr_uint64_t min_size; + + /* changes kept in this struct */ + svn_fs_fs__large_change_info_t **changes; +} svn_fs_fs__largest_changes_t; + +/* Information we gather per size bracket. + */ +typedef struct svn_fs_fs__histogram_line_t +{ + /* number of item that fall into this bracket */ + apr_uint64_t count; + + /* sum of values in this bracket */ + apr_uint64_t sum; +} svn_fs_fs__histogram_line_t; + +/* A histogram of 64 bit integer values. + */ +typedef struct svn_fs_fs__histogram_t +{ + /* total sum over all brackets */ + svn_fs_fs__histogram_line_t total; + + /* one bracket per binary step. + * line[i] is the 2^(i-1) <= x < 2^i bracket */ + svn_fs_fs__histogram_line_t lines[64]; +} svn_fs_fs__histogram_t; + +/* Information we collect per file ending. + */ +typedef struct svn_fs_fs__extension_info_t +{ + /* file extension, including leading "." + * "(none)" in the container for files w/o extension. */ + const char *extension; + + /* histogram of representation sizes */ + svn_fs_fs__histogram_t rep_histogram; + + /* histogram of sizes of changed files */ + svn_fs_fs__histogram_t node_histogram; +} svn_fs_fs__extension_info_t; + +/* Compression statistics we collect over a given set of representations. + */ +typedef struct svn_fs_fs__rep_pack_stats_t +{ + /* number of representations */ + apr_uint64_t count; + + /* total size after deltification (i.e. on disk size) */ + apr_uint64_t packed_size; + + /* total size after de-deltification (i.e. plain text size) */ + apr_uint64_t expanded_size; + + /* total on-disk header size */ + apr_uint64_t overhead_size; +} svn_fs_fs__rep_pack_stats_t; + +/* Statistics we collect over a given set of representations. + * We group them into shared and non-shared ("unique") reps. + */ +typedef struct svn_fs_fs__representation_stats_t +{ + /* stats over all representations */ + svn_fs_fs__rep_pack_stats_t total; + + /* stats over those representations with ref_count == 1 */ + svn_fs_fs__rep_pack_stats_t uniques; + + /* stats over those representations with ref_count > 1 */ + svn_fs_fs__rep_pack_stats_t shared; + + /* sum of all ref_counts */ + apr_uint64_t references; + + /* sum of ref_count * expanded_size, + * i.e. total plaintext content if there was no rep sharing */ + apr_uint64_t expanded_size; +} svn_fs_fs__representation_stats_t; + +/* Basic statistics we collect over a given set of noderevs. + */ +typedef struct svn_fs_fs__node_stats_t +{ + /* number of noderev structs */ + apr_uint64_t count; + + /* their total size on disk (structs only) */ + apr_uint64_t size; +} svn_fs_fs__node_stats_t; + +/* Comprises all the information needed to create the output of the + * 'svnfsfs stats' command. + */ +typedef struct svn_fs_fs__stats_t +{ + /* sum total of all rev / pack file sizes in bytes */ + apr_uint64_t total_size; + + /* number of revisions in the repository */ + apr_uint64_t revision_count; + + /* total number of changed paths */ + apr_uint64_t change_count; + + /* sum of all changed path list sizes on disk in bytes */ + apr_uint64_t change_len; + + /* stats on all representations */ + svn_fs_fs__representation_stats_t total_rep_stats; + + /* stats on all file text representations */ + svn_fs_fs__representation_stats_t file_rep_stats; + + /* stats on all directory text representations */ + svn_fs_fs__representation_stats_t dir_rep_stats; + + /* stats on all file prop representations */ + svn_fs_fs__representation_stats_t file_prop_rep_stats; + + /* stats on all directory prop representations */ + svn_fs_fs__representation_stats_t dir_prop_rep_stats; + + /* size and count summary over all noderevs */ + svn_fs_fs__node_stats_t total_node_stats; + + /* size and count summary over all file noderevs */ + svn_fs_fs__node_stats_t file_node_stats; + + /* size and count summary over all directory noderevs */ + svn_fs_fs__node_stats_t dir_node_stats; + + /* the biggest single contributors to repo size */ + svn_fs_fs__largest_changes_t *largest_changes; + + /* histogram of representation sizes */ + svn_fs_fs__histogram_t rep_size_histogram; + + /* histogram of sizes of changed nodes */ + svn_fs_fs__histogram_t node_size_histogram; + + /* histogram of representation sizes */ + svn_fs_fs__histogram_t added_rep_size_histogram; + + /* histogram of sizes of changed nodes */ + svn_fs_fs__histogram_t added_node_size_histogram; + + /* histogram of unused representations */ + svn_fs_fs__histogram_t unused_rep_histogram; + + /* histogram of sizes of changed files */ + svn_fs_fs__histogram_t file_histogram; + + /* histogram of sizes of file representations */ + svn_fs_fs__histogram_t file_rep_histogram; + + /* histogram of sizes of changed file property sets */ + svn_fs_fs__histogram_t file_prop_histogram; + + /* histogram of sizes of file property representations */ + svn_fs_fs__histogram_t file_prop_rep_histogram; + + /* histogram of sizes of changed directories (in bytes) */ + svn_fs_fs__histogram_t dir_histogram; + + /* histogram of sizes of directories representations */ + svn_fs_fs__histogram_t dir_rep_histogram; + + /* histogram of sizes of changed directories property sets */ + svn_fs_fs__histogram_t dir_prop_histogram; + + /* histogram of sizes of directories property representations */ + svn_fs_fs__histogram_t dir_prop_rep_histogram; + + /* extension -> svn_fs_fs__extension_info_t* map */ + apr_hash_t *by_extension; +} svn_fs_fs__stats_t; + + +/* Scan all contents of the repository FS and return statistics in *STATS, + * allocated in RESULT_POOL. Report progress through PROGRESS_FUNC with + * PROGRESS_BATON, if PROGRESS_FUNC is not NULL. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__get_stats(svn_fs_fs__stats_t **stats, + svn_fs_t *fs, + svn_fs_progress_notify_func_t progress_func, + void *progress_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Node-revision IDs in FSFS consist of 3 of sub-IDs ("parts") that consist + * of a creation REVISION number and some revision- / transaction-local + * counter value (NUMBER). Old-style ID parts use global counter values. + * + * The parts are: node_id, copy_id and txn_id for in-txn IDs as well as + * node_id, copy_id and rev_offset for in-revision IDs. This struct the + * data structure used for each of those parts. + */ +typedef struct svn_fs_fs__id_part_t +{ + /* SVN_INVALID_REVNUM for txns -> not a txn, COUNTER must be 0. + SVN_INVALID_REVNUM for others -> not assigned to a revision, yet. + 0 for others -> old-style ID or the root in rev 0. */ + svn_revnum_t revision; + + /* sub-id value relative to REVISION. Its interpretation depends on + the part itself. In rev_item, it is the index_index value, in others + it represents a unique counter value. */ + apr_uint64_t number; +} svn_fs_fs__id_part_t; + +/* (user visible) entry in the phys-to-log index. It describes a section + * of some packed / non-packed rev file as containing a specific item. + * There must be no overlapping / conflicting entries. + */ +typedef struct svn_fs_fs__p2l_entry_t +{ + /* offset of the first byte that belongs to the item */ + apr_off_t offset; + + /* length of the item in bytes */ + apr_off_t size; + + /* type of the item (see SVN_FS_FS__ITEM_TYPE_*) defines */ + apr_uint32_t type; + + /* modified FNV-1a checksum. 0 if unknown checksum */ + apr_uint32_t fnv1_checksum; + + /* item in that block */ + svn_fs_fs__id_part_t item; +} svn_fs_fs__p2l_entry_t; + + +/* Callback function type receiving a single P2L index ENTRY, a user + * provided BATON and a SCRATCH_POOL for temporary allocations. + * ENTRY's lifetime may end when the callback returns. + */ +typedef svn_error_t * +(*svn_fs_fs__dump_index_func_t)(const svn_fs_fs__p2l_entry_t *entry, + void *baton, + apr_pool_t *scratch_pool); + +/* Read the P2L index for the rev / pack file containing REVISION in FS. + * For each index entry, invoke CALLBACK_FUNC with CALLBACK_BATON. + * If not NULL, call CANCEL_FUNC with CANCEL_BATON from time to time. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__dump_index(svn_fs_t *fs, + svn_revnum_t revision, + svn_fs_fs__dump_index_func_t callback_func, + void *callback_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + + +/* Rewrite the respective index information of the rev / pack file in FS + * containing REVISION and use the svn_fs_fs__p2l_entry_t * array ENTRIES + * as the new index contents. Allocate temporaries from SCRATCH_POOL. + * + * Note that this becomes a no-op if ENTRIES is empty. You may use a zero- + * sized empty entry instead. + */ +svn_error_t * +svn_fs_fs__load_index(svn_fs_t *fs, + svn_revnum_t revision, + apr_array_header_t *entries, + apr_pool_t *scratch_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_FS_FS_PRIVATE_H */ diff --git a/contrib/subversion/subversion/include/private/svn_fs_private.h b/contrib/subversion/subversion/include/private/svn_fs_private.h index 20b70cd20..5bd89c028 100644 --- a/contrib/subversion/subversion/include/private/svn_fs_private.h +++ b/contrib/subversion/subversion/include/private/svn_fs_private.h @@ -179,6 +179,23 @@ svn_fs__editor_commit(svn_revnum_t *revision, apr_pool_t *scratch_pool); +/** Set @a *mergeinfo to the mergeinfo for @a path in @a root. + * + * If there is no mergeinfo, set @a *mergeinfo to NULL. + * + * See svn_fs_get_mergeinfo2() but for the meanings of @a inherit and + * @a adjust_inheritable_mergeinfo and other details. + */ +svn_error_t * +svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo, + svn_fs_root_t *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); + + /** @} */ diff --git a/contrib/subversion/subversion/include/private/svn_fs_util.h b/contrib/subversion/subversion/include/private/svn_fs_util.h index eb0f024de..c9f74a126 100644 --- a/contrib/subversion/subversion/include/private/svn_fs_util.h +++ b/contrib/subversion/subversion/include/private/svn_fs_util.h @@ -29,12 +29,17 @@ #include "svn_types.h" #include "svn_error.h" +#include "svn_version.h" #include "svn_fs.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/* Get libsvn_fs_util version information. */ +const svn_version_t * +svn_fs_util__version(void); + /* Returns whether PATH is in canonical form as defined by svn_fs__canonicalize_abspath(). */ @@ -210,6 +215,26 @@ svn_fs__append_to_merged_froms(svn_mergeinfo_t *output, const char *rel_path, apr_pool_t *pool); +/* Given the FS creation options in CONFIG, return the oldest version that + we shall be compatible with in *COMPATIBLE_VERSION. The patch level + is always set to 0 and the tag to "". Allocate the result in POOL. + + Note that the result will always be compatible to the current tool + version, i.e. will be a version number not more recent than this tool. */ +svn_error_t * +svn_fs__compatible_version(svn_version_t **compatible_version, + apr_hash_t *config, + apr_pool_t *pool); + +/* Compare the property lists A and B using POOL for temporary allocations. + Return true iff both lists contain the same properties with the same + values. A and B may be NULL in which case they will be equal to and + empty list. */ +svn_boolean_t +svn_fs__prop_lists_equal(apr_hash_t *a, + apr_hash_t *b, + apr_pool_t *pool); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/private/svn_io_private.h b/contrib/subversion/subversion/include/private/svn_io_private.h index 239cd6e61..814c27aea 100644 --- a/contrib/subversion/subversion/include/private/svn_io_private.h +++ b/contrib/subversion/subversion/include/private/svn_io_private.h @@ -44,6 +44,15 @@ extern "C" { #define SVN__APR_FINFO_MASK_OUT (0) #endif +/* 90% of the lines we encounter will be less than this many chars. + * + * Line-based functions like svn_stream_readline should fetch data in + * blocks no longer than this. Although using a larger prefetch size is + * not illegal and must not break any functionality, it may be + * significantly less efficient in certain situations. + */ +#define SVN__LINE_CHUNK_SIZE 80 + /** Set @a *executable TRUE if @a file_info is executable for the * user, FALSE otherwise. @@ -64,6 +73,18 @@ svn_io__is_finfo_read_only(svn_boolean_t *read_only, apr_pool_t *pool); +/** + * Lock file at @a lock_file. If that file does not exist, create an empty + * file. + * + * Lock will be automatically released when @a pool is cleared or destroyed. + * Use @a pool for memory allocations. + */ +svn_error_t * +svn_io__file_lock_autocreate(const char *lock_file, + apr_pool_t *pool); + + /** Buffer test handler function for a generic stream. @see svn_stream_t * and svn_stream__is_buffered(). * @@ -81,7 +102,7 @@ svn_stream__set_is_buffered(svn_stream_t *stream, /** Return whether this generic @a stream uses internal buffering. * This may be used to work around subtle differences between buffered - * an non-buffered APR files. A lazy-open stream cannot report the + * and non-buffered APR files. A lazy-open stream cannot report the * true buffering state until after the lazy open: a stream that * initially reports as non-buffered may report as buffered later. * @@ -96,6 +117,82 @@ svn_stream__is_buffered(svn_stream_t *stream); apr_file_t * svn_stream__aprfile(svn_stream_t *stream); +/* Creates as *INSTALL_STREAM a stream that once completed can be installed + using Windows checkouts much slower than Unix. + + While writing the stream is temporarily stored in TMP_ABSPATH. + */ +svn_error_t * +svn_stream__create_for_install(svn_stream_t **install_stream, + const char *tmp_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Installs a stream created with svn_stream__create_for_install in its final + location FINAL_ABSPATH, potentially using platform specific optimizations. + + If MAKE_PARENTS is TRUE, this function will create missing parent + directories if needed. + */ +svn_error_t * +svn_stream__install_stream(svn_stream_t *install_stream, + const char *final_abspath, + svn_boolean_t make_parents, + apr_pool_t *scratch_pool); + +/* Deletes the install stream (when installing is not necessary after all) */ +svn_error_t * +svn_stream__install_delete(svn_stream_t *install_stream, + apr_pool_t *scratch_pool); + +/* Optimized apr_file_stat / apr_file_info_get operating on a closed + install stream */ +svn_error_t * +svn_stream__install_get_info(apr_finfo_t *finfo, + svn_stream_t *install_stream, + apr_int32_t wanted, + apr_pool_t *scratch_pool); + + +#if defined(WIN32) + +/* ### Move to something like io.h or subr.h, to avoid making it + part of the DLL api */ + +/* This is semantically the same as the APR utf8_to_unicode_path + function, but reimplemented here because APR does not export it. + + Note that this function creates "\\?\" paths so the resulting path + can only be used for WINAPI functions that explicitly document support + for this kind of paths. Newer Windows functions (Vista+) that support + long paths directly DON'T want this kind of escaping. + */ +svn_error_t* +svn_io__utf8_to_unicode_longpath(const WCHAR **result, + const char *source, + apr_pool_t *result_pool); + +/* This Windows-specific function marks the file to be deleted on close using + an existing file handle. It can be used to avoid having to reopen the file + as part of the delete handling. Return SVN_ERR_UNSUPPORTED_FEATURE if + delete on close operation is not supported by OS. */ +svn_error_t * +svn_io__win_delete_file_on_close(apr_file_t *file, + const char *path, + apr_pool_t *pool); + +/* This Windows-specific function renames the file using an existing file + handle. It can be used to avoid having to reopen the file as part of the + rename operation. Return SVN_ERR_UNSUPPORTED_FEATURE if renaming open + file is not supported by OS.*/ +svn_error_t * +svn_io__win_rename_open_file(apr_file_t *file, + const char *from_path, + const char *to_path, + apr_pool_t *pool); + +#endif /* WIN32 */ + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/private/svn_log.h b/contrib/subversion/subversion/include/private/svn_log.h index bdcf32f5a..1ad8d559b 100644 --- a/contrib/subversion/subversion/include/private/svn_log.h +++ b/contrib/subversion/subversion/include/private/svn_log.h @@ -204,7 +204,7 @@ svn_log__get_file_revs(const char *path, svn_revnum_t start, svn_revnum_t end, * @since New in 1.6. */ const char * -svn_log__lock(const apr_array_header_t *paths, svn_boolean_t steal, +svn_log__lock(apr_hash_t *targets, svn_boolean_t steal, apr_pool_t *pool); /** @@ -213,7 +213,7 @@ svn_log__lock(const apr_array_header_t *paths, svn_boolean_t steal, * @since New in 1.6. */ const char * -svn_log__unlock(const apr_array_header_t *paths, svn_boolean_t break_lock, +svn_log__unlock(apr_hash_t *targets, svn_boolean_t break_lock, apr_pool_t *pool); /** diff --git a/contrib/subversion/subversion/include/private/svn_magic.h b/contrib/subversion/subversion/include/private/svn_magic.h index b057e563b..2e5fafd60 100644 --- a/contrib/subversion/subversion/include/private/svn_magic.h +++ b/contrib/subversion/subversion/include/private/svn_magic.h @@ -30,14 +30,16 @@ /* An opaque struct that wraps a libmagic cookie. */ typedef struct svn_magic__cookie_t svn_magic__cookie_t; -/* This routine initialises libmagic. +/* This routine initialises libmagic. CONFIG is a config hash and + * may be NULL. * Upon success a new *MAGIC_COOKIE is allocated in RESULT_POOL. * On failure *MAGIC_COOKIE is set to NULL. * All resources used by libmagic are freed by a cleanup handler * installed on RESULT_POOL, i.e. *MAGIC_COOKIE becomes invalid when * the pool is cleared! */ -void +svn_error_t * svn_magic__init(svn_magic__cookie_t **magic_cookie, + apr_hash_t *config, apr_pool_t *result_pool); /* Detect the mime-type of the file at LOCAL_ABSPATH using MAGIC_COOKIE. diff --git a/contrib/subversion/subversion/include/private/svn_mergeinfo_private.h b/contrib/subversion/subversion/include/private/svn_mergeinfo_private.h index b8748f4dc..716d0c9f9 100644 --- a/contrib/subversion/subversion/include/private/svn_mergeinfo_private.h +++ b/contrib/subversion/subversion/include/private/svn_mergeinfo_private.h @@ -119,13 +119,13 @@ svn_mergeinfo__equals(svn_boolean_t *is_equal, 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 +/* Remove all paths from MERGEINFO which map to empty rangelists. + + Return 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); + apr_pool_t *scratch_pool); /* Make a shallow (ie, mergeinfos are not duped, or altered at all; keys share storage) copy of IN_CATALOG in *OUT_CATALOG, removing diff --git a/contrib/subversion/subversion/include/private/svn_mutex.h b/contrib/subversion/subversion/include/private/svn_mutex.h index c64769717..c04820bac 100644 --- a/contrib/subversion/subversion/include/private/svn_mutex.h +++ b/contrib/subversion/subversion/include/private/svn_mutex.h @@ -39,27 +39,23 @@ extern "C" { * This is a simple wrapper around @c apr_thread_mutex_t and will be a * valid identifier even if APR does not support threading. */ -#if APR_HAS_THREADS /** A mutex for synchronization between threads. It may be NULL, in * which case no synchronization will take place. The latter is useful * when implementing some functionality with optional synchronization. */ -typedef apr_thread_mutex_t svn_mutex__t; - -#else - -/** Dummy definition. The content will never be actually accessed. - */ -typedef void svn_mutex__t; - -#endif +typedef struct svn_mutex__t svn_mutex__t; /** Initialize the @a *mutex. If @a mutex_required is TRUE, the mutex will * actually be created with a lifetime defined by @a result_pool. Otherwise, * the pointer will be set to @c NULL and svn_mutex__lock() as well as * svn_mutex__unlock() will be no-ops. * + * We don't support recursive locks, i.e. a thread may not acquire the same + * mutex twice without releasing it in between. Attempts to lock a mutex + * recursively will cause lock ups and other undefined behavior on some + * systems. + * * If threading is not supported by APR, this function is a no-op. */ svn_error_t * diff --git a/contrib/subversion/subversion/include/private/svn_named_atomic.h b/contrib/subversion/subversion/include/private/svn_named_atomic.h deleted file mode 100644 index 4efa255e7..000000000 --- a/contrib/subversion/subversion/include/private/svn_named_atomic.h +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @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_named_atomics.h - * @brief Structures and functions for machine-wide named atomics. - * These atomics store 64 bit signed integer values and provide - * a number of basic operations on them. Instead of an address, - * these atomics are identified by strings / names. We also support - * namespaces - mainly to separate debug from production data. - */ - -#ifndef SVN_NAMED_ATOMICS_H -#define SVN_NAMED_ATOMICS_H - -#include "svn_error.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** An opaque structure that represents a namespace, i.e. a container - * for named atomics. - */ -typedef struct svn_atomic_namespace__t svn_atomic_namespace__t; - -/** An opaque structure that represents a named, system-wide visible - * 64 bit integer with atomic access routines. - */ -typedef struct svn_named_atomic__t svn_named_atomic__t; - -/** Maximum length of the name of any atomic (excluding the terminal NUL). - */ -#define SVN_NAMED_ATOMIC__MAX_NAME_LENGTH 30 - -/** Returns #FALSE when named atomics are not available to our process - * and svn_atomic_namespace__create is likely to fail. - * - * @note The actual check will be performed only once and later - * changes in process privileges will not reflect in the outcome of future - * calls to this function. - */ -svn_boolean_t -svn_named_atomic__is_supported(void); - -/** Returns #TRUE on platforms that don't need expensive synchronization - * objects to serialize access to named atomics. If this returns #FALSE, - * reading from or modifying a #svn_named_atomic__t may be as expensive - * as a file system operation. - */ -svn_boolean_t -svn_named_atomic__is_efficient(void); - -/** Create a namespace (i.e. access object) with the given @a name and - * return it in @a *ns. - * - * Multiple access objects with the same name may be created. They access - * the same shared memory region but have independent lifetimes. - * - * The access object will be allocated in @a result_pool and atomics gotten - * from this object will become invalid when the pool is being cleared. - */ -svn_error_t * -svn_atomic_namespace__create(svn_atomic_namespace__t **ns, - const char *name, - apr_pool_t *result_pool); - -/** Removes persistent data structures (files in particular) that got - * created for the namespace given by @a name. Use @a pool for temporary - * allocations. - * - * @note You must not call this while the respective namespace is still - * in use. Calling this multiple times for the same namespace is safe. - */ -svn_error_t * -svn_atomic_namespace__cleanup(const char *name, - apr_pool_t *pool); - -/** Find the atomic with the specified @a name in namespace @a ns and - * return it in @a *atomic. If no object with that name can be found, the - * behavior depends on @a auto_create. If it is @c FALSE, @a *atomic will - * be set to @c NULL. Otherwise, a new atomic will be created, its value - * set to 0 and the access structure be returned in @a *atomic. - * - * Note that @a name must not exceed #SVN_NAMED_ATOMIC__MAX_NAME_LENGTH - * characters and an error will be returned if the specified name is longer - * than supported. - * - * @note The lifetime of the atomic object is bound to the lifetime - * of the @a ns object, i.e. the pool the latter was created in. - * The data in the namespace persists as long as at least one process - * holds an #svn_atomic_namespace__t object corresponding to it. - */ -svn_error_t * -svn_named_atomic__get(svn_named_atomic__t **atomic, - svn_atomic_namespace__t *ns, - const char *name, - svn_boolean_t auto_create); - -/** Read the @a atomic and return its current @a *value. - * An error will be returned if @a atomic is @c NULL. - */ -svn_error_t * -svn_named_atomic__read(apr_int64_t *value, - svn_named_atomic__t *atomic); - -/** Set the data in @a atomic to @a new_value and return its old content - * in @a *old_value. @a old_value may be NULL. - * - * An error will be returned if @a atomic is @c NULL. - */ -svn_error_t * -svn_named_atomic__write(apr_int64_t *old_value, - apr_int64_t new_value, - svn_named_atomic__t *atomic); - -/** Add @a delta to the data in @a atomic and return its new value in - * @a *new_value. @a new_value may be null. - * - * An error will be returned if @a atomic is @c NULL. - */ -svn_error_t * -svn_named_atomic__add(apr_int64_t *new_value, - apr_int64_t delta, - svn_named_atomic__t *atomic); - -/** If the current data in @a atomic equals @a comperand, set it to - * @a new_value. Return the initial value in @a *old_value. - * @a old_value may be NULL. - * - * An error will be returned if @a atomic is @c NULL. - */ -svn_error_t * -svn_named_atomic__cmpxchg(apr_int64_t *old_value, - apr_int64_t new_value, - apr_int64_t comperand, - svn_named_atomic__t *atomic); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SVN_NAMED_ATOMICS_H */ diff --git a/contrib/subversion/subversion/include/private/svn_object_pool.h b/contrib/subversion/subversion/include/private/svn_object_pool.h new file mode 100644 index 000000000..7a9383e5f --- /dev/null +++ b/contrib/subversion/subversion/include/private/svn_object_pool.h @@ -0,0 +1,154 @@ +/** + * @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_object_pool.h + * @brief multithreaded object pool API + * + * This is the core data structure behind various object pools. It + * provides a thread-safe associative container for object instances of + * the same type. + * + * Memory and lifetime management for the objects are handled by the pool. + * Reference counting takes care that neither objects nor the object pool + * get actually destroyed while other parts depend on them. All objects + * are thought to be recycle-able and live in their own root memory pools + * making them (potentially) safe to be used from different threads. + * Currently unused objects may be kept around for a while and returned + * by the next lookup. + * + * Two modes are supported: shared use and exclusive use. In shared mode, + * any object can be handed out to multiple users and in potentially + * different threads at the same time. In exclusive mode, the same object + * will only be referenced at most once. + * + * Object creation and access must be provided outside this structure. + * In particular, the using container will usually wrap the actual object + * in a meta-data struct containing key information etc and must provide + * getters and setters for those wrapper structs. + */ + + + +#ifndef SVN_OBJECT_POOL_H +#define SVN_OBJECT_POOL_H + +#include /* for apr_int64_t */ +#include /* for apr_pool_t */ +#include /* for apr_hash_t */ + +#include "svn_types.h" + +#include "private/svn_mutex.h" +#include "private/svn_string_private.h" + + + +/* The opaque object container type. */ +typedef struct svn_object_pool__t svn_object_pool__t; + +/* Extract the actual object from the WRAPPER using optional information + * from BATON (provided through #svn_object_pool__lookup) and return it. + * The result will be used with POOL and must remain valid throughout + * POOL's lifetime. + * + * It is legal to return a copy, allocated in POOL, of the wrapped object. + */ +typedef void * (* svn_object_pool__getter_t)(void *wrapper, + void *baton, + apr_pool_t *pool); + +/* Copy the information from the SOURCE object wrapper into the already + * existing *TARGET object wrapper using POOL for allocations and BATON + * for optional context (provided through #svn_object_pool__insert). + */ +typedef svn_error_t * (* svn_object_pool__setter_t)(void **target, + void *source, + void *baton, + apr_pool_t *pool); + +/* Create a new object pool in POOL and return it in *OBJECT_POOL. + * Objects will be extracted using GETTER and updated using SETTER. Either + * one (or both) may be NULL and the default implementation assumes that + * wrapper == object and updating is a no-op. + * + * If THREAD_SAFE is not set, neither the object pool nor the object + * references returned from it may be accessed from multiple threads. + * + * It is not legal to call any API on the object pool after POOL got + * cleared or destroyed. However, existing object references handed out + * from the object pool remain valid and will keep the internal pool data + * structures alive for as long as such object references exist. + */ +svn_error_t * +svn_object_pool__create(svn_object_pool__t **object_pool, + svn_object_pool__getter_t getter, + svn_object_pool__setter_t setter, + svn_boolean_t thread_safe, + apr_pool_t *pool); + +/* Return the root pool containing the OBJECT_POOL and all sub-structures. + */ +apr_pool_t * +svn_object_pool__new_wrapper_pool(svn_object_pool__t *object_pool); + +/* Return the mutex used to serialize all OBJECT_POOL access. + */ +svn_mutex__t * +svn_object_pool__mutex(svn_object_pool__t *object_pool); + +/* Return the number of object instances (used or unused) in OBJECT_POOL. + */ +unsigned +svn_object_pool__count(svn_object_pool__t *object_pool); + +/* In OBJECT_POOL, look for an available object by KEY and return a + * reference to it in *OBJECT. If none can be found, *OBJECT will be NULL. + * BATON will be passed to OBJECT_POOL's getter function. The reference + * will be returned when *RESULT_POOL gets cleaned up or destroyed. + */ +svn_error_t * +svn_object_pool__lookup(void **object, + svn_object_pool__t *object_pool, + svn_membuf_t *key, + void *baton, + apr_pool_t *result_pool); + +/* Store the wrapped object WRAPPER under KEY in OBJECT_POOL and return + * a reference to the object in *OBJECT (just like lookup). + * + * The object must have been created in WRAPPER_POOL and the latter must + * be a sub-pool of OBJECT_POOL's root POOL (see #svn_object_pool__pool). + * + * BATON will be passed to OBJECT_POOL's setter and getter functions. + * The reference will be returned when *RESULT_POOL gets cleaned up or + * destroyed. + */ +svn_error_t * +svn_object_pool__insert(void **object, + svn_object_pool__t *object_pool, + const svn_membuf_t *key, + void *wrapper, + void *baton, + apr_pool_t *wrapper_pool, + apr_pool_t *result_pool); + +#endif /* SVN_OBJECT_POOL_H */ diff --git a/contrib/subversion/subversion/include/private/svn_opt_private.h b/contrib/subversion/subversion/include/private/svn_opt_private.h index 6ae67a5bf..89e722760 100644 --- a/contrib/subversion/subversion/include/private/svn_opt_private.h +++ b/contrib/subversion/subversion/include/private/svn_opt_private.h @@ -46,10 +46,6 @@ extern "C" { * UTF8_TARGET need not be canonical. *TRUE_TARGET will not be canonical * unless UTF8_TARGET is. * - * It is an error if *TRUE_TARGET results in the empty string after the - * split, which happens in case UTF8_TARGET has a leading '@' character - * with no additional '@' characters to escape the first '@'. - * * Note that *PEG_REVISION will still contain the '@' symbol as the first * character if a peg revision was found. If a trailing '@' symbol was * used to escape other '@' characters in UTF8_TARGET, *PEG_REVISION will diff --git a/contrib/subversion/subversion/include/private/svn_packed_data.h b/contrib/subversion/subversion/include/private/svn_packed_data.h new file mode 100644 index 000000000..6faf0dd0e --- /dev/null +++ b/contrib/subversion/subversion/include/private/svn_packed_data.h @@ -0,0 +1,255 @@ +/* packed_data.h : Interface to the packed binary stream data structure + * + * ==================================================================== + * 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_PACKED_DATA_H +#define SVN_PACKED_DATA_H + +#include "svn_string.h" +#include "svn_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* This API provides Yet Another Serialization Framework. + * + * It is geared towards efficiently encoding collections of structured + * binary data (e.g. an array of noderev objects). The basic idea is to + * transform them into hierarchies of streams with each stream usually + * corresponding to a single attribute in the original data structures. + * The user is free model the mapping structure <-> streams mapping as she + * sees fit. + * + * With all data inside the same (sub-)stream carrying similar attribute + * values, the whole stream lends itself to data compression. Strings / + * plain byte sequences will be stored as is. Numbers use a 7b/8b encoding + * scheme to eliminate leading zeros. Because values are often dependent + * (increasing offsets, roughly similar revision number, etc.), streams + * can be configured as storing (hopefully shorter) deltas instead of the + * original value. + * + * Two stream types are provided: integer and byte streams. While the + * first store 64 bit integers only and can be configured to assume + * signed and / or deltifyable data, the second will store arbitrary + * byte sequences including their length. At the root level, you may + * create an arbitrary number of integer and byte streams. Any stream + * may have an arbitrary number of sub-streams of the same kind. You + * should create the full stream hierarchy before writing any data to it. + * + * As a convenience, when an integer stream has sub-streams, you may write + * to the parent stream instead of all sub-streams individually and the + * values will be passed down automatically in a round-robin fashion. + * Reading from the parent stream is similarly supported. + * + * When all data has been added to the stream, it can be written to an + * ordinary svn_stream_t. First, we write a description of the stream + * structure (types, sub-streams, sizes and configurations) followed by + * zlib compressed stream content. For each top-level stream, all sub- + * stream data will be concatenated and then compressed as a single block. + * To maximize the effect of this, make sure all data in that stream + * hierarchy has a similar value distribution. + * + * Reading data starts with an svn_stream_t and automatically recreates + * the stream hierarchies. You only need to extract data from it in the + * same order as you wrote it. + * + * Although not enforced programmatically, you may either only write to a + * stream hierarchy or only read from it but you cannot do both on the + * same data structure. + */ + + + +/* We pack / unpack integers en block to minimize calling and setup overhead. + * This is the number of integers we put into a buffer before writing them + * them to / after reading them from the 7b/8b stream. Under 64 bits, this + * value creates a 128 byte data structure (14 + 2 integers, 8 bytes each). + */ +#define SVN__PACKED_DATA_BUFFER_SIZE 14 + + +/* Data types. */ + +/* Opaque type for the root object. + */ +typedef struct svn_packed__data_root_t svn_packed__data_root_t; + +/* Opaque type for byte streams. + */ +typedef struct svn_packed__byte_stream_t svn_packed__byte_stream_t; + +/* Semi-opaque type for integer streams. We expose the unpacked buffer + * to allow for replacing svn_packed__add_uint and friends by macros. + */ +typedef struct svn_packed__int_stream_t +{ + /* pointer to the remainder of the data structure */ + void *private_data; + + /* number of value entries in BUFFER */ + apr_size_t buffer_used; + + /* unpacked integers (either yet to be packed or pre-fetched from the + * packed buffers). Only the first BUFFER_USED entries are valid. */ + apr_uint64_t buffer[SVN__PACKED_DATA_BUFFER_SIZE]; +} svn_packed__int_stream_t; + + +/* Writing data. */ + +/* Return a new serialization root object, allocated in POOL. + */ +svn_packed__data_root_t * +svn_packed__data_create_root(apr_pool_t *pool); + +/* Create and return a new top-level integer stream in ROOT. If signed, + * negative numbers will be put into that stream, SIGNED_INTS should be + * TRUE as a more efficient encoding will be used in that case. Set + * DIFF to TRUE if you expect the difference between consecutive numbers + * to be much smaller (~100 times) than the actual numbers. + */ +svn_packed__int_stream_t * +svn_packed__create_int_stream(svn_packed__data_root_t *root, + svn_boolean_t diff, + svn_boolean_t signed_ints); + +/* Create and return a sub-stream to the existing integer stream PARENT. + * If signed, negative numbers will be put into that stream, SIGNED_INTS + * should be TRUE as a more efficient encoding will be used in that case. + * Set DIFF to TRUE if you expect the difference between consecutive numbers + * to be much smaller (~100 times) than the actual numbers. + */ +svn_packed__int_stream_t * +svn_packed__create_int_substream(svn_packed__int_stream_t *parent, + svn_boolean_t diff, + svn_boolean_t signed_ints); + +/* Create and return a new top-level byte sequence stream in ROOT. + */ +svn_packed__byte_stream_t * +svn_packed__create_bytes_stream(svn_packed__data_root_t *root); + +/* Write the unsigned integer VALUE to STEAM. + */ +void +svn_packed__add_uint(svn_packed__int_stream_t *stream, + apr_uint64_t value); + +/* Write the signed integer VALUE to STEAM. + */ +void +svn_packed__add_int(svn_packed__int_stream_t *stream, + apr_int64_t value); + +/* Write the sequence stating at DATA containing LEN bytes to STEAM. + */ +void +svn_packed__add_bytes(svn_packed__byte_stream_t *stream, + const char *data, + apr_size_t len); + +/* Write all contents of ROOT (including all sub-streams) to STREAM. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_packed__data_write(svn_stream_t *stream, + svn_packed__data_root_t *root, + apr_pool_t *scratch_pool); + + +/* Reading data. */ + +/* Return the first integer stream in ROOT. Returns NULL in case there + * aren't any. + */ +svn_packed__int_stream_t * +svn_packed__first_int_stream(svn_packed__data_root_t *root); + +/* Return the first byte sequence stream in ROOT. Returns NULL in case + * there aren't any. + */ +svn_packed__byte_stream_t * +svn_packed__first_byte_stream(svn_packed__data_root_t *root); + +/* Return the next (sibling) integer stream to STREAM. Returns NULL in + * case there isn't any. + */ +svn_packed__int_stream_t * +svn_packed__next_int_stream(svn_packed__int_stream_t *stream); + +/* Return the next (sibling) byte sequence stream to STREAM. Returns NULL + * in case there isn't any. + */ +svn_packed__byte_stream_t * +svn_packed__next_byte_stream(svn_packed__byte_stream_t *stream); + +/* Return the first sub-stream of STREAM. Returns NULL in case there + * isn't any. + */ +svn_packed__int_stream_t * +svn_packed__first_int_substream(svn_packed__int_stream_t *stream); + +/* Return the number of integers left to read from STREAM. + */ +apr_size_t +svn_packed__int_count(svn_packed__int_stream_t *stream); + +/* Return the number of bytes left to read from STREAM. + */ +apr_size_t +svn_packed__byte_count(svn_packed__byte_stream_t *stream); + +/* Return the next number from STREAM as unsigned integer. Returns 0 when + * reading beyond the end of the stream. + */ +apr_uint64_t +svn_packed__get_uint(svn_packed__int_stream_t *stream); + +/* Return the next number from STREAM as signed integer. Returns 0 when + * reading beyond the end of the stream. + */ +apr_int64_t +svn_packed__get_int(svn_packed__int_stream_t *stream); + +/* Return the next byte sequence from STREAM and set *LEN to the length + * of that sequence. Sets *LEN to 0 when reading beyond the end of the + * stream. + */ +const char * +svn_packed__get_bytes(svn_packed__byte_stream_t *stream, + apr_size_t *len); + +/* Allocate a new packed data root in RESULT_POOL, read its structure and + * stream contents from STREAM and return it in *ROOT_P. Use SCRATCH_POOL + * for temporary allocations. + */ +svn_error_t * +svn_packed__data_read(svn_packed__data_root_t **root_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_PACKED_DATA_H */ diff --git a/contrib/subversion/subversion/include/private/svn_pseudo_md5.h b/contrib/subversion/subversion/include/private/svn_pseudo_md5.h deleted file mode 100644 index 34d59296e..000000000 --- a/contrib/subversion/subversion/include/private/svn_pseudo_md5.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @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_pseudo_md5.h - * @brief Subversion hash sum calculation for runtime data (only) - */ - -#ifndef SVN_PSEUDO_MD5_H -#define SVN_PSEUDO_MD5_H - -#include /* for apr_uint32_t */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -/** - * Calculates a hash sum for 15 bytes in @a x and returns it in @a digest. - * The most significant byte in @a x must be 0 (independent of being on a - * little or big endian machine). - * - * @note Use for runtime data hashing only. - * - * @note The output is NOT an MD5 digest shares has the same basic - * cryptographic properties. Collisions with proper MD5 on the same - * or other input data is equally unlikely as any MD5 collision. - */ -void svn__pseudo_md5_15(apr_uint32_t digest[4], - const apr_uint32_t x[4]); - -/** - * Calculates a hash sum for 31 bytes in @a x and returns it in @a digest. - * The most significant byte in @a x must be 0 (independent of being on a - * little or big endian machine). - * - * @note Use for runtime data hashing only. - * - * @note The output is NOT an MD5 digest shares has the same basic - * cryptographic properties. Collisions with proper MD5 on the same - * or other input data is equally unlikely as any MD5 collision. - */ -void svn__pseudo_md5_31(apr_uint32_t digest[4], - const apr_uint32_t x[8]); - -/** - * Calculates a hash sum for 63 bytes in @a x and returns it in @a digest. - * The most significant byte in @a x must be 0 (independent of being on a - * little or big endian machine). - * - * @note Use for runtime data hashing only. - * - * @note The output is NOT an MD5 digest shares has the same basic - * cryptographic properties. Collisions with proper MD5 on the same - * or other input data is equally unlikely as any MD5 collision. - */ -void svn__pseudo_md5_63(apr_uint32_t digest[4], - const apr_uint32_t x[16]); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SVN_PSEUDO_MD5_H */ diff --git a/contrib/subversion/subversion/include/private/svn_ra_private.h b/contrib/subversion/subversion/include/private/svn_ra_private.h index accca3d4f..1afa07148 100644 --- a/contrib/subversion/subversion/include/private/svn_ra_private.h +++ b/contrib/subversion/subversion/include/private/svn_ra_private.h @@ -39,6 +39,33 @@ extern "C" { #endif /* __cplusplus */ + + +/** + * Open a new ra session @a *new_session to the same repository as an existing + * ra session @a old_session, copying the callbacks, auth baton, etc. from the + * old session. This essentially limits the lifetime of the new, duplicated + * session to the lifetime of the old session. If the new session should + * outlive the new session, creating a new session using svn_ra_open4() is + * recommended. + * + * If @a session_url is not NULL, parent the new session at session_url. Note + * that @a session_url MUST BE in the same repository as @a old_session or an + * error will be returned. When @a session_url NULL the same session root + * will be used. + * + * Allocate @a new_session in @a result_pool. Perform temporary allocations + * in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_ra__dup_session(svn_ra_session_t **new_session, + svn_ra_session_t *old_session, + const char *session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Equivalent to svn_ra__assert_capable_server() for SVN_RA_CAPABILITY_MERGEINFO. */ svn_error_t * diff --git a/contrib/subversion/subversion/include/private/svn_ra_svn_private.h b/contrib/subversion/subversion/include/private/svn_ra_svn_private.h index b4294d048..bc2fa4533 100644 --- a/contrib/subversion/subversion/include/private/svn_ra_svn_private.h +++ b/contrib/subversion/subversion/include/private/svn_ra_svn_private.h @@ -42,6 +42,12 @@ svn_error_t * svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn, svn_delta_shim_callbacks_t *shim_callbacks); +/** + * Return the memory pool used to allocate @a conn. + */ +apr_pool_t * +svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn); + /** * @defgroup ra_svn_deprecated ra_svn low-level functions * @{ @@ -83,6 +89,15 @@ svn_ra_svn__write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *word); +/** Write a boolean over the net. + * + * Writes will be buffered until the next read or flush. + */ +svn_error_t * +svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_boolean_t value); + /** Write a list of properties over the net. @a props is allowed to be NULL, * in which case an empty list will be written out. * @@ -186,6 +201,7 @@ svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, w const char ** Word b svn_boolean_t * Word ("true" or "false") B apr_uint64_t * Word ("true" or "false") + 3 svn_tristate_t * Word ("true" or "false") l apr_array_header_t ** List ( Begin tuple ) End tuple @@ -196,14 +212,18 @@ svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, * the end of the specification. So if @a fmt is "c?cc" and @a list * contains two elements, an error will result. * - * 'B' is similar to 'b', but may be used in the optional tuple specification. - * It returns TRUE, FALSE, or SVN_RA_SVN_UNSPECIFIED_NUMBER. + * '3' is similar to 'b', but may be used in the optional tuple specification. + * It returns #svn_tristate_true, #svn_tristate_false or #svn_tristate_unknown. + * + * 'B' is similar to '3', but it returns @c TRUE, @c FALSE, or + * #SVN_RA_SVN_UNSPECIFIED_NUMBER. 'B' is deprecated; new code should + * use '3' instead. * * If an optional part of a tuple contains no data, 'r' values will be - * set to @c SVN_INVALID_REVNUM, 'n' and 'B' values will be set to - * SVN_RA_SVN_UNSPECIFIED_NUMBER, and 's', 'c', 'w', and 'l' values - * will be set to @c NULL. 'b' may not appear inside an optional - * tuple specification; use 'B' instead. + * set to @c SVN_INVALID_REVNUM; 'n' and 'B' values will be set to + * #SVN_RA_SVN_UNSPECIFIED_NUMBER; 's', 'c', 'w', and 'l' values + * will be set to @c NULL; and '3' values will be set to #svn_tristate_unknown + * 'b' may not appear inside an optional tuple specification; use '3' instead. */ svn_error_t * svn_ra_svn__parse_tuple(const apr_array_header_t *list, @@ -236,6 +256,33 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *fmt, ...); +/** Check the receive buffer and socket of @a conn whether there is some + * unprocessed incoming data without waiting for new data to come in. + * If data is found, set @a *has_command to TRUE. If the connection does + * not contain any more data and has been closed, set @a *terminated to + * TRUE. + */ +svn_error_t * +svn_ra_svn__has_command(svn_boolean_t *has_command, + svn_boolean_t *terminated, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool); + +/** Accept a single command from @a conn and handle them according + * to @a cmd_hash. Command handlers will be passed @a conn, @a pool, + * the parameters of the command, and @a baton. @a *terminate will be + * set if either @a error_on_disconnect is FALSE and the connection got + * closed, or if the command being handled has the "terminate" flag set + * in the command table. + */ +svn_error_t * +svn_ra_svn__handle_command(svn_boolean_t *terminate, + apr_hash_t *cmd_hash, + void *baton, + svn_ra_svn_conn_t *conn, + svn_boolean_t error_on_disconnect, + apr_pool_t *pool); + /** Accept commands over the network and handle them according to @a * commands. Command handlers will be passed @a conn, a subpool of @a * pool (cleared after each command is handled), the parameters of the @@ -267,11 +314,13 @@ svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *fmt, ...); -/** Write an unsuccessful command response over the network. */ +/** Write an unsuccessful command response over the network. + * + * @note This does not clear @a err. */ svn_error_t * svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - svn_error_t *err); + const svn_error_t *err); /** * @} @@ -563,7 +612,11 @@ svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, /** Send a "change-rev-prop2" command over connection @a conn. * Use @a pool for allocations. * - * @see #svn_ra_change_rev_prop2 for a description. + * If @a dont_care is false then check that the old value matches + * @a old_value. If @a dont_care is true then do not check the old + * value; in this case @a old_value must be NULL. + * + * @see #svn_ra_change_rev_prop2 for the rest of the description. */ svn_error_t * svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, @@ -819,6 +872,77 @@ svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, /** * @} */ + +/** + * @defgroup svn_send_data sending data structures over ra_svn + * @{ + */ + +/** Send a changed path (as part of transmitting a log entry) over connection + * @a conn. Use @a pool for allocations. + * + * @see svn_log_changed_path2_t for a description of the other parameters. + */ +svn_error_t * +svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *path, + char action, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev, + svn_node_kind_t node_kind, + svn_boolean_t text_modified, + svn_boolean_t props_modified); + +/** Send a the details of a log entry (as part of transmitting a log entry + * and without revprops and changed paths) over connection @a conn. + * Use @a pool for allocations. + * + * @a author, @a date and @a message have been extracted and removed from + * the revprops to follow. @a has_children is taken directly from the + * #svn_log_entry_t struct. @a revision is too, except when it equals + * #SVN_INVALID_REVNUM. In that case, @a revision must be 0 and + * @a invalid_revnum be set to TRUE. @a revprop_count is the number of + * revprops that will follow in the revprops list. + */ +svn_error_t * +svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_revnum_t revision, + const svn_string_t *author, + const svn_string_t *date, + const svn_string_t *message, + svn_boolean_t has_children, + svn_boolean_t invalid_revnum, + unsigned revprop_count); + +/** + * @} + */ + +/** + * @defgroup svn_read_data reading data structures from ra_svn + * @{ + */ + +/** Take the data tuple ITEMS received over ra_svn and convert it to the + * a changed path (as part of receiving a log entry). + * + * @see svn_log_changed_path2_t for a description of the output parameters. + */ +svn_error_t * +svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, + svn_string_t **cpath, + const char **action, + const char **copy_path, + svn_revnum_t *copy_rev, + const char **kind_str, + apr_uint64_t *text_mods, + apr_uint64_t *prop_mods); +/** + * @} + */ + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/private/svn_repos_private.h b/contrib/subversion/subversion/include/private/svn_repos_private.h index 09e4037e7..c5a232f29 100644 --- a/contrib/subversion/subversion/include/private/svn_repos_private.h +++ b/contrib/subversion/subversion/include/private/svn_repos_private.h @@ -32,16 +32,30 @@ #include "svn_types.h" #include "svn_repos.h" #include "svn_editor.h" +#include "svn_config.h" + +#include "private/svn_string_private.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. +/** Validate that property @a name with @a value is valid (as an addition + * or edit or deletion) in a Subversion repository. Return an error if not. + * + * If @a value is NULL, return #SVN_NO_ERROR to indicate that any property + * may be deleted, even an invalid one. Otherwise, if the @a name is not + * of kind #svn_prop_regular_kind (see #svn_prop_kind_t), return + * #SVN_ERR_REPOS_BAD_ARGS. Otherwise, for some "svn:" properties, also + * perform some validations on the @a value (e.g., for such properties, + * typically the @a value must be in UTF-8 with LF linefeeds), and return + * #SVN_ERR_BAD_PROPERTY_VALUE if it is not valid. + * + * Validations may be added in future releases, for example, for + * newly-added #SVN_PROP_PREFIX properties. However, user-defined + * (non-#SVN_PROP_PREFIX) properties will never have their @a value + * validated in any way. * * Use @a pool for temporary allocations. * @@ -113,10 +127,260 @@ svn_repos__replay_ev2(svn_fs_root_t *root, 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)) +/* Given a PATH which might be a relative repo URL (^/), an absolute + * local repo URL (file://), an absolute path outside of the repo + * or a location in the Windows registry. + * + * Retrieve the configuration data that PATH points at and parse it into + * CFG_P allocated in POOL. + * + * If PATH cannot be parsed as a config file then an error is returned. The + * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing + * authz file is also an error. The CASE_SENSITIVE controls the lookup + * behavior for section and option names alike. + * + * REPOS_ROOT points at the root of the repos you are + * going to apply the authz against, can be NULL if you are sure that you + * don't have a repos relative URL in PATH. */ +svn_error_t * +svn_repos__retrieve_config(svn_config_t **cfg_p, + const char *path, + svn_boolean_t must_exist, + svn_boolean_t case_sensitive, + apr_pool_t *pool); + +/** + * @defgroup svn_config_pool Configuration object pool API + * @{ + */ + +/* Opaque thread-safe factory and container for configuration objects. + * + * Instances handed out are read-only and may be given to multiple callers + * from multiple threads. Configuration objects no longer referenced by + * any user may linger for a while before being cleaned up. + */ +typedef struct svn_repos__config_pool_t svn_repos__config_pool_t; + +/* Create a new configuration pool object with a lifetime determined by + * POOL and return it in *CONFIG_POOL. + * + * The THREAD_SAFE flag indicates whether the pool actually needs to be + * thread-safe and POOL must be also be thread-safe if this flag is set. + */ +svn_error_t * +svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool, + svn_boolean_t thread_safe, + apr_pool_t *pool); +/* Set *CFG to a read-only reference to the current contents of the + * configuration specified by PATH. If the latter is a URL, we read the + * data from a local repository. CONFIG_POOL will store the configuration + * and make further callers use the same instance if the content matches. + * If KEY is not NULL, *KEY will be set to a unique ID - if available. + * + * If MUST_EXIST is TRUE, a missing config file is also an error, *CFG + * is otherwise simply NULL. The CASE_SENSITIVE controls the lookup + * behavior for section and option names alike. + * + * PREFERRED_REPOS is only used if it is not NULL and PATH is a URL. + * If it matches the URL, access the repository through this object + * instead of creating a new repo instance. Note that this might not + * return the latest content. + * + * POOL determines the minimum lifetime of *CFG (may remain cached after + * release) but must not exceed the lifetime of the pool provided to + * #svn_repos__config_pool_create. + */ +svn_error_t * +svn_repos__config_pool_get(svn_config_t **cfg, + svn_membuf_t **key, + svn_repos__config_pool_t *config_pool, + const char *path, + svn_boolean_t must_exist, + svn_boolean_t case_sensitive, + svn_repos_t *preferred_repos, + apr_pool_t *pool); + +/** @} */ + +/** + * @defgroup svn_authz_pool Authz object pool API + * @{ + */ + +/* Opaque thread-safe factory and container for authorization objects. + * + * Instances handed out are read-only and may be given to multiple callers + * from multiple threads. Authorization objects no longer referenced by + * any user may linger for a while before being cleaned up. + */ +typedef struct svn_repos__authz_pool_t svn_repos__authz_pool_t; + +/* Create a new authorization pool object with a lifetime determined by + * POOL and return it in *AUTHZ_POOL. CONFIG_POOL will be the common + * source for the configuration data underlying the authz objects and must + * remain valid at least until POOL cleanup. + * + * The THREAD_SAFE flag indicates whether the pool actually needs to be + * thread-safe and POOL must be also be thread-safe if this flag is set. + */ +svn_error_t * +svn_repos__authz_pool_create(svn_repos__authz_pool_t **authz_pool, + svn_repos__config_pool_t *config_pool, + svn_boolean_t thread_safe, + apr_pool_t *pool); + +/* Set *AUTHZ_P to a read-only reference to the current contents of the + * authorization specified by PATH and GROUPS_PATH. If these are URLs, + * we read the data from a local repository (see #svn_repos_authz_read2). + * AUTHZ_POOL will store the authz data and make further callers use the + * same instance if the content matches. + * + * If MUST_EXIST is TRUE, a missing config file is also an error, *AUTHZ_P + * is otherwise simply NULL. + * + * PREFERRED_REPOS is only used if it is not NULL and PATH is a URL. + * If it matches the URL, access the repository through this object + * instead of creating a new repo instance. Note that this might not + * return the latest content. + * + * POOL determines the minimum lifetime of *AUTHZ_P (may remain cached + * after release) but must not exceed the lifetime of the pool provided to + * svn_repos__authz_pool_create. + */ +svn_error_t * +svn_repos__authz_pool_get(svn_authz_t **authz_p, + svn_repos__authz_pool_t *authz_pool, + const char *path, + const char *groups_path, + svn_boolean_t must_exist, + svn_repos_t *preferred_repos, + apr_pool_t *pool); + +/** @} */ + +/* Adjust mergeinfo paths and revisions in ways that are useful when loading + * a dump stream. + * + * Set *NEW_VALUE_P to an adjusted version of the mergeinfo property value + * supplied in OLD_VALUE, with the following adjustments. + * + * - Normalize line endings: if all CRLF, change to LF; but error if + * mixed. If this normalization is performed, send a notification type + * svn_repos_notify_load_normalized_mergeinfo to NOTIFY_FUNC/NOTIFY_BATON. + * + * - Prefix all the merge source paths with PARENT_DIR, if not null. + * + * - Adjust any mergeinfo revisions not older than OLDEST_DUMPSTREAM_REV + * by using REV_MAP which maps (svn_revnum_t) old rev to (svn_revnum_t) + * new rev. + * + * - Adjust any mergeinfo revisions older than OLDEST_DUMPSTREAM_REV by + * (-OLDER_REVS_OFFSET), dropping any revisions that become <= 0. + * + * Allocate *NEW_VALUE_P in RESULT_POOL. + */ +svn_error_t * +svn_repos__adjust_mergeinfo_property(svn_string_t **new_value_p, + const svn_string_t *old_value, + const char *parent_dir, + apr_hash_t *rev_map, + svn_revnum_t oldest_dumpstream_rev, + apr_int32_t older_revs_offset, + svn_repos_notify_func_t notify_func, + void *notify_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* A (nearly) opaque representation of an ordered list of header lines. + */ +typedef struct apr_array_header_t svn_repos__dumpfile_headers_t; + +/* Create an empty set of headers. + */ +svn_repos__dumpfile_headers_t * +svn_repos__dumpfile_headers_create(apr_pool_t *pool); + +/* Push the header (KEY, VAL) onto HEADERS. + * + * Duplicate the key and value into HEADERS's pool. + */ +void +svn_repos__dumpfile_header_push(svn_repos__dumpfile_headers_t *headers, + const char *key, + const char *val); + +/* Push the header (KEY, val = VAL_FMT ...) onto HEADERS. + * + * Duplicate the key and value into HEADERS's pool. + */ +void +svn_repos__dumpfile_header_pushf(svn_repos__dumpfile_headers_t *headers, + const char *key, + const char *val_fmt, + ...) + __attribute__((format(printf, 3, 4))); + +/* Write to STREAM the headers in HEADERS followed by a blank line. + */ +svn_error_t * +svn_repos__dump_headers(svn_stream_t *stream, + svn_repos__dumpfile_headers_t *headers, + apr_pool_t *scratch_pool); + +/* Write a revision record to DUMP_STREAM for revision REVISION with revision + * properies REVPROPS, creating appropriate headers. + * + * Include all of the headers in EXTRA_HEADERS (if non-null), ignoring + * the revision number header and the three content length headers (which + * will be recreated as needed). EXTRA_HEADERS maps (char *) key to + * (char *) value. + * + * REVPROPS maps (char *) key to (svn_string_t *) value. + * + * Iff PROPS_SECTION_ALWAYS is true, include a prop content section (and + * corresponding header) even when REVPROPS is empty. This option exists + * to support a historical difference between svndumpfilter and svnadmin + * dump. + * + * Finally write another blank line. + */ +svn_error_t * +svn_repos__dump_revision_record(svn_stream_t *dump_stream, + svn_revnum_t revision, + apr_hash_t *extra_headers, + apr_hash_t *revprops, + svn_boolean_t props_section_always, + apr_pool_t *scratch_pool); + +/* Output node headers and props. + * + * Output HEADERS, content length headers, blank line, and + * then PROPS_STR (if non-null) to DUMP_STREAM. + * + * HEADERS is an array of headers as struct {const char *key, *val;}. + * Write them all in the given order. + * + * PROPS_STR is the property content block, including a terminating + * 'PROPS_END\n' line. Iff PROPS_STR is non-null, write a + * Prop-content-length header and the prop content block. + * + * Iff HAS_TEXT is true, write a Text-content length, using the value + * TEXT_CONTENT_LENGTH. + * + * Write a Content-length header, its value being the sum of the + * Prop- and Text- content length headers, if props and/or text are present + * or if CONTENT_LENGTH_ALWAYS is true. + */ +svn_error_t * +svn_repos__dump_node_record(svn_stream_t *dump_stream, + svn_repos__dumpfile_headers_t *headers, + svn_stringbuf_t *props_str, + svn_boolean_t has_text, + svn_filesize_t text_content_length, + svn_boolean_t content_length_always, + apr_pool_t *scratch_pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/include/private/svn_sorts_private.h b/contrib/subversion/subversion/include/private/svn_sorts_private.h new file mode 100644 index 000000000..c358513e9 --- /dev/null +++ b/contrib/subversion/subversion/include/private/svn_sorts_private.h @@ -0,0 +1,227 @@ +/** + * @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_sorts_private.h + * @brief all sorts of sorts. + */ + + +#ifndef SVN_SORTS_PRIVATE_H +#define SVN_SORTS_PRIVATE_H + +#include "../svn_sorts.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/** This structure is used to hold a key/value from a hash table. + * @note Private. For use by Subversion's own code only. See issue #1644. + */ +struct svn_sort__item_t { + /** pointer to the key */ + const void *key; + + /** size of the key */ + apr_ssize_t klen; + + /** pointer to the value */ + void *value; +}; + +/** Sort @a ht according to its keys, return an @c apr_array_header_t + * containing @c svn_sort__item_t structures holding those keys and values + * (i.e. for each @c svn_sort__item_t @a item in the returned array, + * @a item->key and @a item->size are the hash key, and @a item->value points to + * the hash value). + * + * Storage is shared with the original hash, not copied. + * + * @a comparison_func should take two @c svn_sort__item_t's and return an + * integer greater than, equal to, or less than 0, according as the first item + * is greater than, equal to, or less than the second. + * + * @note Private. For use by Subversion's own code only. See issue #1644. + * + * @note This function and the @c svn_sort__item_t should go over to APR. + */ +apr_array_header_t * +svn_sort__hash(apr_hash_t *ht, + int (*comparison_func)(const svn_sort__item_t *, + const svn_sort__item_t *), + apr_pool_t *pool); + +/* Sort APR array @a array using ordering defined by @a comparison_func. + * @a comparison_func is defined as for the C stdlib function qsort(). + */ +void +svn_sort__array(apr_array_header_t *array, + int (*comparison_func)(const void *, + const void *)); + +/* Return the lowest index at which the element @a *key should be inserted into + * the array @a array, according to the ordering defined by @a compare_func. + * The array must already be sorted in the ordering defined by @a compare_func. + * @a compare_func is defined as for the C stdlib function bsearch(); the + * @a key will always passed to it as the second parameter. + * + * @note Private. For use by Subversion's own code only. + */ +int +svn_sort__bsearch_lower_bound(const apr_array_header_t *array, + const void *key, + int (*compare_func)(const void *, const void *)); + +/* Find the lowest index at which the element @a *key should be inserted into + * the array @a array, according to the ordering defined by @a compare_func. + * The array must already be sorted in the ordering defined by @a compare_func. + * @a compare_func is defined as for the C stdlib function bsearch(); the + * @a key will always passed to it as the second parameter. + * + * Returns a reference to the array element at the insertion location if + * that matches @a key and return NULL otherwise. If you call this function + * multiple times for the same array and expect the results to often be + * consecutive array elements, provide @a hint. It should be initialized + * with -1 for the first call and receives the array index if the returned + * element. If the return value is NULL, @a *hint is the location where + * the respective key would be inserted. + * + * @note Private. For use by Subversion's own code only. + */ +void * +svn_sort__array_lookup(const apr_array_header_t *array, + const void *key, + int *hint, + int (*compare_func)(const void *, const void *)); + + +/* Insert a shallow copy of @a *new_element into the array @a array at the index + * @a insert_index, growing the array and shuffling existing elements along to + * make room. + * + * @note Private. For use by Subversion's own code only. + */ +void +svn_sort__array_insert(apr_array_header_t *array, + const void *new_element, + int insert_index); + + +/* Remove @a elements_to_delete elements starting at @a delete_index from the + * array @a arr. If @a delete_index is not a valid element of @a arr, + * @a elements_to_delete is not greater than zero, or + * @a delete_index + @a elements_to_delete is greater than @a arr->nelts, + * then do nothing. + * + * @note Private. For use by Subversion's own code only. + */ +void +svn_sort__array_delete(apr_array_header_t *arr, + int delete_index, + int elements_to_delete); + +/* Reverse the order of elements in @a array, in place. + * + * @note Private. For use by Subversion's own code only. + */ +void +svn_sort__array_reverse(apr_array_header_t *array, + apr_pool_t *scratch_pool); + +/** Priority queues. + * + * @defgroup svn_priority_queue__t Priority Queues + * @{ + */ + +/** + * We implement priority queues on top of existing ELEMENTS arrays. They + * provide us with memory management and very basic element type information. + * + * The extraction order is being defined by a comparison function similar + * to the ones used with qsort. The first element in the queue is always + * on with COMPARISON_FUNC(first,element) <= 0, for all elements in the + * queue. + */ + +/** + * Opaque data type for priority queues. + */ +typedef struct svn_priority_queue__t svn_priority_queue__t; + +/** + * Return a priority queue containing all provided @a elements and prioritize + * them according to @a compare_func. + * + * @note The priority queue will use the existing @a elements array for data + * storage. So, you must not manipulate that array while using the queue. + * Also, the lifetime of the queue is bound to that of the array. + */ +svn_priority_queue__t * +svn_priority_queue__create(apr_array_header_t *elements, + int (*compare_func)(const void *, const void *)); + +/** + * Returns the number of elements in the @a queue. + */ +apr_size_t +svn_priority_queue__size(svn_priority_queue__t *queue); + +/** + * Returns a reference to the first element in the @a queue. The queue + * contents remains unchanged. If the @a queue is empty, #NULL will be + * returned. + */ +void * +svn_priority_queue__peek(svn_priority_queue__t *queue); + +/** + * Notify the @a queue after modifying the first item as returned by + * #svn_priority_queue__peek. + */ +void +svn_priority_queue__update(svn_priority_queue__t *queue); + +/** + * Remove the first element from the @a queue. This is a no-op for empty + * queues. + */ +void +svn_priority_queue__pop(svn_priority_queue__t *queue); + +/** + * Append the new @a element to the @a queue. @a element must neither be + * #NULL nor the first element as returned by #svn_priority_queue__peek. + */ +void +svn_priority_queue__push(svn_priority_queue__t *queue, const void *element); + +/** @} */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_SORTS_PRIVATE_H */ diff --git a/contrib/subversion/subversion/include/private/svn_sqlite.h b/contrib/subversion/subversion/include/private/svn_sqlite.h index 4c03e19a8..6faa5b384 100644 --- a/contrib/subversion/subversion/include/private/svn_sqlite.h +++ b/contrib/subversion/subversion/include/private/svn_sqlite.h @@ -63,7 +63,7 @@ typedef enum svn_sqlite__mode_e { 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); + void *baton); /* Step the given statement; if it returns SQLITE_DONE, reset the statement. @@ -117,12 +117,16 @@ svn_sqlite__read_schema_version(int *version, STATEMENTS itself may be NULL, in which case it has no impact. See svn_sqlite__get_statement() for how these strings are used. + TIMEOUT defines the SQLite busy timeout, values <= 0 cause a Subversion + default to be 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__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], int latest_schema, const char * const *upgrade_sql, + apr_int32_t timeout, apr_pool_t *result_pool, apr_pool_t *scratch_pool); /* Explicitly close the connection in DB. */ @@ -130,11 +134,16 @@ 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. */ + in BATON should live at least as long as the connection in DB. + + Pass TRUE if the result of the function is constant within a statement with + a specific set of argument values and FALSE if not (or when in doubt). When + TRUE newer Sqlite versions use this knowledge for query optimizations. */ svn_error_t * svn_sqlite__create_scalar_function(svn_sqlite__db_t *db, const char *func_name, int argc, + svn_boolean_t deterministic, svn_sqlite__func_t func, void *baton); @@ -345,6 +354,11 @@ svn_sqlite__column_is_null(svn_sqlite__stmt_t *stmt, int column); int svn_sqlite__column_bytes(svn_sqlite__stmt_t *stmt, int column); +/* When Subversion is compiled in maintainer mode: enables the sqlite error + logging to SVN_DBG_OUTPUT. */ +void +svn_sqlite__dbg_enable_errorlog(void); + /* --------------------------------------------------------------------- */ @@ -372,6 +386,9 @@ svn_sqlite__result_null(svn_sqlite__context_t *sctx); void svn_sqlite__result_int64(svn_sqlite__context_t *sctx, apr_int64_t val); +void +svn_sqlite__result_error(svn_sqlite__context_t *sctx, const char *msg, int num); + /* --------------------------------------------------------------------- */ @@ -522,7 +539,7 @@ svn_sqlite__with_immediate_transaction(svn_sqlite__db_t *db, 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 + always behaving like a deferred transaction. Can it be combined with svn_sqlite__with_transaction()? */ svn_error_t * diff --git a/contrib/subversion/subversion/include/private/svn_string_private.h b/contrib/subversion/subversion/include/private/svn_string_private.h index 8579f4d33..2f162733d 100644 --- a/contrib/subversion/subversion/include/private/svn_string_private.h +++ b/contrib/subversion/subversion/include/private/svn_string_private.h @@ -131,10 +131,13 @@ svn_membuf__nzero(svn_membuf_t *membuf, apr_size_t size); svn_string_t * svn_stringbuf__morph_into_string(svn_stringbuf_t *strbuf); -/** Like apr_strtoff but provided here for backward compatibility - * with APR 0.9 */ -apr_status_t -svn__strtoff(apr_off_t *offset, const char *buf, char **end, int base); +/** Like strtoul but with a fixed base of 10 and without overflow checks. + * This allows the compiler to generate massively faster (4x on 64bit LINUX) + * code. Overflow checks may be added on the caller side where you might + * want to test for a more specific value range anyway. + */ +unsigned long +svn__strtoul(const char *buffer, const char **end); /** Number of chars needed to represent signed (19 places + sign + NUL) or * unsigned (20 places + NUL) integers as strings. @@ -156,22 +159,55 @@ apr_size_t svn__i64toa(char * dest, apr_int64_t number); /** Returns a decimal string for @a number allocated in @a pool. Put in - * the @a seperator at each third place. + * the @a separator at each third place. */ char * -svn__ui64toa_sep(apr_uint64_t number, char seperator, apr_pool_t *pool); +svn__ui64toa_sep(apr_uint64_t number, char separator, apr_pool_t *pool); /** Returns a decimal string for @a number allocated in @a pool. Put in - * the @a seperator at each third place. + * the @a separator at each third place. */ char * -svn__i64toa_sep(apr_int64_t number, char seperator, apr_pool_t *pool); +svn__i64toa_sep(apr_int64_t number, char separator, apr_pool_t *pool); + + +/** Writes the @a number as base36-encoded string into @a dest. The latter + * must provide space for at least #SVN_INT64_BUFFER_SIZE characters. + * Returns the number chars written excluding the terminating NUL. + * + * @note The actual maximum buffer requirement is much shorter than + * #SVN_INT64_BUFFER_SIZE but introducing yet another constant is only + * marginally useful and may open the door to security issues when e.g. + * switching between base10 and base36 encoding. + */ +apr_size_t +svn__ui64tobase36(char *dest, apr_uint64_t number); + +/** Returns the value of the base36 encoded unsigned integer starting at + * @a source. If @a next is not NULL, @a *next will be set to the first + * position after the integer. + * + * The data in @a source will be considered part of the number to parse + * as long as the characters are within the base36 range. If there are + * no such characters to begin with, 0 is returned. Inputs with more than + * #SVN_INT64_BUFFER_SIZE digits will not be fully parsed, i.e. the value + * of @a *next as well as the return value are undefined. + */ +apr_uint64_t +svn__base36toui64(const char **next, const char *source); + +/** + * The upper limit of the similarity range returned by + * svn_cstring__similarity() and svn_string__similarity(). + */ +#define SVN_STRING__SIM_RANGE_MAX 1000000 /** * Computes the similarity score of STRA and STRB. Returns the ratio * of the length of their longest common subsequence and the average - * length of the strings, normalized to the range [0..1000]. - * The result is equivalent to Python's + * length of the strings, normalized to the range + * [0..SVN_STRING__SIM_RANGE_MAX]. The result is equivalent to + * Python's * * difflib.SequenceMatcher.ratio * @@ -196,7 +232,7 @@ svn__i64toa_sep(apr_int64_t number, char seperator, apr_pool_t *pool); * has O(strlen(STRA) * strlen(STRB)) worst-case performance, * so do keep a rein on your enthusiasm. */ -unsigned int +apr_size_t svn_cstring__similarity(const char *stra, const char *strb, svn_membuf_t *buffer, apr_size_t *rlcs); @@ -204,12 +240,82 @@ svn_cstring__similarity(const char *stra, const char *strb, * Like svn_cstring__similarity, but accepts svn_string_t's instead * of NUL-terminated character strings. */ -unsigned int +apr_size_t svn_string__similarity(const svn_string_t *stringa, const svn_string_t *stringb, svn_membuf_t *buffer, apr_size_t *rlcs); +/* Return the lowest position at which A and B differ. If no difference + * can be found in the first MAX_LEN characters, MAX_LEN will be returned. + */ +apr_size_t +svn_cstring__match_length(const char *a, + const char *b, + apr_size_t max_len); + +/* Return the number of bytes before A and B that don't differ. If no + * difference can be found in the first MAX_LEN characters, MAX_LEN will + * be returned. Please note that A-MAX_LEN and B-MAX_LEN must both be + * valid addresses. + */ +apr_size_t +svn_cstring__reverse_match_length(const char *a, + const char *b, + apr_size_t max_len); + +/** @} */ + +/** Prefix trees. + * + * Prefix trees allow for a space-efficient representation of a set of path- + * like strings, i.e. those that share common prefixes. Any given string + * value will be stored only once, i.e. two strings stored in the same tree + * are equal if and only if the point to the same #svn_prefix_string__t. + * + * @defgroup svn_prefix_string Strings in prefix trees. +* @{ + */ + +/** + * Opaque data type for prefix-tree-based strings. + */ +typedef struct svn_prefix_string__t svn_prefix_string__t; + +/** + * Opaque data type representing a prefix tree + */ +typedef struct svn_prefix_tree__t svn_prefix_tree__t; + +/** + * Return a new prefix tree allocated in @a pool. + */ +svn_prefix_tree__t * +svn_prefix_tree__create(apr_pool_t *pool); + +/** + * Return a string with the value @a s stored in @a tree. If no such string + * exists yet, add it automatically. + */ +svn_prefix_string__t * +svn_prefix_string__create(svn_prefix_tree__t *tree, + const char *s); + +/** + * Return the contents of @a s as a new string object allocated in @a pool. + */ +svn_string_t * +svn_prefix_string__expand(const svn_prefix_string__t *s, + apr_pool_t *pool); + +/** + * Compare the two strings @a lhs and @a rhs that must be part of the same + * tree. + */ +int +svn_prefix_string__compare(const svn_prefix_string__t *lhs, + const svn_prefix_string__t *rhs); + /** @} */ /** @} */ diff --git a/contrib/subversion/subversion/include/private/svn_subr_private.h b/contrib/subversion/subversion/include/private/svn_subr_private.h index 30fbbf264..095d71c5d 100644 --- a/contrib/subversion/subversion/include/private/svn_subr_private.h +++ b/contrib/subversion/subversion/include/private/svn_subr_private.h @@ -26,7 +26,7 @@ #include "svn_types.h" #include "svn_io.h" -#include "svn_version.h" +#include "svn_config.h" #ifdef __cplusplus @@ -95,11 +95,32 @@ svn_spillbuf__create(apr_size_t blocksize, apr_size_t maxsize, apr_pool_t *result_pool); +/* Create a spill buffer, with extra parameters. */ +svn_spillbuf_t * +svn_spillbuf__create_extended(apr_size_t blocksize, + apr_size_t maxsize, + svn_boolean_t delete_on_close, + svn_boolean_t spill_all_contents, + const char* dirpath, + apr_pool_t *result_pool); /* Determine how much content is stored in the spill buffer. */ svn_filesize_t svn_spillbuf__get_size(const svn_spillbuf_t *buf); +/* Determine how much content the spill buffer is caching in memory. */ +svn_filesize_t +svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf); + +/* Retrieve the name of the spill file. The returned value can be NULL + if the file has not been created yet. */ +const char * +svn_spillbuf__get_filename(const svn_spillbuf_t *buf); + +/* Retrieve the handle of the spill file. The returned value can be + NULL if the file has not been created yet. */ +apr_file_t * +svn_spillbuf__get_file(const svn_spillbuf_t *buf); /* Write some data into the spill buffer. */ svn_error_t * @@ -153,13 +174,13 @@ svn_spillbuf__process(svn_boolean_t *exhausted, typedef struct svn_spillbuf_reader_t svn_spillbuf_reader_t; -/* Create a spill-buffer and a reader for it. */ +/* Create a spill-buffer and a reader for it, using the same arguments as + svn_spillbuf__create(). */ svn_spillbuf_reader_t * svn_spillbuf__reader_create(apr_size_t blocksize, apr_size_t maxsize, apr_pool_t *result_pool); - /* Read @a len bytes from @a reader into @a data. The number of bytes actually read is stored in @a amt. If the content is exhausted, then @a amt is set to zero. It will always be non-zero if the spill-buffer @@ -191,17 +212,23 @@ svn_spillbuf__reader_write(svn_spillbuf_reader_t *reader, apr_pool_t *scratch_pool); -/* Return a stream built on top of a spillbuf, using the same arguments as - svn_spillbuf__create(). This stream can be used for reading and writing, - but implements the same basic sematics of a spillbuf for the underlying - storage. */ +/* Return a stream built on top of a spillbuf. + + This stream can be used for reading and writing, but implements the + same basic semantics of a spillbuf for the underlying storage. */ svn_stream_t * -svn_stream__from_spillbuf(apr_size_t blocksize, - apr_size_t maxsize, +svn_stream__from_spillbuf(svn_spillbuf_t *buf, apr_pool_t *result_pool); /** @} */ +/*----------------------------------------------------*/ + +/** + * @defgroup svn_checksum_private Checksumming helper APIs + * @{ + */ + /** * Internal function for creating a MD5 checksum from a binary digest. * @@ -221,6 +248,76 @@ svn_checksum_t * svn_checksum__from_digest_sha1(const unsigned char *digest, apr_pool_t *result_pool); +/** + * Internal function for creating a 32 bit FNV-1a checksum from a binary + * digest. + * + * @since New in 1.9 + */ +svn_checksum_t * +svn_checksum__from_digest_fnv1a_32(const unsigned char *digest, + apr_pool_t *result_pool); + +/** + * Internal function for creating a modified 32 bit FNV-1a checksum from + * a binary digest. + * + * @since New in 1.9 + */ +svn_checksum_t * +svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest, + apr_pool_t *result_pool); + + +/** + * Return a stream that calculates a checksum of type @a kind over all + * data written to the @a inner_stream. When the returned stream gets + * closed, write the checksum to @a *checksum. + * Allocate the result in @a pool. + * + * @note The stream returned only supports #svn_stream_write and + * #svn_stream_close. + */ +svn_stream_t * +svn_checksum__wrap_write_stream(svn_checksum_t **checksum, + svn_stream_t *inner_stream, + svn_checksum_kind_t kind, + apr_pool_t *pool); + +/** + * Return a stream that calculates a 32 bit modified FNV-1a checksum + * over all data written to the @a inner_stream and writes the digest + * to @a *digest when the returned stream gets closed. + * Allocate the stream in @a pool. + */ +svn_stream_t * +svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest, + svn_stream_t *inner_stream, + apr_pool_t *pool); + +/** + * Return a 32 bit FNV-1a checksum for the first @a len bytes in @a input. + * + * @since New in 1.9 + */ +apr_uint32_t +svn__fnv1a_32(const void *input, apr_size_t len); + +/** + * Return a 32 bit modified FNV-1a checksum for the first @a len bytes in + * @a input. + * + * @note This is a proprietary checksumming algorithm based FNV-1a with + * approximately the same strength. It is up to 4 times faster + * than plain FNV-1a for longer data blocks. + * + * @since New in 1.9 + */ +apr_uint32_t +svn__fnv1a_32x4(const void *input, apr_size_t len); + +/** @} */ + /** * @defgroup svn_hash_support Hash table serialization support @@ -285,6 +382,54 @@ svn_hash__make(apr_pool_t *pool); /** @} */ +/** + * @defgroup svn_hash_read Reading serialized hash tables + * @{ + */ + +/** Struct that represents a key value pair read from a serialized hash + * representation. There are special cases that can also be represented: + * a #NULL @a key signifies the end of the hash, a #NULL @a val for non- + * NULL keys is only possible in incremental mode describes a deletion. + * + * @since New in 1.9. + */ +typedef struct svn_hash__entry_t +{ + /** 0-terminated Key. #NULL if this contains no data at all because we + * encountered the end of the hash. */ + char *key; + + /** Length of @a key. Must be 0 if @a key is #NULL. */ + apr_size_t keylen; + + /** 0-terminated value stored with the key. If this is #NULL for a + * non-NULL @a key, then this means that the key shall be removed from + * the hash (only used in incremental mode). Must be #NULL if @a key is + * #NULL. */ + char *val; + + /** Length of @a val. Must be 0 if @a val is #NULL. */ + apr_size_t vallen; +} svn_hash__entry_t; + +/** Reads a single key-value pair from @a stream and returns it in the + * caller-provided @a *entry (members don't need to be pre-initialized). + * @a pool is used to allocate members of @a *entry and for tempoaries. + * + * @see #svn_hash_read2 for more details. + * + * @since New in 1.9. + */ +svn_error_t * +svn_hash__read_entry(svn_hash__entry_t *entry, + svn_stream_t *stream, + const char *terminator, + svn_boolean_t incremental, + apr_pool_t *pool); + +/** @} */ + /** @} */ @@ -332,20 +477,230 @@ svn_version__at_least(svn_version_t *version, int minor, int patch); -/** Like svn_ver_check_list(), but with a @a comparator parameter. - * Private backport of svn_ver_check_list2() from trunk. +/** @} */ + +/** + * @defgroup svn_compress Data (de-)compression API + * @{ + */ + +/* This is at least as big as the largest size of an integer that + svn__encode_uint() 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 SVN__MAX_ENCODED_UINT_LEN 10 + +/* Compression method parameters for svn__encode_uint. */ + +/* No compression (but a length prefix will still be added to the buffer) */ +#define SVN__COMPRESSION_NONE 0 + +/* Fastest, least effective compression method & level provided by zlib. */ +#define SVN__COMPRESSION_ZLIB_MIN 1 + +/* Default compression method & level provided by zlib. */ +#define SVN__COMPRESSION_ZLIB_DEFAULT 5 + +/* Slowest, best compression method & level provided by zlib. */ +#define SVN__COMPRESSION_ZLIB_MAX 9 + +/* Encode VAL into the buffer P using the variable-length 7b/8b unsigned + 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 SVN__MAX_ENCODED_UINT_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] +*/ +unsigned char * +svn__encode_uint(unsigned char *p, apr_uint64_t val); + +/* Decode an unsigned 7b/8b-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 svn__encode_uint() earlier in this file for more + detail on the encoding format. */ +const unsigned char * +svn__decode_uint(apr_uint64_t *val, + const unsigned char *p, + const unsigned char *end); + +/* Get the data from IN, compress it according to the specified + * COMPRESSION_METHOD and write the result to OUT. + * SVN__COMPRESSION_NONE is valid for COMPRESSION_METHOD. + */ +svn_error_t * +svn__compress(svn_stringbuf_t *in, + svn_stringbuf_t *out, + int compression_method); + +/* Get the compressed data from IN, decompress it and write the result to + * OUT. Return an error if the decompressed size is larger than LIMIT. + */ +svn_error_t * +svn__decompress(svn_stringbuf_t *in, + svn_stringbuf_t *out, + apr_size_t limit); + +/** @} */ + +/** + * @defgroup svn_root_pools Recycle-able root pools API + * @{ + */ + +/* Opaque thread-safe container for unused / recylcleable root pools. + * + * Recyling root pools (actually, their allocators) circumvents a + * scalability bottleneck in the OS memory management when multi-threaded + * applications frequently create and destroy allocators. + */ +typedef struct svn_root_pools__t svn_root_pools__t; + +/* Create a new root pools container and return it in *POOLS. */ 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_root_pools__create(svn_root_pools__t **pools); + +/* Return a currently unused pool from POOLS. If POOLS is empty, create a + * new root pool and return that. The pool returned is not thread-safe. + */ +apr_pool_t * +svn_root_pools__acquire_pool(svn_root_pools__t *pools); -/** To minimize merge churn in callers, alias the trunk name privately. */ -#define svn_ver_check_list2 svn_ver__check_list2 +/* Clear and release the given root POOL and put it back into POOLS. + * If that fails, destroy POOL. + */ +void +svn_root_pools__release_pool(apr_pool_t *pool, + svn_root_pools__t *pools); /** @} */ +/** + * @defgroup svn_config_private Private configuration handling API + * @{ + */ + +/* Future attempts to modify CFG will trigger an assertion. */ +void +svn_config__set_read_only(svn_config_t *cfg, + apr_pool_t *scratch_pool); + +/* Return TRUE, if CFG cannot be modified. */ +svn_boolean_t +svn_config__is_read_only(svn_config_t *cfg); + +/* Return TRUE, if OPTION in SECTION in CFG exists and does not require + * further expansion (due to either containing no placeholders or already + * having been expanded). */ +svn_boolean_t +svn_config__is_expanded(svn_config_t *cfg, + const char *section, + const char *option); + +/* Return a shallow copy of SCR in POOL. If SRC is read-only, different + * shallow copies may be used from different threads. + * + * Any single r/o svn_config_t or shallow copy is not thread-safe because + * it contains shared buffers for tempoary data. + */ +svn_config_t * +svn_config__shallow_copy(svn_config_t *src, + apr_pool_t *pool); + +/* Add / replace SECTION in TARGET with the same section from SOURCE by + * simply adding a reference to it. If TARGET is read-only, the sections + * list in target gets duplicated before the modification. + * + * This is an API tailored for use by the svn_repos__authz_pool_t API to + * prevent breach of encapsulation. + */ +void +svn_config__shallow_replace_section(svn_config_t *target, + svn_config_t *source, + const char *section); + +/* Allocate *CFG_HASH and populate it with default, empty, + * svn_config_t for the configuration categories (@c + * SVN_CONFIG_CATEGORY_SERVERS, @c SVN_CONFIG_CATEGORY_CONFIG, etc.). + * This returns a hash equivalent to svn_config_get_config when the + * config files are empty. + */ +svn_error_t * +svn_config__get_default_config(apr_hash_t **cfg_hash, + apr_pool_t *pool); + +/** @} */ + + +/** + * @defgroup svn_bit_array Packed bit array handling API + * @{ + */ + +/* This opaque data struct is an alternative to an INT->VOID hash. + * + * Technically, it is an automatically growing packed bit array. + * All indexes not previously set are implicitly 0 and setting it will + * grow the array as needed. + */ +typedef struct svn_bit_array__t svn_bit_array__t; + +/* Return a new bit array allocated in POOL. MAX is a mere hint for + * the initial size of the array in bits. + */ +svn_bit_array__t * +svn_bit_array__create(apr_size_t max, + apr_pool_t *pool); + +/* Set bit at index IDX in ARRAY to VALUE. If necessary, grow the + * underlying data buffer, i.e. any IDX is valid unless we run OOM. + */ +void +svn_bit_array__set(svn_bit_array__t *array, + apr_size_t idx, + svn_boolean_t value); + +/* Get the bit value at index IDX in ARRAY. Bits not previously accessed + * are implicitly 0 (or FALSE). That implies IDX can never be out-of-range. + */ +svn_boolean_t +svn_bit_array__get(svn_bit_array__t *array, + apr_size_t idx); + +/* Return the global pool used by the DSO loader, this may be NULL if + no DSOs have been loaded. */ +apr_pool_t * +svn_dso__pool(void); + +/** @} */ + + +/* Return the xml (expat) version we compiled against. */ +const char *svn_xml__compiled_version(void); + +/* Return the xml (expat) version we run against. */ +const char *svn_xml__runtime_version(void); + +/* Return the zlib version we compiled against. */ +const char *svn_zlib__compiled_version(void); + +/* Return the zlib version we run against. */ +const char *svn_zlib__runtime_version(void); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/private/svn_temp_serializer.h b/contrib/subversion/subversion/include/private/svn_temp_serializer.h index 7a007c38f..dd2e0478e 100644 --- a/contrib/subversion/subversion/include/private/svn_temp_serializer.h +++ b/contrib/subversion/subversion/include/private/svn_temp_serializer.h @@ -142,6 +142,22 @@ svn_temp_serializer__push(svn_temp_serializer__context_t *context, void svn_temp_serializer__pop(svn_temp_serializer__context_t *context); +/** + * Serialize a referenced sub-structure within the serialization + * @a context. @a source_struct must be a reference to the + * pointer in the original parent structure so that the correspondence in + * the serialized structure can be established. @a struct_size must match + * the result of @c sizeof() of the actual structure. + * + * This function is equivalent but more efficient than calling + * #svn_temp_serializer__push() immediately followed by + * #svn_temp_serializer__pop(). + */ +void +svn_temp_serializer__add_leaf(svn_temp_serializer__context_t *context, + const void * const * source_struct, + apr_size_t struct_size); + /** * Serialize a string referenced from the current structure within the * serialization @a context. @a s must be a reference to the @c char* diff --git a/contrib/subversion/subversion/include/private/svn_utf_private.h b/contrib/subversion/subversion/include/private/svn_utf_private.h index 9f5a4ad41..4584944fb 100644 --- a/contrib/subversion/subversion/include/private/svn_utf_private.h +++ b/contrib/subversion/subversion/include/private/svn_utf_private.h @@ -21,7 +21,7 @@ * @endcopyright * * @file svn_utf_private.h - * @brief UTF validation routines + * @brief UTF validation and normalization routines */ #ifndef SVN_UTF_PRIVATE_H @@ -31,6 +31,8 @@ #include #include "svn_types.h" +#include "svn_string.h" +#include "svn_string_private.h" #ifdef __cplusplus extern "C" { @@ -71,6 +73,18 @@ svn_utf__last_valid(const char *src, apr_size_t len); const char * svn_utf__last_valid2(const char *src, apr_size_t len); +/* Copy LENGTH bytes of SRC, converting characters as follows: + - Pass characters from the ASCII subset to the result + - Strip all combining marks from the string + - Represent other valid Unicode chars as {U+XXXX} + - Replace invalid Unicode chars with {U?XXXX} + - Represent chars that are not valid UTF-8 as ?\XX + - Replace codes outside the Unicode range with a sequence of ?\XX + - Represent the null byte as \0 + Allocate the result in POOL. */ +const char * +svn_utf__fuzzy_escape(const char *src, apr_size_t length, apr_pool_t *pool); + const char * svn_utf__cstring_from_utf8_fuzzy(const char *src, apr_pool_t *pool, @@ -80,6 +94,166 @@ svn_utf__cstring_from_utf8_fuzzy(const char *src, apr_pool_t *)); +#if defined(WIN32) +/* On Windows: Convert the UTF-8 string SRC to UTF-16. + If PREFIX is not NULL, prepend it to the converted result. + The result, if not empty, will be allocated in RESULT_POOL. */ +svn_error_t * +svn_utf__win32_utf8_to_utf16(const WCHAR **result, + const char *src, + const WCHAR *prefix, + apr_pool_t *result_pool); + +/* On Windows: Convert the UTF-16 string SRC to UTF-8. + If PREFIX is not NULL, prepend it to the converted result. + The result, if not empty, will be allocated in RESULT_POOL. */ +svn_error_t * +svn_utf__win32_utf16_to_utf8(const char **result, + const WCHAR *src, + const char *prefix, + apr_pool_t *result_pool); +#endif /* WIN32*/ + + +/* A constant used for many length parameters in the utf8proc wrappers + * to indicate that the length of a string is unknonw. */ +#define SVN_UTF__UNKNOWN_LENGTH ((apr_size_t) -1) + + +/* Compare two UTF-8 strings, ignoring normalization, using buffers + * BUF1 and BUF2 for temporary storage. If either of LEN1 or LEN2 is + * SVN_UTF__UNKNOWN_LENGTH, assume the associated string is + * null-terminated; otherwise, consider the string only up to the + * given length. + * + * Return compare value in *RESULT. + */ +svn_error_t * +svn_utf__normcmp(int *result, + const char *str1, apr_size_t len1, + const char *str2, apr_size_t len2, + svn_membuf_t *buf1, svn_membuf_t *buf2); + +/* Normalize the UTF-8 string STR to form C, using BUF for temporary + * storage. If LEN is SVN_UTF__UNKNOWN_LENGTH, assume STR is + * null-terminated; otherwise, consider the string only up to the + * given length. + * + * Return the normalized string in *RESULT, which shares storage with + * BUF and is valid only until the next time BUF is modified. + * + * A returned error may indicate that STRING contains invalid UTF-8 or + * invalid Unicode codepoints. + */ +svn_error_t* +svn_utf__normalize(const char **result, + const char *str, apr_size_t len, + svn_membuf_t *buf); + +/* Check if STRING is a valid, NFC-normalized UTF-8 string. Note that + * a FALSE return value may indicate that STRING is not valid UTF-8 at + * all. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_boolean_t +svn_utf__is_normalized(const char *string, apr_pool_t *scratch_pool); + +/* Encode an UCS-4 string to UTF-8, placing the result into BUFFER. + * While utf8proc does have a similar function, it does more checking + * and processing than we want here; this function does not attempt + * any normalizations but just encodes the individual code points. + * The encoded string will always be NUL-terminated. + * + * Return the length of the result (excluding the NUL terminator) in + * *result_length. + * + * A returned error indicates that a codepoint is invalid. + */ +svn_error_t * +svn_utf__encode_ucs4_string(svn_membuf_t *buffer, + const apr_int32_t *ucs4str, + apr_size_t length, + apr_size_t *result_length); + +/* Pattern matching similar to the the SQLite LIKE and GLOB + * operators. PATTERN, KEY and ESCAPE must all point to UTF-8 + * strings. Furthermore, ESCAPE, if provided, must be a character from + * the ASCII subset. + * + * If any of PATTERN_LEN, STRING_LEN or ESCAPE_LEN are + * SVN_UTF__UNKNOWN_LENGTH, assume the associated string is + * null-terminated; otherwise, consider the string only up to the + * given length. + * + * Use buffers PATTERN_BUF, STRING_BUF and TEMP_BUF for temporary storage. + * + * If SQL_LIKE is true, interpret PATTERN as a pattern used by the SQL + * LIKE operator and notice ESCAPE. Otherwise it's a Unix fileglob + * pattern, and ESCAPE must be NULL. + * + * Set *MATCH to the result of the comparison. +*/ +svn_error_t * +svn_utf__glob(svn_boolean_t *match, + const char *pattern, apr_size_t pattern_len, + const char *string, apr_size_t string_len, + const char *escape, apr_size_t escape_len, + svn_boolean_t sql_like, + svn_membuf_t *pattern_buf, + svn_membuf_t *string_buf, + svn_membuf_t *temp_buf); + +/* Return the compiled version of the wrapped utf8proc library. */ +const char * +svn_utf__utf8proc_compiled_version(void); + +/* Return the runtime version of the wrapped utf8proc library. */ +const char * +svn_utf__utf8proc_runtime_version(void); + +/* Convert an UTF-16 (or UCS-2) string to UTF-8, returning the pointer + * in RESULT. If BIG_ENDIAN is set, then UTF16STR is big-endian; + * otherwise, it's little-endian. + * + * If UTF16LEN is SVN_UTF__UNKNOWN_LENGTH, then UTF16STR must be + * terminated with a zero; otherwise, it is the number of 16-bit codes + * to convert, and the source string may contain NUL values. + * + * Allocate RESULT in RESULT_POOL and use SCRATCH_POOL for + * intermediate allocation. + * + * This function combines UTF-16 surrogate pairs into single code + * points, but will leave single lead or trail surrogates unchanged. + */ +svn_error_t * +svn_utf__utf16_to_utf8(const svn_string_t **result, + const apr_uint16_t *utf16str, + apr_size_t utf16len, + svn_boolean_t big_endian, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Convert an UTF-32 string to UTF-8, returning the pointer in + * RESULT. If BIG_ENDIAN is set, then UTF32STR is big-endian; + * otherwise, it's little-endian. + * + * If UTF32LEN is SVN_UTF__UNKNOWN_LENGTH, then UTF32STR must be + * terminated with a zero; otherwise, it is the number of 32-bit codes + * to convert, and the source string may contain NUL values. + * + * Allocate RESULT in RESULT_POOL and use SCRATCH_POOL for + * intermediate allocation. + */ +svn_error_t * +svn_utf__utf32_to_utf8(const svn_string_t **result, + const apr_int32_t *utf32str, + apr_size_t utf32len, + svn_boolean_t big_endian, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/private/svn_wc_private.h b/contrib/subversion/subversion/include/private/svn_wc_private.h index fce42b053..28d224791 100644 --- a/contrib/subversion/subversion/include/private/svn_wc_private.h +++ b/contrib/subversion/subversion/include/private/svn_wc_private.h @@ -82,8 +82,6 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, 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, @@ -354,7 +352,6 @@ svn_wc__get_wcroot(const char **wcroot_abspath, * before the 1.7 release. */ - /* * Convert from svn_wc_conflict_description2_t to * svn_wc_conflict_description_t. This is needed by some backwards-compat @@ -370,7 +367,11 @@ svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict, /* * Convert from svn_wc_status3_t to svn_wc_status2_t. * Allocate the result in RESULT_POOL. + * + * Deprecated because svn_wc_status2_t is deprecated and the only + * calls are from other deprecated functions. */ +SVN_DEPRECATED svn_error_t * svn_wc__status2_from_3(svn_wc_status2_t **status, const svn_wc_status3_t *old_status, @@ -379,15 +380,13 @@ svn_wc__status2_from_3(svn_wc_status2_t **status, apr_pool_t *result_pool, apr_pool_t *scratch_pool); - /** * Set @a *children to a new array of the immediate children of the working * node at @a dir_abspath. The elements of @a *children are (const char *) * absolute paths. * - * Include children that are scheduled for deletion. Iff @a show_hidden - * is true, also include children that are 'excluded' or 'server-excluded' or - * 'not-present'. + * Include children that are scheduled for deletion, but not those that + * are excluded, server-excluded or not-present. * * Return every path that refers to a child of the working node at * @a dir_abspath. Do not include a path just because it was a child of a @@ -401,24 +400,20 @@ svn_error_t * svn_wc__node_get_children_of_working_node(const apr_array_header_t **children, svn_wc_context_t *wc_ctx, const char *dir_abspath, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool); /** - * Like svn_wc__node_get_children_of_working_node(), except also include any - * path that was a child of a deleted directory that existed at - * @a dir_abspath, even if that directory is now scheduled to be replaced by - * the working node at @a dir_abspath. + * Gets the immediate 'not-present' children of a node. + * + * #### Needed during 'svn cp WC URL' to handle mixed revision cases */ svn_error_t * -svn_wc__node_get_children(const apr_array_header_t **children, - svn_wc_context_t *wc_ctx, - const char *dir_abspath, - svn_boolean_t show_hidden, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - +svn_wc__node_get_not_present_children(const apr_array_header_t **children, + svn_wc_context_t *wc_ctx, + const char *dir_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** * Fetch the repository information for the working version @@ -442,18 +437,6 @@ svn_wc__node_get_repos_info(svn_revnum_t *revision, apr_pool_t *result_pool, apr_pool_t *scratch_pool); - - -/** - * Get the depth of @a local_abspath using @a wc_ctx. If @a local_abspath is - * not in the working copy, return @c SVN_ERR_WC_PATH_NOT_FOUND. - */ -svn_error_t * -svn_wc__node_get_depth(svn_depth_t *depth, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool); - /** * Get the changed revision, date and author for @a local_abspath using @a * wc_ctx. Allocate the return values in @a result_pool; use @a scratch_pool @@ -501,6 +484,8 @@ svn_wc__node_get_url(const char **url, * If not NULL, sets @a revision, @a repos_relpath, @a repos_root_url and * @a repos_uuid to the original (if a copy) or their current values. * + * If not NULL, set @a depth, to the recorded depth on @a local_abspath. + * * If @a copy_root_abspath is not NULL, and @a *is_copy indicates that the * node was copied, set @a *copy_root_abspath to the local absolute path of * the root of the copied subtree containing the node. If the copied node is @@ -519,6 +504,7 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -526,38 +512,6 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/** - * Set @a *is_deleted to TRUE if @a local_abspath is deleted, using - * @a wc_ctx. If @a local_abspath is not in the working copy, return - * @c SVN_ERR_WC_PATH_NOT_FOUND. Use @a scratch_pool for all temporary - * allocations. - */ -svn_error_t * -svn_wc__node_is_status_deleted(svn_boolean_t *is_deleted, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool); - -/** - * Set @a *deleted_ancestor_abspath to the root of the delete operation - * that deleted @a local_abspath. If @a local_abspath itself was deleted - * and has no deleted ancestor, @a *deleted_ancestor_abspath will equal - * @a local_abspath. If @a local_abspath was not deleted, - * set @a *deleted_ancestor_abspath to @c NULL. - * - * A node is considered 'deleted' if it is deleted or moved-away, and is - * not replaced. - * - * @a *deleted_ancestor_abspath is allocated in @a result_pool. - * Use @a scratch_pool for all temporary allocations. - */ -svn_error_t * -svn_wc__node_get_deleted_ancestor(const char **deleted_ancestor_abspath, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /** * Set @a *not_present to TRUE when @a local_abspath has status * svn_wc__db_status_not_present. Set @a *user_excluded to TRUE when @@ -641,7 +595,6 @@ svn_wc__node_get_base(svn_node_kind_t *kind, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t ignore_enoent, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -771,20 +724,6 @@ svn_wc__call_with_write_lock(svn_wc__with_write_lock_func_t func, } while (0) -/** - * Calculates the schedule and copied status of a node as that would - * have been stored in an svn_wc_entry_t instance. - * - * If not @c NULL, @a schedule and @a copied are set to their calculated - * values. - */ -svn_error_t * -svn_wc__node_get_schedule(svn_wc_schedule_t *schedule, - svn_boolean_t *copied, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool); - /** A callback invoked by svn_wc__prop_list_recursive(). * It is equivalent to svn_proplist_receiver_t declared in svn_client.h, * but kept private within the svn_wc__ namespace because it is used within @@ -978,15 +917,17 @@ svn_wc__get_excluded_subtrees(apr_hash_t **server_excluded_subtrees, /* Indicate in @a *is_modified whether the working copy has local * modifications, using context @a wc_ctx. - * Use @a scratch_pool for temporary allocations. * - * This function provides a subset of the functionality of - * svn_wc_revision_status2() and is more efficient if the caller - * doesn't need all information returned by svn_wc_revision_status2(). */ + * If IGNORE_UNVERSIONED, unversioned paths inside the tree rooted by + * LOCAL_ABSPATH are not seen as a change, otherwise they are. + * (svn:ignored paths are always ignored) + * + * Use @a scratch_pool for temporary allocations. */ svn_error_t * svn_wc__has_local_mods(svn_boolean_t *is_modified, svn_wc_context_t *wc_ctx, const char *local_abspath, + svn_boolean_t ignore_unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool); @@ -1294,6 +1235,44 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +typedef enum svn_wc__external_description_format_t +{ + /* LOCALPATH [-r PEG] URL */ + svn_wc__external_description_format_1 = 0, + + /* [-r REV] URL[@PEG] LOCALPATH, introduced in Subversion 1.5 */ + svn_wc__external_description_format_2 +} svn_wc__external_description_format_t; + +/* Additional information about what the external's parser has parsed. */ +typedef struct svn_wc__externals_parser_info_t +{ + /* The syntax format used by the external description. */ + svn_wc__external_description_format_t format; + + /* The string used for defining the operative revision, i.e. + "-rN", "-rHEAD", or "-r{DATE}". + NULL if revision was not given. */ + const char *rev_str; + + /* The string used for defining the peg revision (equals rev_str in + format 1, is "@N", or "@HEAD" or "@{DATE}" in format 2). + NULL if peg revision was not given. */ + const char *peg_rev_str; + +} svn_wc__externals_parser_info_t; + +/* Like svn_wc_parse_externals_description3() but returns an additional array + * with elements of type svn_wc__externals_parser_info_t in @a *parser_infos_p. + * @a parser_infos_p may be NULL if not required by the caller. + */ +svn_error_t * +svn_wc__parse_externals_description(apr_array_header_t **externals_p, + apr_array_header_t **parser_infos_p, + const char *defining_directory, + const char *desc, + svn_boolean_t canonicalize_url, + apr_pool_t *pool); /** * Set @a *editor and @a *edit_baton to an editor that generates @@ -1318,8 +1297,8 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, * * Assuming the target is a directory, then: * - * - If @a get_all is FALSE, then only locally-modified entries will be - * returned. If TRUE, then all entries will be returned. + * - If @a get_all is @c FALSE, then only locally-modified entries will be + * returned. If @c TRUE, then all entries will be returned. * * - If @a depth is #svn_depth_empty, a status structure will * be returned for the target only; if #svn_depth_files, for the @@ -1334,6 +1313,9 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, * If the given @a depth is incompatible with the depth found in a * working copy directory, the found depth always governs. * + * If @a check_working_copy is not set, do not scan the working copy + * for local modifications, taking only the BASE tree into account. + * * If @a no_ignore is set, statuses that would typically be ignored * will instead be reported. * @@ -1368,6 +1350,7 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, const char *target_basename, svn_depth_t depth, svn_boolean_t get_all, + svn_boolean_t check_working_copy, svn_boolean_t no_ignore, svn_boolean_t depth_as_sticky, svn_boolean_t server_performs_filtering, @@ -1557,7 +1540,7 @@ svn_wc__get_switch_editor(const svn_delta_editor_t **editor, * Diffs will be reported as valid relpaths, with @a anchor_abspath being * the root (""). * - * @a callbacks/@a callback_baton is the callback table to use. + * @a diff_processor will retrieve the diff report. * * If @a depth is #svn_depth_empty, just diff exactly @a target or * @a anchor_path if @a target is empty. If #svn_depth_files then do the same @@ -1583,8 +1566,12 @@ svn_wc__get_switch_editor(const svn_delta_editor_t **editor, * if they weren't modified after being copied. This allows the callbacks * to generate appropriate --git diff headers for such files. * - * Normally, the difference from repository->working_copy is shown. - * If @a reverse_order is TRUE, then show working_copy->repository diffs. + * Normally, the difference from repository->working_copy is shown. If + * @a reverse_order is TRUE, then we want to show working_copy->repository + * diffs. Most of the reversal is done by the caller; here we just swap the + * order of reporting a replacement so that the local addition is reported + * before the remote delete. (The caller's diff processor can then transform + * adds into deletes and deletes into adds, but it can't reorder the output.) * * If @a cancel_func is non-NULL, it will be used along with @a cancel_baton * to periodically check if the client has canceled the operation. @@ -1628,14 +1615,11 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, 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, + const svn_diff_tree_processor_t *diff_processor, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, @@ -1840,6 +1824,29 @@ svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool); + +/* The implemementation of svn_wc_diff6(), but reporting to a diff processor + * + * If ROOT_RELPATH is not NULL, set *ROOT_RELPATH to the target of the diff + * within the diff namespace. ("" or a single path component). + * + * If ROOT_IS_FILE is NOT NULL set it + * the first processor call. (The anchor is LOCAL_ABSPATH or an ancestor of it) + */ +svn_error_t * +svn_wc__diff7(const char **root_relpath, + svn_boolean_t *root_is_dir, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + const apr_array_header_t *changelist_filter, + const svn_diff_tree_processor_t *diff_processor, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/svn_auth.h b/contrib/subversion/subversion/include/svn_auth.h index 3e9f45e5c..3a78ba6ac 100644 --- a/contrib/subversion/subversion/include/svn_auth.h +++ b/contrib/subversion/subversion/include/svn_auth.h @@ -884,7 +884,11 @@ svn_auth_get_platform_specific_client_providers( * @note An administrative password reset may invalidate the account's * secret key. This function will detect that situation and behave as * if the password were not cached at all. + * @deprecated Provided for backwards compatibility with the 1.8 API. Use + * svn_auth_get_platform_specific_provider with provider_name of "windows" + * and provider_type of "simple". */ +SVN_DEPRECATED void svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool); @@ -906,7 +910,11 @@ svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, * @note An administrative password reset may invalidate the account's * secret key. This function will detect that situation and behave as * if the password were not cached at all. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "windows" and provider_type of "ssl_client_cert_pw". */ +SVN_DEPRECATED void svn_auth_get_windows_ssl_client_cert_pw_provider( svn_auth_provider_object_t **provider, @@ -923,7 +931,11 @@ svn_auth_get_windows_ssl_client_cert_pw_provider( * * @since New in 1.5. * @note This function is only available on Windows. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "windows" and provider_type of "ssl_server_trust". */ +SVN_DEPRECATED void svn_auth_get_windows_ssl_server_trust_provider( svn_auth_provider_object_t **provider, @@ -943,7 +955,11 @@ svn_auth_get_windows_ssl_server_trust_provider( * * @since New in 1.4 * @note This function is only available on Mac OS 10.2 and higher. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "keychain" and provider_type of "simple". */ +SVN_DEPRECATED void svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool); @@ -959,7 +975,11 @@ svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, * * @since New in 1.6 * @note This function is only available on Mac OS 10.2 and higher. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "keychain" and provider_type of "ssl_client_cert_pw". */ +SVN_DEPRECATED void svn_auth_get_keychain_ssl_client_cert_pw_provider( svn_auth_provider_object_t **provider, @@ -1029,7 +1049,11 @@ svn_auth_gnome_keyring_version(void); * @since New in 1.6 * @note This function actually works only on systems with * libsvn_auth_gnome_keyring and GNOME Keyring installed. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "gnome_keyring" and provider_type of "simple". */ +SVN_DEPRECATED void svn_auth_get_gnome_keyring_simple_provider( svn_auth_provider_object_t **provider, @@ -1056,7 +1080,11 @@ svn_auth_get_gnome_keyring_simple_provider( * @since New in 1.6 * @note This function actually works only on systems with * libsvn_auth_gnome_keyring and GNOME Keyring installed. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "gnome_keyring" and provider_type of "ssl_client_cert_pw". */ +SVN_DEPRECATED void svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider( svn_auth_provider_object_t **provider, @@ -1084,7 +1112,11 @@ svn_auth_kwallet_version(void); * @since New in 1.6 * @note This function actually works only on systems with libsvn_auth_kwallet * and KWallet installed. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "kwallet" and provider_type of "simple". */ +SVN_DEPRECATED void svn_auth_get_kwallet_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool); @@ -1102,7 +1134,11 @@ svn_auth_get_kwallet_simple_provider(svn_auth_provider_object_t **provider, * @since New in 1.6 * @note This function actually works only on systems with libsvn_auth_kwallet * and KWallet installed. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "kwallet" and provider_type of "ssl_client_cert_pw". */ +SVN_DEPRECATED void svn_auth_get_kwallet_ssl_client_cert_pw_provider( svn_auth_provider_object_t **provider, @@ -1124,7 +1160,11 @@ svn_auth_get_kwallet_ssl_client_cert_pw_provider( * @since New in 1.8 * @note This function actually works only on systems with * GNU Privacy Guard installed. + * @deprecated Provided for backwards compatibility with the 1.8 API. + * Use svn_auth_get_platform_specific_provider with provider_name + * of "gpg_agent" and provider_type of "simple". */ +SVN_DEPRECATED void svn_auth_get_gpg_agent_simple_provider (svn_auth_provider_object_t **provider, diff --git a/contrib/subversion/subversion/include/svn_cache_config.h b/contrib/subversion/subversion/include/svn_cache_config.h index b03a07914..2db619af2 100644 --- a/contrib/subversion/subversion/include/svn_cache_config.h +++ b/contrib/subversion/subversion/include/svn_cache_config.h @@ -41,6 +41,9 @@ extern "C" { /** Cache resource settings. It controls what caches, in what size and how they will be created. The settings apply for the whole process. + @note Do not extend this data structure as this would break binary + compatibility. + @since New in 1.7. */ typedef struct svn_cache_config_t @@ -56,6 +59,8 @@ typedef struct svn_cache_config_t /** is this application guaranteed to be single-threaded? */ svn_boolean_t single_threaded; + + /* DON'T add new members here. Bump struct and API version instead. */ } svn_cache_config_t; /** Get the current cache configuration. If it has not been set, diff --git a/contrib/subversion/subversion/include/svn_checksum.h b/contrib/subversion/subversion/include/svn_checksum.h index d3271f553..e332e87e3 100644 --- a/contrib/subversion/subversion/include/svn_checksum.h +++ b/contrib/subversion/subversion/include/svn_checksum.h @@ -48,7 +48,17 @@ typedef enum svn_checksum_kind_t svn_checksum_md5, /** The checksum is (or should be set to) a SHA1 checksum. */ - svn_checksum_sha1 + svn_checksum_sha1, + + /** The checksum is (or should be set to) a FNV-1a 32 bit checksum, + * in big endian byte order. + * @since New in 1.9. */ + svn_checksum_fnv1a_32, + + /** The checksum is (or should be set to) a modified FNV-1a 32 bit, + * in big endian byte order. + * @since New in 1.9. */ + svn_checksum_fnv1a_32x4 } svn_checksum_kind_t; /** diff --git a/contrib/subversion/subversion/include/svn_client.h b/contrib/subversion/subversion/include/svn_client.h index 5db3e1689..cb0f49d98 100644 --- a/contrib/subversion/subversion/include/svn_client.h +++ b/contrib/subversion/subversion/include/svn_client.h @@ -465,13 +465,19 @@ typedef struct svn_client_commit_item3_t { /* IMPORTANT: If you extend this structure, add new fields to the end. */ - /** absolute working-copy path of item */ + /** absolute working-copy path of item. Always set during normal commits + * (and copies from a working copy) to the repository. Can only be NULL + * when stub commit items are created for operations that only involve + * direct repository operations. During WC->REPOS copy operations, this + * path is the WC source path of the operation. */ const char *path; /** node kind (dir, file) */ svn_node_kind_t kind; - /** commit URL for this item */ + /** commit URL for this item. Points to the repository location of PATH + * during commits, or to the final URL of the item when copying from the + * working copy to the repository. */ const char *url; /** revision of textbase */ @@ -1014,6 +1020,31 @@ typedef struct svn_client_ctx_t * @since New in 1.7. */ svn_wc_context_t *wc_ctx; + /** Check-tunnel callback + * + * If not @c NULL, and open_tunnel_func is also not @c NULL, this + * callback will be invoked to check if open_tunnel_func should be + * used to create a specific tunnel, or if the default tunnel + * implementation (either built-in or configured in the client + * configuration file) should be used instead. + * @since New in 1.9. + */ + svn_ra_check_tunnel_func_t check_tunnel_func; + + /** Open-tunnel callback + * + * If not @c NULL, this callback will be invoked to create a tunnel + * for a ra_svn connection that needs one, overriding any tunnel + * definitions in the client config file. This callback is used only + * for ra_svn and ignored by the other RA modules. + * @since New in 1.9. + */ + svn_ra_open_tunnel_func_t open_tunnel_func; + + /** The baton used with check_tunnel_func and open_tunnel_func. + * @since New in 1.9. + */ + void *tunnel_baton; } svn_client_ctx_t; /** Initialize a client context. @@ -1051,20 +1082,15 @@ svn_client_create_context(svn_client_ctx_t **ctx, /** @} end group: Client context management */ /** - * @name Authentication information file names - * - * Names of files that contain authentication information. - * - * These filenames are decided by libsvn_client, since this library - * implements all the auth-protocols; libsvn_wc does nothing but - * blindly store and retrieve these files from protected areas. - * - * @defgroup clnt_auth_filenames Client authentication file names - * @{ + * @deprecated Provided for backward compatibility. This constant was never + * used in released versions. */ #define SVN_CLIENT_AUTH_USERNAME "username" +/** + * @deprecated Provided for backward compatibility. This constant was never + * used in released versions. + */ #define SVN_CLIENT_AUTH_PASSWORD "password" -/** @} group end: Authentication information file names */ /** Client argument processing * @@ -1100,6 +1126,12 @@ svn_client_create_context(svn_client_ctx_t **ctx, * error, and if this is the only type of error encountered, complete * the operation before returning the error(s). * + * Return an error if a target is just a peg specifier with no path, such as + * "@abc". Before v1.6.5 (r878062) this form was interpreted as a literal path; + * it is now ambiguous. The form "@abc@" should now be used to refer to the + * literal path "@abc" with no peg revision, or the form ".@abc" to refer to + * the empty path with peg revision "abc". + * * @since New in 1.7 */ svn_error_t * @@ -1114,6 +1146,9 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p, * Similar to svn_client_args_to_target_array2() but with * @a keep_last_origpath_on_truepath_collision always set to FALSE. * + * @since Since 1.6.5, this returns an error if a path contains a peg + * specifier with no path before it, such as "@abc". + * * @deprecated Provided for backward compatibility with the 1.6 API. */ SVN_DEPRECATED @@ -2471,12 +2506,20 @@ typedef svn_error_t *(*svn_client_status_func_t)( * retrieve only "interesting" entries (local mods and/or * out of date). * - * - If @a update is set, contact the repository and augment the - * status structures with information about out-of-dateness (with - * respect to @a revision). Also, if @a result_rev is not @c NULL, - * set @a *result_rev to the actual revision against which the - * working copy was compared (@a *result_rev is not meaningful unless - * @a update is set). + * - If @a check_out_of_date is set, contact the repository and + * augment the status structures with information about + * out-of-dateness (with respect to @a revision). Also, if @a + * result_rev is not @c NULL, set @a *result_rev to the actual + * revision against which the working copy was compared (@a + * *result_rev is not meaningful unless @a check_out_of_date is + * set). + * + * - If @a check_working_copy is not set, do not scan the working + * copy for local modifications. This parameter will be ignored + * unless @a check_out_of_date is set. When set, the status + * report will not contain any information about local changes in + * the working copy; this includes local deletions and + * replacements. * * If @a no_ignore is @c FALSE, don't report any file or directory (or * recurse into any directory) that is found by recursion (as opposed to @@ -2507,8 +2550,34 @@ typedef svn_error_t *(*svn_client_status_func_t)( * * All temporary allocations are performed in @a scratch_pool. * + * @since New in 1.9. + */ +svn_error_t * +svn_client_status6(svn_revnum_t *result_rev, + svn_client_ctx_t *ctx, + const char *path, + const svn_opt_revision_t *revision, + svn_depth_t depth, + svn_boolean_t get_all, + svn_boolean_t check_out_of_date, + svn_boolean_t check_working_copy, + svn_boolean_t no_ignore, + svn_boolean_t ignore_externals, + svn_boolean_t depth_as_sticky, + const apr_array_header_t *changelists, + svn_client_status_func_t status_func, + void *status_baton, + apr_pool_t *scratch_pool); + + +/** + * Same as svn_client_status6(), but with @a check_out_of_date set to + * @a update and @a check_working_copy set to @c TRUE. + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_client_status5(svn_revnum_t *result_rev, svn_client_ctx_t *ctx, @@ -2641,8 +2710,8 @@ svn_client_status(svn_revnum_t *result_rev, * #svn_opt_revision_unspecified, it defaults to #svn_opt_revision_head * for URLs or #svn_opt_revision_working for WC paths. * - * If @a limit is non-zero only invoke @a receiver on the first @a limit - * logs. + * If @a limit is greater than zero only invoke @a receiver on the first + * @a limit logs. * * If @a discover_changed_paths is set, then the @c changed_paths and @c * changed_paths2 fields in the @c log_entry argument to @a receiver will be @@ -2814,6 +2883,11 @@ svn_client_log(const apr_array_header_t *targets, * in which case blame information will be generated regardless of the * MIME types of the revisions. * + * @a start may resolve to a revision number greater (younger) than @a end + * only if the server is 1.8.0 or greater (supports + * #SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) and the client is 1.9.0 or + * newer. + * * Use @a diff_options to determine how to compare different revisions of the * target. * @@ -3919,8 +3993,15 @@ svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo, * If a depth other than #svn_depth_empty or #svn_depth_infinity is * requested then return a #SVN_ERR_UNSUPPORTED_FEATURE error. * - * @a discover_changed_paths and @a revprops are the same as for - * svn_client_log5(). Use @a scratch_pool for all temporary allocations. + * In addition to the behavior of @a discover_changed_paths described in + * svn_client_log5(), if set to TRUE it enables detection of sub-tree + * merges that are complete but can't be detected as complete without + * access to the changed paths. Sub-tree merges detected as complete will + * be included if @a finding_merged is TRUE or filtered if @a finding_merged + * is FALSE. + * + * @a revprops is the same as for svn_client_log5(). Use @a scratch_pool for + * all temporary allocations. * * @a ctx is a context used for authentication. * @@ -4017,17 +4098,96 @@ svn_client_mergeinfo_log_eligible(const char *path_or_url, * @{ */ -/** Recursively cleanup a working copy directory @a dir, finishing any +/** Recursively vacuum a working copy directory @a dir_abspath, + * removing unnecessary data. + * + * If @a include_externals is @c TRUE, recurse into externals and vacuum them + * as well. + * + * If @a remove_unversioned_items is @c TRUE, remove unversioned items + * in @a dir_abspath after successful working copy cleanup. + * If @a remove_ignored_items is @c TRUE, remove ignored unversioned items + * in @a dir_abspath after successful working copy cleanup. + * + * If @a fix_recorded_timestamps is @c TRUE, this function fixes recorded + * timestamps for unmodified files in the working copy, reducing comparision + * time on future checks. + * + * If @a vacuum_pristines is @c TRUE, and @a dir_abspath points to the working + * copy root unreferenced files in the pristine store are removed. + * + * When asked to remove unversioned or ignored items, and the working copy + * is already locked, return #SVN_ERR_WC_LOCKED. This prevents accidental + * working copy corruption in case users run the cleanup operation to + * remove unversioned items while another client is performing some other + * operation on the working copy. + * + * If @a ctx->cancel_func is non-NULL, invoke it with @a + * ctx->cancel_baton at various points during the operation. If it + * returns an error (typically #SVN_ERR_CANCELLED), return that error + * immediately. + * + * Use @a scratch_pool for any temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client_vacuum(const char *dir_abspath, + svn_boolean_t remove_unversioned_items, + svn_boolean_t remove_ignored_items, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t vacuum_pristines, + svn_boolean_t include_externals, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + + +/** Recursively cleanup a working copy directory @a dir_abspath, finishing any * incomplete operations, removing lockfiles, etc. * + * If @a break_locks is @c TRUE, existing working copy locks at or below @a + * dir_abspath are broken, otherwise a normal write lock is obtained. + * + * If @a fix_recorded_timestamps is @c TRUE, this function fixes recorded + * timestamps for unmodified files in the working copy, reducing comparision + * time on future checks. + * + * If @a clear_dav_cache is @c TRUE, the caching of DAV information for older + * mod_dav served repositories is cleared. This clearing invalidates some + * cached information used for pre-HTTPv2 repositories. + * + * If @a vacuum_pristines is @c TRUE, and @a dir_abspath points to the working + * copy root unreferenced files in the pristine store are removed. + * + * If @a include_externals is @c TRUE, recurse into externals and clean + * them up as well. + * * If @a ctx->cancel_func is non-NULL, invoke it with @a * ctx->cancel_baton at various points during the operation. If it * returns an error (typically #SVN_ERR_CANCELLED), return that error * immediately. * * Use @a scratch_pool for any temporary allocations. + * + * @since New in 1.9. */ svn_error_t * +svn_client_cleanup2(const char *dir_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, + svn_boolean_t include_externals, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +/** Like svn_client_cleanup2(), but no support for not breaking locks and + * cleaning up externals and using a potentially non absolute path. + * + * @deprecated Provided for limited backwards compatibility with the 1.8 API. + */ +SVN_DEPRECATED +svn_error_t * svn_client_cleanup(const char *dir, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool); @@ -4133,6 +4293,13 @@ svn_client_relocate(const char *dir, * changelists. If @a changelists is empty (or altogether @c NULL), * no changelist filtering occurs. * + * If @a clear_changelists is TRUE, then changelist information for the + * paths is cleared while reverting. + * + * If @a metadata_only is TRUE, the files and directories aren't changed + * by the operation. If there are conflict marker files attached to the + * targets these are removed. + * * If @a ctx->notify_func2 is non-NULL, then for each item reverted, * call @a ctx->notify_func2 with @a ctx->notify_baton2 and the path of * the reverted item. @@ -4141,8 +4308,24 @@ svn_client_relocate(const char *dir, * then do not error, just invoke @a ctx->notify_func2 with @a * ctx->notify_baton2, using notification code #svn_wc_notify_skip. * + * @since New in 1.9. + */ +svn_error_t * +svn_client_revert3(const apr_array_header_t *paths, + svn_depth_t depth, + const apr_array_header_t *changelists, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** Similar to svn_client_revert2, but with @a clear_changelists set to + * FALSE and @a metadata_only set to FALSE. + * * @since New in 1.5. + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_client_revert2(const apr_array_header_t *paths, svn_depth_t depth, @@ -4160,7 +4343,7 @@ svn_client_revert2(const apr_array_header_t *paths, * @note Most APIs map @a recurse==FALSE to @a depth==svn_depth_files; * revert is deliberately different. * - * @deprecated Provided for backwards compatibility with the 1.0 API. + * @deprecated Provided for backwards compatibility with the 1.4 API. */ SVN_DEPRECATED svn_error_t * @@ -4316,6 +4499,41 @@ typedef struct svn_client_copy_source_t * If @a ignore_externals is set, don't process externals definitions * as part of this operation. * + * If @a metadata_only is @c TRUE and copying a file in a working copy, + * everything in the metadata is updated as if the node is moved, but the + * actual disk copy operation is not performed. This feature is useful for + * clients that want to keep the working copy in sync while the actual working + * copy is updated by some other task. + * + * If @a pin_externals is set, pin URLs in copied externals definitions + * to their current revision unless they were already pinned to a + * particular revision. A pinned external uses a URL which points at a + * fixed revision, rather than the HEAD revision. Externals in the copy + * destination are pinned to either a working copy base revision or the + * HEAD revision of a repository (as of the time the copy operation is + * performed), depending on the type of the copy source: +
+    copy source: working copy (WC)       REPOS
+   ------------+------------------------+---------------------------+
+    copy    WC | external's WC BASE rev | external's repos HEAD rev |
+    dest:      |------------------------+---------------------------+
+         REPOS | external's WC BASE rev | external's repos HEAD rev |
+   ------------+------------------------+---------------------------+
+ 
+ * If the copy source is a working copy, then all externals must be checked + * out, be at a single-revision, contain no local modifications, and contain + * no switched subtrees. Else, #SVN_ERR_WC_PATH_UNEXPECTED_STATUS is returned. + * + * If non-NULL, @a externals_to_pin restricts pinning to a subset of externals. + * It is a hash table keyed by either a local absolute path or a URL at which + * an svn:externals property is set. The hash table contains apr_array_header_t* + * elements as returned by svn_wc_parse_externals_description3(). These arrays + * contain elements of type svn_wc_external_item2_t*, each of which corresponds + * to a single line of an svn:externals definition. Externals corresponding to + * these items will be pinned, other externals will not be pinned. + * If @a externals_to_pin is @c NULL then all externals are pinned. + * If @a pin_externals is @c FALSE then @a externals_to_pin is ignored. + * * If non-NULL, @a revprop_table is a hash table holding additional, * custom revision properties (const char * names mapped to * svn_string_t * values) to be set on the new revision in @@ -4334,8 +4552,32 @@ typedef struct svn_client_copy_source_t * @a commit_callback with @a commit_baton and a #svn_commit_info_t for * the commit. * + * @since New in 1.9. + */ +svn_error_t * +svn_client_copy7(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, + svn_boolean_t metadata_only, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, + const apr_hash_t *revprop_table, + svn_commit_callback2_t commit_callback, + void *commit_baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** + * Similar to svn_client_copy7(), but doesn't support meta_data_only + * and cannot pin externals. + * + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_client_copy6(const apr_array_header_t *sources, const char *dst_path, @@ -5065,6 +5307,8 @@ svn_client_propget(apr_hash_t **props, * in @a ctx for authentication, and @a pool for all memory allocation. * Return the actual rev queried in @a *set_rev. * + * If @a propname does not exist on @a revision, set @a *propval to @c NULL. + * * Note that unlike its cousin svn_client_propget(), this routine * doesn't affect the working copy at all; it's a pure network * operation that queries an *unversioned* property attached to a @@ -5616,15 +5860,14 @@ svn_client_ls(apr_hash_t **dirents, /** * Output the content of a file. * - * @param[in] out The stream to which the content will be written. - * @param[in] path_or_url The path or URL of the file. - * @param[in] peg_revision The peg revision. - * @param[in] revision The operative revision. + * @param[out] props Optional output argument to obtain properties. + * @param[in] out The stream to which the content will be written. + * @param[in] path_or_url The path or URL of the file. + * @param[in] peg_revision The peg revision. + * @param[in] revision The operative revision. + * @param[in] expand_keywords When true, keywords (when set) are expanded. * @param[in] ctx The standard client context, used for possible * authentication. - * @param[in] pool Used for any temporary allocation. - * - * @todo Add an expansion/translation flag? * * @return A pointer to an #svn_error_t of the type (this list is not * exhaustive):
@@ -5634,12 +5877,35 @@ svn_client_ls(apr_hash_t **dirents, * determined.
* If no error occurred, return #SVN_NO_ERROR. * - * @since New in 1.2. + * If @a *props is not NULL it is set to a hash of all the file's + * non-inherited properties. If it is NULL, the properties are only + * used for determining how and if the file should be translated. * * @see #svn_client_ctx_t
@ref clnt_revisions for * a discussion of operative and peg revisions. + * + * @since New in 1.9. */ svn_error_t * +svn_client_cat3(apr_hash_t **props, + svn_stream_t *out, + const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_boolean_t expand_keywords, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Similar to svn_client_cat3() except without the option of directly + * reading the properties, and with @a expand_keywords always TRUE. + * + * @since New in 1.2. + * @deprecated Provided for backward compatibility with the 1.8 API. + */ +SVN_DEPRECATED +svn_error_t * svn_client_cat2(svn_stream_t *out, const char *path_or_url, const svn_opt_revision_t *peg_revision, @@ -6110,14 +6376,38 @@ typedef svn_error_t *(*svn_client_info_receiver2_t)( * is TRUE also send nodes that don't exist as versioned but are still * tree conflicted. * + * If @a include_externals is @c TRUE, recurse into externals and report about + * them as well. + * * @a changelists is an array of const char * changelist * names, used as a restrictive filter on items whose info is * reported; that is, don't report info about any item unless * it's a member of one of those changelists. If @a changelists is * empty (or altogether @c NULL), no changelist filtering occurs. + * + * @since New in 1.9. + */ +svn_error_t * +svn_client_info4(const char *abspath_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + svn_boolean_t fetch_excluded, + svn_boolean_t fetch_actual_only, + svn_boolean_t include_externals, + const apr_array_header_t *changelists, + svn_client_info_receiver2_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + + +/** Similar to svn_client_info4, but doesn't support walking externals. * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_client_info3(const char *abspath_or_url, const svn_opt_revision_t *peg_revision, diff --git a/contrib/subversion/subversion/include/svn_cmdline.h b/contrib/subversion/subversion/include/svn_cmdline.h index 80442e454..923aeee62 100644 --- a/contrib/subversion/subversion/include/svn_cmdline.h +++ b/contrib/subversion/subversion/include/svn_cmdline.h @@ -320,8 +320,28 @@ svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, * by the command line client. * * @a non_interactive, @a username, @a password, @a config_dir, - * @a no_auth_cache, and @a trust_server_cert are the values of the - * command line options of the corresponding names. + * and @a no_auth_cache are the values of the command line options + * of the corresponding names. + * + * If @a non_interactive is @c TRUE, then the following parameters + * control whether an invalid SSL certificate will be accepted + * regardless of a specific verification failure: + * + * @a trust_server_cert_unknown_ca: If @c TRUE, accept certificates + * from unknown certificate authorities. + * + * @a trust_server_cert_cn_mismatch: If @c TRUE, accept certificates + * even if the Common Name attribute of the certificate differs from + * the hostname of the server. + * + * @a trust_server_cert_expired: If @c TRUE, accept certificates even + * if they are expired. + * + * @a trust_server_cert_not_yet_valid: If @c TRUE, accept certificates + * from the future. + * + * @a trust_server_cert_other_failure: If @c TRUE, accept certificates + * even if any other verification failure than the above occured. * * @a cfg is the @c SVN_CONFIG_CATEGORY_CONFIG configuration, and * @a cancel_func and @a cancel_baton control the cancellation of the @@ -329,6 +349,29 @@ svn_cmdline_auth_plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, * * Use @a pool for all allocations. * + * @since New in 1.9. + */ +svn_error_t * +svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, + svn_boolean_t non_interactive, + const char *username, + const char *password, + const char *config_dir, + svn_boolean_t no_auth_cache, + svn_boolean_t trust_server_cert_unknown_ca, + svn_boolean_t trust_server_cert_cn_mismatch, + svn_boolean_t trust_server_cert_expired, + svn_boolean_t trust_server_cert_not_yet_valid, + svn_boolean_t trust_server_cert_other_failure, + svn_config_t *cfg, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool); + +/* Like svn_cmdline_create_auth_baton2, but with only one trust_server_cert + * option which corresponds to trust_server_cert_unknown_ca. + * + * @deprecated Provided for backward compatibility with the 1.8 API. * @since New in 1.6. */ svn_error_t * diff --git a/contrib/subversion/subversion/include/svn_compat.h b/contrib/subversion/subversion/include/svn_compat.h index a127125c7..35bbe1978 100644 --- a/contrib/subversion/subversion/include/svn_compat.h +++ b/contrib/subversion/subversion/include/svn_compat.h @@ -32,6 +32,7 @@ #include #include "svn_types.h" +#include "svn_string.h" #ifdef __cplusplus extern "C" { @@ -75,6 +76,17 @@ svn_compat_log_revprops_in(apr_pool_t *pool); * revprops is NULL, all return values are NULL. Any return value may be * NULL if the corresponding property is not set in @a revprops. * + * @since New in 1.9. + */ +void +svn_compat_log_revprops_out_string(const svn_string_t **author, + const svn_string_t **date, + const svn_string_t **message, + apr_hash_t *revprops); + +/** Simiar to svn_compat_log_revprops_out_string() but returns C-style strings + * instead of #svn_string_t. + * * @since New in 1.5. */ void diff --git a/contrib/subversion/subversion/include/svn_config.h b/contrib/subversion/subversion/include/svn_config.h index a3fa9df93..5ad3b0bae 100644 --- a/contrib/subversion/subversion/include/svn_config.h +++ b/contrib/subversion/subversion/include/svn_config.h @@ -76,18 +76,27 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS "http-proxy-exceptions" #define SVN_CONFIG_OPTION_HTTP_TIMEOUT "http-timeout" #define SVN_CONFIG_OPTION_HTTP_COMPRESSION "http-compression" +/** @deprecated Not used since 1.8. */ #define SVN_CONFIG_OPTION_NEON_DEBUG_MASK "neon-debug-mask" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_HTTP_AUTH_TYPES "http-auth-types" #define SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES "ssl-authority-files" #define SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA "ssl-trust-default-ca" #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE "ssl-client-cert-file" #define SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD "ssl-client-cert-password" +/** @deprecated Not used since 1.8. + * @since New in 1.5. */ #define SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER "ssl-pkcs11-provider" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_HTTP_LIBRARY "http-library" +/** @since New in 1.1. */ #define SVN_CONFIG_OPTION_STORE_PASSWORDS "store-passwords" +/** @since New in 1.6. */ #define SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS "store-plaintext-passwords" #define SVN_CONFIG_OPTION_STORE_AUTH_CREDS "store-auth-creds" +/** @since New in 1.6. */ #define SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP "store-ssl-client-cert-pp" +/** @since New in 1.6. */ #define SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT \ "store-ssl-client-cert-pp-plaintext" #define SVN_CONFIG_OPTION_USERNAME "username" @@ -95,6 +104,14 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_HTTP_BULK_UPDATES "http-bulk-updates" /** @since New in 1.8. */ #define SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS "http-max-connections" +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests" + +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS "serf-log-components" +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_SERF_LOG_LEVEL "serf-log-level" + #define SVN_CONFIG_CATEGORY_CONFIG "config" #define SVN_CONFIG_SECTION_AUTH "auth" @@ -115,6 +132,7 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_DIFF_EXTENSIONS "diff-extensions" #define SVN_CONFIG_OPTION_DIFF3_CMD "diff3-cmd" #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG "diff3-has-program-arg" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD "merge-tool-cmd" #define SVN_CONFIG_SECTION_MISCELLANY "miscellany" #define SVN_CONFIG_OPTION_GLOBAL_IGNORES "global-ignores" @@ -123,13 +141,20 @@ typedef struct svn_config_t svn_config_t; /** @deprecated Not used by Subversion since 2003/r847039 (well before 1.0) */ #define SVN_CONFIG_OPTION_TEMPLATE_ROOT "template-root" #define SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS "enable-auto-props" +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE "enable-magic-file" +/** @since New in 1.2. */ #define SVN_CONFIG_OPTION_NO_UNLOCK "no-unlock" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_MIMETYPES_FILE "mime-types-file" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_PRESERVED_CF_EXTS "preserved-conflict-file-exts" /** @since New in 1.7. */ #define SVN_CONFIG_OPTION_INTERACTIVE_CONFLICTS "interactive-conflicts" /** @since New in 1.7. */ #define SVN_CONFIG_OPTION_MEMORY_CACHE_SIZE "memory-cache-size" +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_DIFF_IGNORE_CONTENT_TYPE "diff-ignore-content-type" #define SVN_CONFIG_SECTION_TUNNELS "tunnels" #define SVN_CONFIG_SECTION_AUTO_PROPS "auto-props" /** @since New in 1.8. */ @@ -138,6 +163,8 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE "exclusive-locking" /** @since New in 1.8. */ #define SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE_CLIENTS "exclusive-locking-clients" +/** @since New in 1.9. */ +#define SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT "busy-timeout" /** @} */ /** @name Repository conf directory configuration files strings @@ -158,9 +185,13 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG_OPTION_FORCE_USERNAME_CASE "force-username-case" /** @since New in 1.8. */ #define SVN_CONFIG_OPTION_HOOKS_ENV "hooks-env" +/** @since New in 1.5. */ #define SVN_CONFIG_SECTION_SASL "sasl" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_USE_SASL "use-sasl" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_MIN_SSF "min-encryption" +/** @since New in 1.5. */ #define SVN_CONFIG_OPTION_MAX_SSF "max-encryption" /* For repository password database */ @@ -177,7 +208,7 @@ typedef struct svn_config_t svn_config_t; #define SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_1 \ "*.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__" #define SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_2 \ - "*.rej *~ #*# .#* .*.swp .DS_Store" + "*.rej *~ #*# .#* .*.swp .DS_Store [Tt]humbs.db" #endif #define SVN_CONFIG_DEFAULT_GLOBAL_IGNORES \ @@ -659,11 +690,67 @@ svn_config_ensure(const char *config_dir, */ -/** A hash-key pointing to a realmstring. Every file containing - * authentication data should have this key. +/** + * Attributes of authentication credentials. + * + * The values of these keys are C strings. + * + * @note Some of these hash keys were also used in versions < 1.9 but were + * not part of the public API (except #SVN_CONFIG_REALMSTRING_KEY which + * has been present since 1.0). + * + * @defgroup cached_authentication_data_attributes Cached authentication data attributes + * @{ + */ + +/** A hash-key pointing to a realmstring. This attribute is mandatory. + * + * @since New in 1.0. */ #define SVN_CONFIG_REALMSTRING_KEY "svn:realmstring" +/** A hash-key for usernames. + * @since New in 1.9. + */ +#define SVN_CONFIG_AUTHN_USERNAME_KEY "username" + +/** A hash-key for passwords. + * The password may be in plaintext or encrypted form, depending on + * the authentication provider. + * @since New in 1.9. + */ +#define SVN_CONFIG_AUTHN_PASSWORD_KEY "password" + +/** A hash-key for passphrases, + * such as SSL client ceritifcate passphrases. The passphrase may be in + * plaintext or encrypted form, depending on the authentication provider. + * @since New in 1.9. + */ +#define SVN_CONFIG_AUTHN_PASSPHRASE_KEY "passphrase" + +/** A hash-key for the type of a password or passphrase. The type + * indicates which provider owns the credential. + * @since New in 1.9. + */ +#define SVN_CONFIG_AUTHN_PASSTYPE_KEY "passtype" + +/** A hash-key for SSL certificates. The value is the base64-encoded DER form + * certificate. + * @since New in 1.9. + * @note The value is not human readable. + */ +#define SVN_CONFIG_AUTHN_ASCII_CERT_KEY "ascii_cert" + +/** A hash-key for recorded SSL certificate verification + * failures. Failures encoded as an ASCII integer containing any of the + * SVN_AUTH_SSL_* SSL server certificate failure bits defined in svn_auth.h. + * @since New in 1.9. + */ +#define SVN_CONFIG_AUTHN_FAILURES_KEY "failures" + + +/** @} */ + /** Use @a cred_kind and @a realmstring to locate a file within the * ~/.subversion/auth/ area. If the file exists, initialize @a *hash * and load the file contents into the hash, using @a pool. If the @@ -712,7 +799,8 @@ svn_config_write_auth_data(apr_hash_t *hash, * fully purged) to allow perusal and selective removal of credentials. * * @a cred_kind and @a realmstring specify the key of the credential. - * @a hash contains the hash data associated with the record. + * @a hash contains the hash data associated with the record. @a walk_baton + * is the baton passed to svn_config_walk_auth_data(). * * Before returning set @a *delete_cred to TRUE to remove the credential from * the cache; leave @a *delete_cred unchanged or set it to FALSE to keep the @@ -728,7 +816,7 @@ svn_config_write_auth_data(apr_hash_t *hash, */ typedef svn_error_t * (*svn_config_auth_walk_func_t)(svn_boolean_t *delete_cred, - void *cleanup_baton, + void *walk_baton, const char *cred_kind, const char *realmstring, apr_hash_t *hash, @@ -747,7 +835,7 @@ typedef svn_error_t * * * @note Removing credentials from the config-based disk store will * not purge them from any open svn_auth_baton_t instance. Consider - * using svn_auth_forget_credentials() -- from the @a cleanup_func, + * using svn_auth_forget_credentials() -- from the @a walk_func, * even -- for this purpose. * * @note Removing credentials from the config-based disk store will @@ -799,7 +887,7 @@ svn_config_get_user_config_path(const char **path, */ svn_error_t * svn_config_dup(svn_config_t **cfgp, - svn_config_t *src, + const svn_config_t *src, apr_pool_t *pool); /** Create a deep copy of the config hash @a src_hash and return diff --git a/contrib/subversion/subversion/include/svn_delta.h b/contrib/subversion/subversion/include/svn_delta.h index 7df7f3f5f..d949ced66 100644 --- a/contrib/subversion/subversion/include/svn_delta.h +++ b/contrib/subversion/subversion/include/svn_delta.h @@ -249,6 +249,7 @@ svn_txdelta_compose_windows(const svn_txdelta_window_t *window_A, * * @since New in 1.4 * + * @since Since 1.9, @a tbuf may be NULL if @a *tlen is 0. */ void svn_txdelta_apply_instructions(svn_txdelta_window_t *window, @@ -545,10 +546,26 @@ svn_txdelta_to_svndiff(svn_stream_t *output, /** Return a writable generic stream which will parse svndiff-format * data into a text delta, invoking @a handler with @a handler_baton - * whenever a new window is ready. If @a error_on_early_close is @c - * TRUE, attempting to close this stream before it has handled the entire - * svndiff data set will result in #SVN_ERR_SVNDIFF_UNEXPECTED_END, - * else this error condition will be ignored. + * whenever a new window is ready. + * + * When the caller closes this stream, this will signal completion to + * the window handler by invoking @a handler once more, passing zero for + * the @c window argument. + * + * If @a error_on_early_close is @c TRUE, then attempt to avoid + * signaling completion to the window handler if the delta was + * incomplete. Specifically, attempting to close the stream will be + * successful only if the data written to the stream consisted of one or + * more complete windows of svndiff data and no extra bytes. Otherwise, + * closing the stream will not signal completion to the window handler, + * and will return a #SVN_ERR_SVNDIFF_UNEXPECTED_END error. Note that if + * no data at all was written, the delta is considered incomplete. + * + * If @a error_on_early_close is @c FALSE, closing the stream will + * signal completion to the window handler, regardless of how much data + * was written, and discard any pending incomplete data. + * + * Allocate the stream in @a pool. */ svn_stream_t * svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler, diff --git a/contrib/subversion/subversion/include/svn_diff.h b/contrib/subversion/subversion/include/svn_diff.h index ac9f8fcb4..5b8f8d7fc 100644 --- a/contrib/subversion/subversion/include/svn_diff.h +++ b/contrib/subversion/subversion/include/svn_diff.h @@ -55,6 +55,7 @@ #include "svn_types.h" #include "svn_io.h" /* for svn_stream_t */ #include "svn_string.h" +#include "svn_mergeinfo.h" #ifdef __cplusplus extern "C" { @@ -402,12 +403,32 @@ typedef enum svn_diff_conflict_display_style_t /** Like svn_diff_conflict_display_modified_original_latest, but *only* showing conflicts. */ svn_diff_conflict_display_only_conflicts + + /* IMPORTANT: If you extend this enum note that it is mapped in + tools/diff/diff3.c. */ } svn_diff_conflict_display_style_t; /** Given a vtable of @a output_fns/@a output_baton for consuming * differences, output the differences in @a diff. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * @since New in 1.9. + */ +svn_error_t * +svn_diff_output2(svn_diff_t *diff, + void *output_baton, + const svn_diff_output_fns_t *output_fns, + svn_cancel_func_t cancel_func, + void *cancel_baton); + +/** Similar to svn_diff_output2(), but without cancel support. + * + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_output(svn_diff_t *diff, void *output_baton, @@ -457,8 +478,15 @@ typedef struct svn_diff_file_options_t * of the nearest preceding line that starts with a character that might be * the initial character of a C language identifier. The default is * @c FALSE. + * @since New in 1.5. */ svn_boolean_t show_c_function; + + /** The number of context lines produced above and below modifications, if + * available. The number of context lines must be >= 0. + * + * @since New in 1.9 */ + int context_size; } svn_diff_file_options_t; /** Allocate a @c svn_diff_file_options_t structure in @a pool, initializing @@ -481,6 +509,7 @@ svn_diff_file_options_create(apr_pool_t *pool); * - --ignore-all-space, -w * - --ignore-eol-style * - --show-c-function, -p @since New in 1.5. + * - --context, -U ARG @since New in 1.9. * - --unified, -u (for compatibility, does nothing). */ svn_error_t * @@ -585,8 +614,6 @@ svn_diff_file_diff4(svn_diff_t **diff, /** A convenience function to produce unified diff output from the * diff generated by svn_diff_file_diff(). * - * @since New in 1.5. - * * Output a @a diff between @a original_path and @a modified_path in unified * context diff format to @a output_stream. Optionally supply * @a original_header and/or @a modified_header to be displayed in the header @@ -599,7 +626,38 @@ svn_diff_file_diff4(svn_diff_t **diff, * @a relative_to_dir is not @c NULL but @a relative_to_dir is not a parent * path of the target, an error is returned. Finally, if @a relative_to_dir * is a URL, an error will be returned. + * + * If @a context_size is not negative, then this number of context lines + * will be used in the generated diff output. Otherwise the legacy compile + * time default will be used. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * @since New in 1.9. + */ +svn_error_t * +svn_diff_file_output_unified4(svn_stream_t *output_stream, + svn_diff_t *diff, + const char *original_path, + const char *modified_path, + const char *original_header, + const char *modified_header, + const char *header_encoding, + const char *relative_to_dir, + svn_boolean_t show_c_function, + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** Similar to svn_diff_file_output_unified4(), but without cancel + * support and with @a context_size set to -1. + * + * @since New in 1.5. + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_file_output_unified3(svn_stream_t *output_stream, svn_diff_t *diff, @@ -653,10 +711,36 @@ svn_diff_file_output_unified(svn_stream_t *output_stream, * @a conflict_latest to be displayed as conflict markers in the output. * If @a conflict_original, @a conflict_modified, @a conflict_latest and/or * @a conflict_separator is @c NULL, a default marker will be displayed. - * @a conflict_style dictates how conflicts are displayed. + * @a conflict_style dictates how conflicts are displayed. + * Uses @a scratch_pool for temporary allocations. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * @since New in 1.9. + */ +svn_error_t * +svn_diff_file_output_merge3(svn_stream_t *output_stream, + svn_diff_t *diff, + const char *original_path, + const char *modified_path, + const char *latest_path, + const char *conflict_original, + const char *conflict_modified, + const char *conflict_latest, + const char *conflict_separator, + svn_diff_conflict_display_style_t conflict_style, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** Similar to svn_diff_file_output_merge3, but without cancel support. * * @since New in 1.6. + * + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_file_output_merge2(svn_stream_t *output_stream, svn_diff_t *diff, @@ -700,7 +784,28 @@ svn_diff_file_output_merge(svn_stream_t *output_stream, svn_boolean_t display_resolved_conflicts, apr_pool_t *pool); - +/** Creates a git-like binary diff hunk describing the differences between + * @a original and @a latest. It does this by either producing either the + * literal content of both versions in a compressed format, or by describing + * one way transforms. + * + * Either @a original or @a latest may be NULL to describe that the version + * didn't exist. + * + * Writes the output to @a output_stream. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * @since New in 1.9. + */ +svn_error_t * +svn_diff_output_binary(svn_stream_t *output_stream, + svn_stream_t *original, + svn_stream_t *latest, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); /* Diffs on in-memory structures */ @@ -762,8 +867,40 @@ svn_diff_mem_string_diff4(svn_diff_t **diff, * final line use the text "\ No newline at end of property" instead of * "\ No newline at end of file". * + * If @a context_size is not negative, then this number of context lines + * will be used in the generated diff output. Otherwise the legacy compile + * time default will be used. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * Uses @a scratch_pool for temporary allocations. + * + * @since New in 1.9 + */ +svn_error_t * +svn_diff_mem_string_output_unified3(svn_stream_t *output_stream, + svn_diff_t *diff, + svn_boolean_t with_diff_header, + const char *hunk_delimiter, + const char *original_header, + const char *modified_header, + const char *header_encoding, + const svn_string_t *original, + const svn_string_t *modified, + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** Similar to svn_diff_mem_string_output_unified3() but without + * cancel support and with @a context_size set to -1. + * * @since New in 1.7. Hunk delimiter "##" has the special meaning since 1.8. + * + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, svn_diff_t *diff, @@ -781,7 +918,10 @@ svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, * set to NULL. * * @since New in 1.5. + * + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_mem_string_output_unified(svn_stream_t *output_stream, svn_diff_t *diff, @@ -801,9 +941,36 @@ svn_diff_mem_string_output_unified(svn_stream_t *output_stream, * each of these if @c NULL is passed. * * @a conflict_style dictates how conflicts are displayed. + * + * If not @c NULL, call @a cancel_func with @a cancel_baton once or multiple + * times while processing larger diffs. + * + * Uses @a scratch_pool for temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_diff_mem_string_output_merge3(svn_stream_t *output_stream, + svn_diff_t *diff, + const svn_string_t *original, + const svn_string_t *modified, + const svn_string_t *latest, + const char *conflict_original, + const char *conflict_modified, + const char *conflict_latest, + const char *conflict_separator, + svn_diff_conflict_display_style_t style, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** Similar to svn_diff_mem_string_output_merge2(), but without cancel support. * * @since New in 1.6. + * + * @deprecated Provided for backwards compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, svn_diff_t *diff, @@ -912,7 +1079,7 @@ typedef struct svn_diff_hunk_t svn_diff_hunk_t; /** * Allocate @a *stringbuf in @a result_pool, and read into it one line * of the diff text of @a hunk. The hunk header is not returned only the - * unidiff data lines (starting with '+', '-', or ' ') are returned. + * unidiff data lines (starting with '+', '-', or ' ') are returned. * If the @a hunk is being interpreted in reverse (i.e. the reverse * parameter of svn_diff_parse_next_patch() was @c TRUE), the diff * text will be returned in reversed form. @@ -922,8 +1089,8 @@ typedef struct svn_diff_hunk_t svn_diff_hunk_t; * hunk does not end with a newline character and @a eol is not NULL. * Temporary allocations will be performed in @a scratch_pool. * - * @note The hunk header information can be retrievied with the following - * functions: + * @note The hunk header information can be retrieved with the following + * functions: * @see svn_diff_hunk_get_original_start() * @see svn_diff_hunk_get_original_length() * @see svn_diff_hunk_get_modified_start() @@ -1074,6 +1241,14 @@ typedef struct svn_patch_t { /** * Indicates whether the patch is being interpreted in reverse. */ svn_boolean_t reverse; + + /** + * Mergeinfo parsed from svn:mergeinfo diff data, with one entry for + * forward merges and one for reverse merges. + * Either entry can be @c NULL if no such merges are part of the diff. + * @since New in 1.9. */ + svn_mergeinfo_t mergeinfo; + svn_mergeinfo_t reverse_mergeinfo; } svn_patch_t; /** An opaque type representing an open patch file. diff --git a/contrib/subversion/subversion/include/svn_dirent_uri.h b/contrib/subversion/subversion/include/svn_dirent_uri.h index c4374d7a3..94856f229 100644 --- a/contrib/subversion/subversion/include/svn_dirent_uri.h +++ b/contrib/subversion/subversion/include/svn_dirent_uri.h @@ -202,7 +202,7 @@ svn_dirent_join(const char *base, apr_pool_t *result_pool); /** Join multiple components onto a @a base dirent. The components are - * terminated by a @c NULL. + * terminated by a @c SVN_VA_NULL. * * If any component is the empty string, it will be ignored. * @@ -218,7 +218,7 @@ svn_dirent_join(const char *base, char * svn_dirent_join_many(apr_pool_t *result_pool, const char *base, - ...); + ...) SVN_NEEDS_SENTINEL_NULL; /** Join a base relpath (@a base) with a component (@a component). * @a component need not be a single component. @@ -354,6 +354,19 @@ char * svn_relpath_dirname(const char *relpath, apr_pool_t *result_pool); +/** Return a maximum of @a max_components components of @a relpath. This is + * an efficient way of calling svn_relpath_dirname() multiple times until only + * a specific number of components is left. + * + * Allocate the result in @a result_pool (or statically in case of 0) + * + * @since New in 1.9. + */ +const char * +svn_relpath_prefix(const char *relpath, + int max_components, + apr_pool_t *result_pool); + /** Divide the canonicalized @a uri into a uri @a *dirpath and a * (URI-decoded) relpath @a *base_name. diff --git a/contrib/subversion/subversion/include/svn_error.h b/contrib/subversion/subversion/include/svn_error.h index 3a6e4c5ac..5681644fd 100644 --- a/contrib/subversion/subversion/include/svn_error.h +++ b/contrib/subversion/subversion/include/svn_error.h @@ -105,7 +105,7 @@ svn_error_symbolic_name(apr_status_t statcode); * @note @a buf and @a bufsize are provided in the interface so that * this function is thread-safe and yet does no allocation. */ -const char *svn_err_best_message(svn_error_t *err, +const char *svn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize); @@ -173,6 +173,19 @@ svn_error_t * svn_error_quick_wrap(svn_error_t *child, const char *new_msg); +/** A quick n' easy way to create a wrapped exception with your own + * printf-style error message produced by passing @a fmt, using + * apr_psprintf(), before throwing it up the stack. (It uses all of the + * @a child's fields.) + * + * @since New in 1.9. + */ +svn_error_t * +svn_error_quick_wrapf(svn_error_t *child, + const char *fmt, + ...) + __attribute__((format(printf, 2, 3))); + /** Compose two errors, returning the composition as a brand new error * and consuming the original errors. Either or both of @a err1 and * @a err2 may be @c SVN_NO_ERROR. If both are not @c SVN_NO_ERROR, @@ -202,7 +215,8 @@ svn_error_compose(svn_error_t *chain, /** Return the root cause of @a err by finding the last error in its * chain (e.g. it or its children). @a err may be @c SVN_NO_ERROR, in - * which case @c SVN_NO_ERROR is returned. + * which case @c SVN_NO_ERROR is returned. The returned error should + * @em not be cleared as it shares memory with @a err. * * @since New in 1.5. */ @@ -225,7 +239,7 @@ svn_error_find_cause(svn_error_t *err, apr_status_t apr_err); * @since New in 1.2. */ svn_error_t * -svn_error_dup(svn_error_t *err); +svn_error_dup(const svn_error_t *err); /** Free the memory used by @a error, as well as all ancestors and * descendants of @a error. @@ -255,6 +269,8 @@ svn_error__locate(const char *file, (svn_error__locate(__FILE__,__LINE__), (svn_error_wrap_apr)) #define svn_error_quick_wrap \ (svn_error__locate(__FILE__,__LINE__), (svn_error_quick_wrap)) +#define svn_error_quick_wrapf \ + (svn_error__locate(__FILE__,__LINE__), (svn_error_quick_wrapf)) #endif @@ -268,6 +284,10 @@ svn_error__locate(const char *file, * what code that used to call svn_handle_error() and now calls * svn_handle_error2() does. * + * Note that this should only be used from commandline specific code, or + * code that knows that @a stream is really where the application wants + * to receive its errors on. + * * @since New in 1.2. */ void @@ -293,11 +313,13 @@ svn_handle_error(svn_error_t *error, * * @a error may not be @c NULL. * + * @note This does not clear @a error. + * * @since New in 1.2. */ void svn_handle_warning2(FILE *stream, - svn_error_t *error, + const svn_error_t *error, const char *prefix); /** Like svn_handle_warning2() but with @c prefix set to "svn: " @@ -387,10 +409,17 @@ svn_error_t *svn_error_purge_tracing(svn_error_t *err); } while (0) -/** A statement macro, similar to @c SVN_ERR, but returns an integer. +/** A statement macro intended for the main() function of the 'svn' program. + * + * Evaluate @a expr. If it yields an error, display the error on stdout + * and return @c EXIT_FAILURE. * - * Evaluate @a expr. If it yields an error, handle that error and - * return @c EXIT_FAILURE. + * @note Not for use in the library, as it prints to stderr. This macro + * no longer suits the needs of the 'svn' program, and is not generally + * suitable for third-party use as it assumes the program name is 'svn'. + * + * @deprecated Provided for backward compatibility with the 1.8 API. Consider + * using svn_handle_error2() or svn_cmdline_handle_exit_error() instead. */ #define SVN_INT_ERR(expr) \ do { \ @@ -417,18 +446,26 @@ svn_error_t *svn_error_purge_tracing(svn_error_t *err); * SVN_ERR_FS_OUT_OF_DATE and SVN_ERR_FS_NOT_FOUND are in here because it's a * non-fatal error that can be thrown when attempting to lock an item. * + * SVN_ERR_REPOS_HOOK_FAILURE refers to the pre-lock hook. + * * @since New in 1.2. */ #define SVN_ERR_IS_LOCK_ERROR(err) \ (err->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED || \ err->apr_err == SVN_ERR_FS_NOT_FOUND || \ err->apr_err == SVN_ERR_FS_OUT_OF_DATE || \ - err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) + err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN || \ + err->apr_err == SVN_ERR_REPOS_HOOK_FAILURE || \ + err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION || \ + err->apr_err == SVN_ERR_FS_OUT_OF_DATE || \ + err->apr_err == SVN_ERR_FS_NOT_FILE) /** * Return TRUE if @a err is an error specifically related to unlocking * a path in the repository, FALSE otherwise. * + * SVN_ERR_REPOS_HOOK_FAILURE refers to the pre-unlock hook. + * * @since New in 1.2. */ #define SVN_ERR_IS_UNLOCK_ERROR(err) \ @@ -437,7 +474,8 @@ svn_error_t *svn_error_purge_tracing(svn_error_t *err); err->apr_err == SVN_ERR_FS_LOCK_OWNER_MISMATCH || \ err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK || \ err->apr_err == SVN_ERR_RA_NOT_LOCKED || \ - err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) + err->apr_err == SVN_ERR_FS_LOCK_EXPIRED || \ + err->apr_err == SVN_ERR_REPOS_HOOK_FAILURE) /** Evaluates to @c TRUE iff @a apr_err (of type apr_status_t) is in the given * @a category, which should be one of the @c SVN_ERR_*_CATEGORY_START @@ -626,6 +664,11 @@ typedef svn_error_t *(*svn_error_malfunction_handler_t) svn_error_malfunction_handler_t svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func); +/** Return the malfunction handler that is currently in effect. + * @since New in 1.9. */ +svn_error_malfunction_handler_t +svn_error_get_malfunction_handler(void); + /** Handle a malfunction by returning an error object that describes it. * * When @a can_return is false, abort() diff --git a/contrib/subversion/subversion/include/svn_error_codes.h b/contrib/subversion/subversion/include/svn_error_codes.h index 222bc2bbb..f8348f48d 100644 --- a/contrib/subversion/subversion/include/svn_error_codes.h +++ b/contrib/subversion/subversion/include/svn_error_codes.h @@ -150,6 +150,8 @@ extern "C" { + (22 * SVN_ERR_CATEGORY_SIZE)) #define SVN_ERR_MALFUNC_CATEGORY_START (APR_OS_START_USERERR \ + (23 * SVN_ERR_CATEGORY_SIZE)) +#define SVN_ERR_X509_CATEGORY_START (APR_OS_START_USERERR \ + + (24 * SVN_ERR_CATEGORY_SIZE)) #endif /* DOXYGEN_SHOULD_SKIP_THIS */ @@ -233,6 +235,11 @@ SVN_ERROR_START SVN_ERR_BAD_CATEGORY_START + 15, "Invalid atomic") + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_BAD_COMPRESSION_METHOD, + SVN_ERR_BAD_CATEGORY_START + 16, + "Invalid compression method") + /* xml errors */ SVN_ERRDEF(SVN_ERR_XML_ATTRIB_NOT_FOUND, @@ -255,6 +262,11 @@ SVN_ERROR_START SVN_ERR_XML_CATEGORY_START + 4, "Data cannot be safely XML-escaped") + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_XML_UNEXPECTED_ELEMENT, + SVN_ERR_XML_CATEGORY_START + 5, + "Unexpected XML element found") + /* io errors */ SVN_ERRDEF(SVN_ERR_IO_INCONSISTENT_EOL, @@ -312,6 +324,11 @@ SVN_ERROR_START SVN_ERR_STREAM_CATEGORY_START + 3, "Stream doesn't support seeking") + /** Since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_STREAM_NOT_SUPPORTED, + SVN_ERR_STREAM_CATEGORY_START + 4, + "Stream doesn't support this capability") + /* node errors */ SVN_ERRDEF(SVN_ERR_NODE_UNKNOWN_KIND, @@ -796,6 +813,61 @@ SVN_ERROR_START SVN_ERR_FS_CATEGORY_START + 52, "Could not initialize the revprop caching infrastructure.") + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_MALFORMED_TXN_ID, + SVN_ERR_FS_CATEGORY_START + 53, + "Malformed transaction ID string.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_INDEX_CORRUPTION, + SVN_ERR_FS_CATEGORY_START + 54, + "Corrupt index file.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_INDEX_REVISION, + SVN_ERR_FS_CATEGORY_START + 55, + "Revision not covered by index.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_INDEX_OVERFLOW, + SVN_ERR_FS_CATEGORY_START + 56, + "Item index too large for this revision.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_CONTAINER_INDEX, + SVN_ERR_FS_CATEGORY_START + 57, + "Container index out of range.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_INDEX_INCONSISTENT, + SVN_ERR_FS_CATEGORY_START + 58, + "Index files are inconsistent.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_LOCK_OPERATION_FAILED, + SVN_ERR_FS_CATEGORY_START + 59, + "Lock operation failed") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_UNSUPPORTED_TYPE, + SVN_ERR_FS_CATEGORY_START + 60, + "Unsupported FS type") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_CONTAINER_SIZE, + SVN_ERR_FS_CATEGORY_START + 61, + "Container capacity exceeded.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_MALFORMED_NODEREV_ID, + SVN_ERR_FS_CATEGORY_START + 62, + "Malformed node revision ID string.") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_FS_INVALID_GENERATION, + SVN_ERR_FS_CATEGORY_START + 63, + "Invalid generation number data.") + /* repos errors */ SVN_ERRDEF(SVN_ERR_REPOS_LOCKED, @@ -910,6 +982,11 @@ SVN_ERROR_START SVN_ERR_RA_CATEGORY_START + 12, "Can't create tunnel") + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_RA_CANNOT_CREATE_SESSION, + SVN_ERR_RA_CATEGORY_START + 13, + "Can't create session") + /* ra_dav errors */ SVN_ERRDEF(SVN_ERR_RA_DAV_SOCK_INIT, @@ -979,6 +1056,16 @@ SVN_ERROR_START SVN_ERR_RA_DAV_CATEGORY_START + 13, "URL access forbidden for unknown reason") + /** @since New in 1.9 */ + SVN_ERRDEF(SVN_ERR_RA_DAV_PRECONDITION_FAILED, + SVN_ERR_RA_DAV_CATEGORY_START + 14, + "The server state conflicts with the requested preconditions") + + /** @since New in 1.9 */ + SVN_ERRDEF(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, + SVN_ERR_RA_DAV_CATEGORY_START + 15, + "The URL doesn't allow the requested method") + /* ra_local errors */ SVN_ERRDEF(SVN_ERR_RA_LOCAL_REPOS_NOT_FOUND, @@ -1330,6 +1417,31 @@ SVN_ERROR_START SVN_ERR_MISC_CATEGORY_START + 38, "Atomic data storage is corrupt") + /** @since New in 1.8. */ + SVN_ERRDEF(SVN_ERR_UTF8PROC_ERROR, + SVN_ERR_MISC_CATEGORY_START + 39, + "utf8proc library error") + + /** @since New in 1.8. */ + SVN_ERRDEF(SVN_ERR_UTF8_GLOB, + SVN_ERR_MISC_CATEGORY_START + 40, + "Bad arguments to SQL operators GLOB or LIKE") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_CORRUPT_PACKED_DATA, + SVN_ERR_MISC_CATEGORY_START + 41, + "Packed data stream is corrupt") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_COMPOSED_ERROR, + SVN_ERR_MISC_CATEGORY_START + 42, + "Additional errors:") + + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_INVALID_INPUT, + SVN_ERR_MISC_CATEGORY_START + 43, + "Parser error: invalid input") + /* command-line client errors */ SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR, @@ -1380,6 +1492,11 @@ SVN_ERROR_START SVN_ERR_CL_CATEGORY_START + 11, "Failed processing one or more externals definitions") + /** @since New in 1.9. */ + SVN_ERRDEF(SVN_ERR_CL_REPOS_VERIFY_FAILED, + SVN_ERR_CL_CATEGORY_START + 12, + "Repository verification failed") + /* ra_svn errors */ SVN_ERRDEF(SVN_ERR_RA_SVN_CMD_ERR, @@ -1478,11 +1595,14 @@ SVN_ERROR_START "Diff data source modified unexpectedly") /* libsvn_ra_serf errors */ - /** @since New in 1.5. */ + /** @since New in 1.5. + @deprecated SSPI now handled by serf rather than libsvn_ra_serf. */ SVN_ERRDEF(SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, SVN_ERR_RA_SERF_CATEGORY_START + 0, "Initialization of SSPI library failed") - /** @since New in 1.5. */ + /** @since New in 1.5. + @deprecated Certificate verification now handled by serf rather + than libsvn_ra_serf. */ SVN_ERRDEF(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, SVN_ERR_RA_SERF_CATEGORY_START + 1, "Server SSL certificate untrusted") @@ -1507,6 +1627,90 @@ SVN_ERROR_START SVN_ERR_MALFUNC_CATEGORY_START + 1, "No non-tracing links found in the error chain") + /* X509 parser errors. + * Names of these error codes are based on tropicssl error codes. + * @since New in 1.9 */ + + SVN_ERRDEF(SVN_ERR_ASN1_OUT_OF_DATA, + SVN_ERR_X509_CATEGORY_START + 0, + "Unexpected end of ASN1 data") + + SVN_ERRDEF(SVN_ERR_ASN1_UNEXPECTED_TAG, + SVN_ERR_X509_CATEGORY_START + 1, + "Unexpected ASN1 tag") + + SVN_ERRDEF(SVN_ERR_ASN1_INVALID_LENGTH, + SVN_ERR_X509_CATEGORY_START + 2, + "Invalid ASN1 length") + + SVN_ERRDEF(SVN_ERR_ASN1_LENGTH_MISMATCH, + SVN_ERR_X509_CATEGORY_START + 3, + "ASN1 length mismatch") + + SVN_ERRDEF(SVN_ERR_ASN1_INVALID_DATA, + SVN_ERR_X509_CATEGORY_START + 4, + "Invalid ASN1 data") + + SVN_ERRDEF(SVN_ERR_X509_FEATURE_UNAVAILABLE, + SVN_ERR_X509_CATEGORY_START + 5, + "Unavailable X509 feature") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_PEM, + SVN_ERR_X509_CATEGORY_START + 6, + "Invalid PEM certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_FORMAT, + SVN_ERR_X509_CATEGORY_START + 7, + "Invalid certificate format") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_VERSION, + SVN_ERR_X509_CATEGORY_START + 8, + "Invalid certificate version") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_SERIAL, + SVN_ERR_X509_CATEGORY_START + 9, + "Invalid certificate serial number") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_ALG, + SVN_ERR_X509_CATEGORY_START + 10, + "Found invalid algorithm in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_NAME, + SVN_ERR_X509_CATEGORY_START + 11, + "Found invalid name in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_DATE, + SVN_ERR_X509_CATEGORY_START + 12, + "Found invalid date in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_PUBKEY, + SVN_ERR_X509_CATEGORY_START + 13, + "Found invalid public key in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_SIGNATURE, + SVN_ERR_X509_CATEGORY_START + 14, + "Found invalid signature in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, + SVN_ERR_X509_CATEGORY_START + 15, + "Found invalid extensions in certificate") + + SVN_ERRDEF(SVN_ERR_X509_CERT_UNKNOWN_VERSION, + SVN_ERR_X509_CATEGORY_START + 16, + "Unknown certificate version") + + SVN_ERRDEF(SVN_ERR_X509_CERT_UNKNOWN_PK_ALG, + SVN_ERR_X509_CATEGORY_START + 17, + "Certificate uses unknown public key algorithm") + + SVN_ERRDEF(SVN_ERR_X509_CERT_SIG_MISMATCH, + SVN_ERR_X509_CATEGORY_START + 18, + "Certificate signature mismatch") + + SVN_ERRDEF(SVN_ERR_X509_CERT_VERIFY_FAILED, + SVN_ERR_X509_CATEGORY_START + 19, + "Certficate verification failed") + SVN_ERROR_END diff --git a/contrib/subversion/subversion/include/svn_fs.h b/contrib/subversion/subversion/include/svn_fs.h index 8cef9a3e2..e34146d16 100644 --- a/contrib/subversion/subversion/include/svn_fs.h +++ b/contrib/subversion/subversion/include/svn_fs.h @@ -65,6 +65,30 @@ svn_fs_version(void); /** An object representing a Subversion filesystem. */ typedef struct svn_fs_t svn_fs_t; +/** + * @defgroup svn_fs_backend_names Built-in back-ends + * Constants defining the currently supported built-in filesystem backends. + * + * @see svn_fs_type + * @{ + */ +/** @since New in 1.1. */ +#define SVN_FS_TYPE_BDB "bdb" +/** @since New in 1.1. */ +#define SVN_FS_TYPE_FSFS "fsfs" + +/** + * EXPERIMENTAL filesystem backend. + * + * It is not ready for general production use. Please consult the + * respective release notes on suggested usage scenarios. + * + * @since New in 1.9. + */ +#define SVN_FS_TYPE_FSX "fsx" + +/** @} */ + /** * @name Filesystem configuration options @@ -110,16 +134,39 @@ typedef struct svn_fs_t svn_fs_t; */ #define SVN_FS_CONFIG_FSFS_CACHE_NS "fsfs-cache-namespace" +/** Enable / disable the FSFS format 7 "block read" feature. + * + * @since New in 1.9. + */ +#define SVN_FS_CONFIG_FSFS_BLOCK_READ "fsfs-block-read" + +/** String with a decimal representation of the FSFS format shard size. + * Zero ("0") means that a repository with linear layout should be created. + * + * This option will only be used during the creation of new repositories + * and is otherwise ignored. + * + * @since New in 1.9. + */ +#define SVN_FS_CONFIG_FSFS_SHARD_SIZE "fsfs-shard-size" + +/** Enable / disable the FSFS format 7 logical addressing feature for a + * newly created repository. + * + * This option will only be used during the creation of new repositories + * and is otherwise ignored. + * + * @since New in 1.9. + */ +#define SVN_FS_CONFIG_FSFS_LOG_ADDRESSING "fsfs-log-addressing" + /* Note to maintainers: if you add further SVN_FS_CONFIG_FSFS_CACHE_* knobs, update fs_fs.c:verify_as_revision_before_current_plus_plus(). */ -/* See also svn_fs_type(). */ -/** @since New in 1.1. */ +/** Select the filesystem type. See also #svn_fs_type(). + * + * @since New in 1.1. */ #define SVN_FS_CONFIG_FS_TYPE "fs-type" -/** @since New in 1.1. */ -#define SVN_FS_TYPE_BDB "bdb" -/** @since New in 1.1. */ -#define SVN_FS_TYPE_FSFS "fsfs" /** Create repository format compatible with Subversion versions * earlier than 1.4. @@ -148,6 +195,18 @@ typedef struct svn_fs_t svn_fs_t; * @since New in 1.8. */ #define SVN_FS_CONFIG_PRE_1_8_COMPATIBLE "pre-1.8-compatible" + +/** Create repository format compatible with the specified Subversion + * release. The value must be a version in the same format as + * #SVN_VER_NUMBER and cannot exceed the current version. + * + * @note The @c patch component would often be ignored, due to our forward + * compatibility promises within minor release lines. It should therefore + * usually be set to @c 0. + * + * @since New in 1.9. + */ +#define SVN_FS_CONFIG_COMPATIBLE_VERSION "compatible-version" /** @} */ @@ -222,6 +281,7 @@ svn_fs_set_warning_func(svn_fs_t *fs, * * SVN_FS_TYPE_BDB Berkeley-DB implementation * SVN_FS_TYPE_FSFS Native-filesystem implementation + * SVN_FS_TYPE_FSX Experimental filesystem implementation * * If @a fs_config is @c NULL or does not contain a value for * #SVN_FS_CONFIG_FS_TYPE then the default filesystem type will be used. @@ -242,40 +302,113 @@ svn_fs_create(svn_fs_t **fs_p, * return a pointer to it in @a *fs_p. If @a fs_config is not @c * NULL, the options it contains modify the behavior of the * filesystem. The interpretation of @a fs_config is specific to the - * filesystem back-end. The opened filesystem may be closed by - * destroying @a pool. + * filesystem back-end. The opened filesystem will be allocated in + * @a result_pool may be closed by clearing or destroying that pool. + * Use @a scratch_pool for temporary allocations. * * @note The lifetime of @a fs_config must not be shorter than @a - * pool's. It's a good idea to allocate @a fs_config from @a pool or - * one of its ancestors. + * result_pool's. It's a good idea to allocate @a fs_config from + * @a result_pool or one of its ancestors. * * Only one thread may operate on any given filesystem object at once. * Two threads may access the same filesystem simultaneously only if * they open separate filesystem objects. * * @note You probably don't want to use this directly. Take a look at - * svn_repos_open2() instead. + * svn_repos_open3() instead. * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_open2(svn_fs_t **fs_p, + const char *path, + apr_hash_t *fs_config, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Like svn_fs_open2(), but without @a scratch_pool. + * + * @deprecated Provided for backward compatibility with the 1.8 API. * @since New in 1.1. */ +SVN_DEPRECATED svn_error_t * svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, apr_pool_t *pool); +/** The kind of action being taken by 'upgrade'. + * + * @since New in 1.9. + */ +typedef enum svn_fs_upgrade_notify_action_t +{ + /** Packing of the revprop shard has completed. + * The number parameter is the shard being processed. */ + svn_fs_upgrade_pack_revprops = 0, + + /** Removal of the non-packed revprop shard is completed. + * The number parameter is the shard being processed */ + svn_fs_upgrade_cleanup_revprops, + + /** DB format has been set to the new value. + * The number parameter is the new format number. */ + svn_fs_upgrade_format_bumped +} svn_fs_upgrade_notify_action_t; + +/** The type of an upgrade notification function. @a number is specifc + * to @a action (see #svn_fs_upgrade_notify_action_t); @a action is the + * type of action being performed. @a baton is the corresponding baton + * for the notification function, and @a scratch_pool can be used for + * temporary allocations, but will be cleared between invocations. + * + * @since New in 1.9. + */ +typedef svn_error_t *(*svn_fs_upgrade_notify_t)(void *baton, + apr_uint64_t number, + svn_fs_upgrade_notify_action_t action, + apr_pool_t *scratch_pool); + /** * Upgrade the Subversion filesystem located in the directory @a path * to the latest version supported by this library. Return * #SVN_ERR_FS_UNSUPPORTED_UPGRADE and make no changes to the - * filesystem if the requested upgrade is not supported. Use @a pool - * for necessary allocations. + * filesystem if the requested upgrade is not supported. Use + * @a scratch_pool for temporary allocations. + * + * The optional @a notify_func callback is only a general feedback that + * the operation is still in process but may be called in e.g. random shard + * order and more than once for the same shard. + * + * The optional @a cancel_func callback will be invoked as usual to allow + * the user to preempt this potentially lengthy operation. * * @note You probably don't want to use this directly. Take a look at - * svn_repos_upgrade() instead. + * svn_repos_upgrade2() instead. * + * @note Canceling an upgrade is legal but may leave remnants of previous + * format data that may not be cleaned up automatically by later calls. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_upgrade2(const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** + * Like svn_fs_upgrade2 but with notify_func, notify_baton, cancel_func + * and cancel_baton being set to NULL. + * + * @deprecated Provided for backward compatibility with the 1.8 API. * @since New in 1.5. */ +SVN_DEPRECATED svn_error_t * svn_fs_upgrade(const char *path, apr_pool_t *pool); @@ -283,7 +416,7 @@ svn_fs_upgrade(const char *path, /** * Callback function type for progress notification. * - * @a revision is the number of the revision currently begin processed, + * @a revision is the number of the revision currently being processed, * #SVN_INVALID_REVNUM if the current stage is not linked to any specific * revision. @a baton is the callback baton. * @@ -328,7 +461,7 @@ svn_fs_path(svn_fs_t *fs, * Return a shallow copy of the configuration parameters used to open * @a fs, allocated in @a pool. It may be @c NULL. The contents of the * hash contents remains valid only for @a fs's lifetime. - * + * * @note This is just what was passed to svn_fs_create() or svn_fs_open(). * You may not modify it. * @@ -351,6 +484,17 @@ svn_error_t * svn_fs_delete_fs(const char *path, apr_pool_t *pool); +/** The type of a hotcopy notification function. @a start_revision and + * @a end_revision indicate the copied revision range. @a baton is the + * corresponding baton for the notification function, and @a scratch_pool + * can be used for temporary allocations, but will be cleared between + * invocations. + */ +typedef void (*svn_fs_hotcopy_notify_t)(void *baton, + svn_revnum_t start_revision, + svn_revnum_t end_revision, + apr_pool_t *scratch_pool); + /** * Copy a possibly live Subversion filesystem from @a src_path to * @a dest_path. If @a clean is @c TRUE, perform cleanup on the @@ -363,10 +507,39 @@ svn_fs_delete_fs(const char *path, * incremental hotcopy is not implemented, raise * #SVN_ERR_UNSUPPORTED_FEATURE. * + * For each revision range copied, @a notify_func will be called with + * staring and ending revision numbers (both inclusive and not necessarily + * different) and with the @a notify_baton. Currently, this notification + * is not triggered by the BDB backend. @a notify_func may be @c NULL + * if this notification is not required. + * + * The optional @a cancel_func callback will be invoked with + * @a cancel_baton as usual to allow the user to preempt this potentially + * lengthy operation. + * * Use @a scratch_pool for temporary allocations. * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_hotcopy3(const char *src_path, + const char *dest_path, + svn_boolean_t clean, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** + * Like svn_fs_hotcopy3(), but with @a notify_func and @a notify_baton + * always passed as @c NULL. + * + * @deprecated Provided for backward compatibility with the 1.8 API. * @since New in 1.8. */ +SVN_DEPRECATED svn_error_t * svn_fs_hotcopy2(const char *src_path, const char *dest_path, @@ -443,6 +616,11 @@ typedef svn_error_t *(*svn_fs_freeze_func_t)(void *baton, apr_pool_t *pool); * Take an exclusive lock on @a fs to prevent commits and then invoke * @a freeze_func passing @a freeze_baton. * + * @note @a freeze_func must not, directly or indirectly, call any function + * that attempts to take out a lock on the underlying repository. These + * include functions for packing, hotcopying, setting revprops and commits. + * Attempts to do so may result in a deadlock. + * * @note The BDB backend doesn't implement this feature so most * callers should not call this function directly but should use the * higher level svn_repos_freeze() instead. @@ -710,12 +888,54 @@ svn_fs_access_add_lock_token(svn_fs_access_t *access_ctx, * @{ */ +/** Defines the possible ways two arbitrary (root, path)-pairs may be + * related. + * + * @since New in 1.9. + */ +typedef enum svn_fs_node_relation_t +{ + /** The (root, path)-pairs are not related, i.e. none of the other cases + * apply. If the roots refer to different @c svn_fs_t instances, then + * they are always considered unrelated - even if the underlying + * repository is the same. + */ + svn_fs_node_unrelated = 0, + + /** No changes have been made between the (root, path)-pairs, i.e. they + * have the same (relative) nodes in their sub-trees, corresponding sub- + * tree nodes have the same contents as well as properties and report the + * same "created-path" and "created-rev" data. This implies having a + * common ancestor. + * + * However, due to efficiency considerations, the FS implementation may + * report some combinations as merely having a common ancestor + * (@a svn_fs_node_common_ancestor) instead of actually being unchanged. + */ + svn_fs_node_unchanged, + + /** The (root, path)-pairs have a common ancestor (which may be one of + * them) but there are changes between them, i.e. they don't fall into + * the @c svn_fs_node_unchanged category. + * + * Due to efficiency considerations, the FS implementation may falsely + * classify some combinations as merely having a common ancestor that + * are, in fact, unchanged (@a svn_fs_node_unchanged). + */ + svn_fs_node_common_ancestor + +} svn_fs_node_relation_t; + /** An object representing a node-revision id. */ typedef struct svn_fs_id_t svn_fs_id_t; /** Return -1, 0, or 1 if node revisions @a a and @a b are respectively * unrelated, equivalent, or otherwise related (part of the same node). + * + * @note Consider using the more expressive #svn_fs_node_relation() instead. + * + * @see #svn_fs_node_relation */ int svn_fs_compare_ids(const svn_fs_id_t *a, @@ -725,6 +945,10 @@ svn_fs_compare_ids(const svn_fs_id_t *a, /** Return TRUE if node revisions @a id1 and @a id2 are related (part of the * same node), else return FALSE. + * + * @note Consider using the more expressive #svn_fs_node_relation() instead. + * + * @see #svn_fs_node_relation */ svn_boolean_t svn_fs_check_related(const svn_fs_id_t *id1, @@ -827,10 +1051,9 @@ svn_fs_unparse_id(const svn_fs_id_t *id, * pairs. When you commit a transaction, all of its properties become * unversioned revision properties of the new revision. (There is one * exception: the svn:date property will be automatically set on new - * transactions to the date that the transaction was created, and will + * transactions to the date that the transaction was created, and can * be overwritten when the transaction is committed by the current - * time; changes to a transaction's svn:date property will not affect - * its committed value.) + * time; see svn_fs_commit_txn.) * * Transaction names are guaranteed to contain only letters (upper- * and lower-case), digits, `-', and `.', from the ASCII character @@ -872,6 +1095,14 @@ typedef struct svn_fs_txn_t svn_fs_txn_t; */ #define SVN_FS_TXN_CHECK_LOCKS 0x00002 +/** Allow the client to specify the final svn:date of the revision by + * setting or deleting the corresponding transaction property rather + * than have it set automatically when the transaction is committed. + * + * @since New in 1.9. + */ +#define SVN_FS_TXN_CLIENT_DATE 0x00004 + /** @} */ /** @@ -925,6 +1156,17 @@ svn_fs_begin_txn(svn_fs_txn_t **txn_p, * a new filesystem revision containing the changes made in @a txn, * storing that new revision number in @a *new_rev, and return zero. * + * If #SVN_FS_TXN_CLIENT_DATE was passed to #svn_fs_begin_txn2 any + * svn:date on the transaction will be become the unversioned property + * svn:date on the revision. svn:date can have any value, it does not + * have to be a timestamp. If the transaction has no svn:date the + * revision will have no svn:date. + * + * If #SVN_FS_TXN_CLIENT_DATE was not passed to #svn_fs_begin_txn2 the + * new revision will have svn:date set to the current time at some + * point during the commit and any svn:date on the transaction will be + * lost. + * * If @a conflict_p is non-zero, use it to provide details on any * conflicts encountered merging @a txn with the most recent committed * revisions. If a conflict occurs, set @a *conflict_p to the path of @@ -962,6 +1204,7 @@ svn_fs_begin_txn(svn_fs_txn_t **txn_p, * ### conflict string * ### *new_rev will always be initialized to SVN_INVALID_REVNUM, or * ### to a valid, committed revision number + * */ svn_error_t * svn_fs_commit_txn(const char **conflict_p, @@ -1218,7 +1461,6 @@ typedef enum svn_fs_path_change_kind_t /** ignore all previous change items for path (internal-use only) */ svn_fs_path_change_reset - } svn_fs_path_change_kind_t; /** Change descriptor. @@ -1227,6 +1469,11 @@ typedef enum svn_fs_path_change_kind_t * versions. Therefore, to preserve binary compatibility, users * should not directly allocate structures of this type. * + * @note The @c text_mod, @c prop_mod and @c mergeinfo_mod flags mean the + * text, properties and mergeinfo property (respectively) were "touched" + * by the commit API; this does not mean the new value is different from + * the old value. + * * @since New in 1.6. */ typedef struct svn_fs_path_change2_t { @@ -1236,10 +1483,23 @@ typedef struct svn_fs_path_change2_t /** kind of change */ svn_fs_path_change_kind_t change_kind; - /** were there text mods? */ + /** was the text touched? + * For node_kind=dir: always false. For node_kind=file: + * modify: true iff text touched. + * add (copy): true iff text touched. + * add (plain): always true. + * delete: always false. + * replace: as for the add/copy part of the replacement. + */ svn_boolean_t text_mod; - /** were there property mods? */ + /** were the properties touched? + * modify: true iff props touched. + * add (copy): true iff props touched. + * add (plain): true iff props touched. + * delete: always false. + * replace: as for the add/copy part of the replacement. + */ svn_boolean_t prop_mod; /** what node kind is the path? @@ -1252,6 +1512,15 @@ typedef struct svn_fs_path_change2_t svn_revnum_t copyfrom_rev; const char *copyfrom_path; + /** was the mergeinfo property touched? + * modify: } true iff svn:mergeinfo property add/del/mod + * add (copy): } and fs format supports this flag. + * add (plain): } + * delete: always false. + * replace: as for the add/copy part of the replacement. + * (Note: Pre-1.9 repositories will report #svn_tristate_unknown.) + * @since New in 1.9. */ + svn_tristate_t mergeinfo_mod; /* NOTE! Please update svn_fs_path_change2_create() when adding new fields here. */ } svn_fs_path_change2_t; @@ -1349,9 +1618,25 @@ typedef struct svn_fs_history_t svn_fs_history_t; /** Set @a *history_p to an opaque node history object which * represents @a path under @a root. @a root must be a revision root. - * Use @a pool for all allocations. + * Allocate the result in @a result_pool and use @a scratch_pool for + * temporary allocations. + * + * @since New in 1.9. */ svn_error_t * +svn_fs_node_history2(svn_fs_history_t **history_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Same as svn_fs_node_history2() but using a single @a pool for all + * allocations. + * + * @deprecated Provided for backward compatibility with the 1.8 API. + */ +SVN_DEPRECATED +svn_error_t * svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, const char *path, @@ -1383,7 +1668,25 @@ svn_fs_node_history(svn_fs_history_t **history_p, * the same age as the revision of that path in @a root. That is, if * @a root is a revision root based on revision X, and @a path was * modified in some revision(s) younger than X, those revisions - * younger than X will not be included for @a path. */ + * younger than X will not be included for @a path. + * + * Allocate the result in @a result_pool and use @a scratch_pool for + * temporary allocations. + * + * @since New in 1.9. */ +svn_error_t * +svn_fs_history_prev2(svn_fs_history_t **prev_history_p, + svn_fs_history_t *history, + svn_boolean_t cross_copies, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Same as svn_fs_history_prev2() but using a single @a pool for all + * allocations. + * + * @deprecated Provided for backward compatibility with the 1.8 API. + */ +SVN_DEPRECATED svn_error_t * svn_fs_history_prev(svn_fs_history_t **prev_history_p, svn_fs_history_t *history, @@ -1437,12 +1740,34 @@ svn_fs_node_id(const svn_fs_id_t **id_p, const char *path, apr_pool_t *pool); -/** Set @a *revision to the revision in which @a path under @a root was - * created. Use @a pool for any temporary allocations. @a *revision will +/** Determine how @a path_a under @a root_a and @a path_b under @a root_b + * are related and return the result in @a relation. There is no restriction + * concerning the roots: They may refer to different repositories, be in + * arbitrary revision order and any of them may pertain to a transaction. + * @a scratch_pool is used for temporary allocations. + * + * @note Paths from different svn_fs_t will be reported as unrelated even + * if the underlying physical repository is the same. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_node_relation(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, + const char *path_a, + svn_fs_root_t *root_b, + const char *path_b, + apr_pool_t *scratch_pool); + +/** Set @a *revision to the revision in which the node-revision identified + * by @a path under @a root was created; that is, to the revision in which + * @a path under @a root was last modified. @a *revision will * be set to #SVN_INVALID_REVNUM for uncommitted nodes (i.e. modified nodes * under a transaction root). Note that the root of an unmodified transaction * is not itself considered to be modified; in that case, return the revision * upon which the transaction was based. + * + * Use @a pool for any temporary allocations. */ svn_error_t * svn_fs_node_created_rev(svn_revnum_t *revision, @@ -1501,6 +1826,18 @@ svn_fs_node_proplist(apr_hash_t **table_p, const char *path, apr_pool_t *pool); +/** Set @a *has_props to TRUE if the node @a path in @a root has properties + * and to FALSE if it doesn't have properties. Perform temporary allocations + * in @a scratch_pool. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_node_has_props(svn_boolean_t *has_props, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool); + /** Change a node's property's value, or add/delete a property. * @@ -1521,10 +1858,53 @@ svn_fs_change_node_prop(svn_fs_root_t *root, /** Determine if the properties of two path/root combinations are different. * - * Set @a *changed_p to 1 if the properties at @a path1 under @a root1 differ - * from those at @a path2 under @a root2, or set it to 0 if they are the - * same. Both paths must exist under their respective roots, and both - * roots must be in the same filesystem. + * Set @a *different_p to #TRUE if the properties at @a path1 under @a root1 + * differ from those at @a path2 under @a root2, or set it to #FALSE if they + * are the same. Both paths must exist under their respective roots, and + * both roots must be in the same filesystem. + * Do any necessary temporary allocation in @a scratch_pool. + * + * @note For the purposes of preserving accurate history, certain bits of + * code (such as the repository dump code) need to care about the distinction + * between situations when the properties are "different" and "have changed + * across two points in history". We have a pair of functions that can + * answer both of these questions, svn_fs_props_different() and + * svn_fs_props_changed(). See issue 4598 for more details. + * + * @see svn_fs_props_changed + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_props_different(svn_boolean_t *different_p, + svn_fs_root_t *root1, + const char *path1, + svn_fs_root_t *root2, + const char *path2, + apr_pool_t *scratch_pool); + + +/** Determine if the properties of two path/root combinations have changed. + * + * Set @a *changed_p to #TRUE if the properties at @a path1 under @a root1 + * differ from those at @a path2 under @a root2, or set it to #FALSE if they + * are the same. Both paths must exist under their respective roots, and + * both roots must be in the same filesystem. + * Do any necessary temporary allocation in @a pool. + * + * @note For the purposes of preserving accurate history, certain bits of + * code (such as the repository dump code) need to care about the distinction + * between situations when the properties are "different" and "have changed + * across two points in history". We have a pair of functions that can + * answer both of these questions, svn_fs_props_different() and + * svn_fs_props_changed(). See issue 4598 for more details. + * + * @note This function can currently return false negatives for FSFS: + * If @a root1 and @a root2 were both transaction roots and the proplists + * of both paths had been changed in their respective transactions, + * @a changed_p would be set to #FALSE. + * + * @see svn_fs_props_different */ svn_error_t * svn_fs_props_changed(svn_boolean_t *changed_p, @@ -1624,12 +2004,14 @@ svn_fs_closest_copy(svn_fs_root_t **root_p, * * If @a adjust_inherited_mergeinfo is @c TRUE, then any inherited * mergeinfo returned in @a *catalog is normalized to represent the - * inherited mergeinfo on the path which inherits it. If + * inherited mergeinfo on the path which inherits it. This adjusted + * mergeinfo is keyed by the path which inherits it. If * @a adjust_inherited_mergeinfo is @c FALSE, then any inherited * mergeinfo is the raw explicit mergeinfo from the nearest parent * of the path with explicit mergeinfo, unadjusted for the path-wise * difference between the path and its parent. This may include - * non-inheritable mergeinfo. + * non-inheritable mergeinfo. This unadjusted mergeinfo is keyed by + * the path at which it was found. * * If @a include_descendants is TRUE, then additionally return the * mergeinfo for any descendant of any element of @a paths which has @@ -1738,6 +2120,21 @@ svn_fs_dir_entries(apr_hash_t **entries_p, const char *path, apr_pool_t *pool); +/** Take the #svn_fs_dirent_t structures in @a entries as returned by + * #svn_fs_dir_entries for @a root and determine an optimized ordering + * in which data access would most likely be efficient. Set @a *ordered_p + * to a newly allocated APR array of pointers to these #svn_fs_dirent_t + * structures. Allocate the array (but not its contents) in @a result_pool + * and use @a scratch_pool for temporaries. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_dir_optimal_order(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** Create a new directory named @a path in @a root. The new directory has * no entries, and no properties. @a root must be the root of a transaction, @@ -1811,6 +2208,7 @@ svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root, const char *path, apr_pool_t *pool); + /* Files. */ @@ -1921,13 +2319,13 @@ typedef svn_error_t * * upon doing so. Use @a pool for allocations. * * This function is intended to support zero copy data processing. It may - * not be implemented for all data backends or not applicable for certain - * content. In that case, @a *success will always be @c FALSE. Also, this - * is a best-effort function which means that there is no guarantee that - * @a processor gets called at all for some content. + * not be implemented for all data backends or not be applicable for certain + * content. In those cases, @a *success will always be @c FALSE. Also, + * this is a best-effort function which means that there is no guarantee + * that @a processor gets called at all. * - * @note @a processor is expected to be relatively short function with - * at most O(content size) runtime. + * @note @a processor is expected to be a relatively simple function with + * a runtime of O(content size) or less. * * @since New in 1.8. */ @@ -2015,11 +2413,8 @@ svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p, * * Do any necessary temporary allocation in @a pool. * - * ### This is like svn_fs_apply_textdelta(), but takes the text - * straight. It is currently used only by the loader, see - * libsvn_repos/load.c. It should accept a checksum, of course, which - * would come from an (optional) header in the dump file. See - * http://subversion.tigris.org/issues/show_bug.cgi?id=1102 for more. + * @note This is like svn_fs_apply_textdelta(), but takes the text + * straight. */ svn_error_t * svn_fs_apply_text(svn_stream_t **contents_p, @@ -2029,12 +2424,54 @@ svn_fs_apply_text(svn_stream_t **contents_p, apr_pool_t *pool); +/** Check if the contents of two root/path combos are different. + * + * Set @a *different_p to #TRUE if the file contents at @a path1 under + * @a root1 differ from those at @a path2 under @a root2, or set it to + * #FALSE if they are the same. Both paths must exist under their + * respective roots, and both roots must be in the same filesystem. + * Do any necessary temporary allocation in @a scratch_pool. + * + * @note For the purposes of preserving accurate history, certain bits of + * code (such as the repository dump code) need to care about the distinction + * between situations when two files have "different" content and when the + * contents of a given file "have changed" across two points in its history. + * We have a pair of functions that can answer both of these questions, + * svn_fs_contents_different() and svn_fs_contents_changed(). See issue + * 4598 for more details. + * + * @see svn_fs_contents_changed + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_contents_different(svn_boolean_t *different_p, + svn_fs_root_t *root1, + const char *path1, + svn_fs_root_t *root2, + const char *path2, + apr_pool_t *scratch_pool); + /** Check if the contents of two root/path combos have changed. * - * Set @a *changed_p to 1 if the contents at @a path1 under @a root1 differ - * from those at @a path2 under @a root2, or set it to 0 if they are the - * same. Both paths must exist under their respective roots, and both - * roots must be in the same filesystem. + * Set @a *changed_p to #TRUE if the file contents at @a path1 under + * @a root1 differ from those at @a path2 under @a root2, or set it to + * #FALSE if they are the same. Both paths must exist under their + * respective roots, and both roots must be in the same filesystem. + * Do any necessary temporary allocation in @a pool. + * + * @note svn_fs_contents_changed() was not designed to be used to detect + * when two files have different content, but really to detect when the + * contents of a given file have changed across two points in its history. + * For the purposes of preserving accurate history, certain bits of code + * (such as the repository dump code) need to care about this distinction. + * For example, it's not an error from the FS API point of view to call + * svn_fs_apply_textdelta() and explicitly set a file's contents to exactly + * what they were before the edit was made. We have a pair of functions + * that can answer both of these questions, svn_fs_contents_changed() and + * svn_fs_contents_different(). See issue 4598 for more details. + * + * @see svn_fs_contents_different */ svn_error_t * svn_fs_contents_changed(svn_boolean_t *changed_p, @@ -2060,6 +2497,41 @@ svn_fs_youngest_rev(svn_revnum_t *youngest_p, apr_pool_t *pool); +/** + * Return filesystem format information for @a fs. + * + * Set @a *fs_format to the filesystem format number of @a fs, which is + * an integer that increases when incompatible changes are made (such as + * by #svn_fs_upgrade). + * + * Set @a *supports_version to the version number of the minimum Subversion GA + * release that can read and write @a fs. + * + * @see svn_repos_info_format + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Return a list of admin-serviceable config files for @a fs. @a *files + * will be set to an array containing paths as C strings. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + + /** Provide filesystem @a fs the opportunity to compress storage relating to * associated with @a revision in filesystem @a fs. Use @a pool for all * allocations. @@ -2185,12 +2657,6 @@ svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool); - -/* Non-historical properties. */ - -/* [[Yes, do tell.]] */ - - /** @defgroup svn_fs_locks Filesystem locks * @{ @@ -2210,7 +2676,7 @@ svn_fs_set_uuid(svn_fs_t *fs, * mainly due to the serialization for tokens for pre-commit hook. * * Locks are not secret; anyone can view existing locks in a - * filesystem. Locks are not omnipotent: they can broken and stolen + * filesystem. Locks are not omnipotent: they can be broken and stolen * by people who don't "own" the lock. (Though admins can tailor a * custom break/steal policy via libsvn_repos pre-lock hook script.) * @@ -2220,16 +2686,64 @@ svn_fs_set_uuid(svn_fs_t *fs, * expiration error (depending on the API). */ +/** Lock information for use with svn_fs_lock_many() [and svn_repos_fs_...]. + * + * @see svn_fs_lock_target_create + * + * @since New in 1.9. + */ +typedef struct svn_fs_lock_target_t svn_fs_lock_target_t; + +/** Create an svn_fs_lock_target_t allocated in @a result_pool. + * @a token can be NULL and @a current_rev can be SVN_INVALID_REVNUM. + * + * The @a token is not duplicated and so must have a lifetime at least as + * long as the returned target object. + * + * @since New in 1.9. + */ +svn_fs_lock_target_t *svn_fs_lock_target_create(const char *token, + svn_revnum_t current_rev, + apr_pool_t *result_pool); + +/** Update @a target changing the token to @a token, @a token can be NULL. + * + * The @a token is not duplicated and so must have a lifetime at least as + * long as @a target. + * + * @since New in 1.9. + */ +void svn_fs_lock_target_set_token(svn_fs_lock_target_t *target, + const char *token); -/** Lock @a path in @a fs, and set @a *lock to a lock - * representing the new lock, allocated in @a pool. +/** The callback invoked by svn_fs_lock_many() and svn_fs_unlock_many(). * - * @warning You may prefer to use svn_repos_fs_lock() instead, - * which see. + * @a path and @a lock are allocated in the result_pool passed to + * svn_fs_lock_many/svn_fs_unlock_many and so will persist beyond the + * callback invocation. @a fs_err will be cleared after the callback + * returns, use svn_error_dup() to preserve the error. + * + * If the callback returns an error no further callbacks will be made + * and svn_fs_lock_many/svn_fs_unlock_many will return an error. The + * caller cannot rely on any particular order for these callbacks and + * cannot rely on interrupting the underlying operation by returning + * an error. Returning an error stops the callbacks but any locks + * that would have been reported in further callbacks may, or may not, + * still be created/released. + * + * @since New in 1.9. + */ +typedef svn_error_t *(*svn_fs_lock_callback_t)(void *baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *scratch_pool); + +/** Lock the paths in @a lock_targets in @a fs. * * @a fs must have a username associated with it (see * #svn_fs_access_t), else return #SVN_ERR_FS_NO_USER. Set the - * 'owner' field in the new lock to the fs username. + * 'owner' field in each new lock to the fs username. * * @a comment is optional: it's either an xml-escapable UTF8 string * which describes the lock, or it is @c NULL. @@ -2238,28 +2752,63 @@ svn_fs_set_uuid(svn_fs_t *fs, * generic DAV client; only mod_dav_svn's autoversioning feature needs * to use it. If in doubt, pass 0. * - * If path is already locked, then return #SVN_ERR_FS_PATH_ALREADY_LOCKED, + * The paths to be locked are passed as the const char * keys + * of the @a lock_targets hash. The hash values are + * svn_fs_lock_target_t * and provide the token and + * @a current_rev for each path. The token is a lock token such as can + * be generated using svn_fs_generate_lock_token() (indicating that + * the caller wants to dictate the lock token used), or it is @c NULL + * (indicating that the caller wishes to have a new token generated by + * this function). If the token is not @c NULL, and represents an + * existing lock, then the path must match the path associated with + * that existing lock. If @a current_rev is a valid revnum, then do an + * out-of-dateness check. If the revnum is less than the + * last-changed-revision of the path (or if the path doesn't exist in + * HEAD), yield an #SVN_ERR_FS_OUT_OF_DATE error for this path. + * + * If a path is already locked, then yield #SVN_ERR_FS_PATH_ALREADY_LOCKED, * unless @a steal_lock is TRUE, in which case "steal" the existing * lock, even if the FS access-context's username does not match the - * current lock's owner: delete the existing lock on @a path, and + * current lock's owner: delete the existing lock on the path, and * create a new one. * - * @a token is a lock token such as can be generated using - * svn_fs_generate_lock_token() (indicating that the caller wants to - * dictate the lock token used), or it is @c NULL (indicating that the - * caller wishes to have a new token generated by this function). If - * @a token is not @c NULL, and represents an existing lock, then @a - * path must match the path associated with that existing lock. - * * If @a expiration_date is zero, then create a non-expiring lock. * Else, the lock will expire at @a expiration_date. * - * If @a current_rev is a valid revnum, then do an out-of-dateness - * check. If the revnum is less than the last-changed-revision of @a - * path (or if @a path doesn't exist in HEAD), return - * #SVN_ERR_FS_OUT_OF_DATE. + * For each path in @a lock_targets @a lock_callback will be invoked + * passing @a lock_baton and the lock and error that apply to path. + * @a lock_callback can be NULL in which case it is not called and any + * errors that would have been passed to the callback are not reported. + * + * The lock and path passed to @a lock_callback will be allocated in + * @a result_pool. Use @a scratch_pool for temporary allocations. * * @note At this time, only files can be locked. + * + * @note This function is not atomic. If it returns an error, some targets + * may remain unlocked while others may have been locked. + * + * @note You probably don't want to use this directly. Take a look at + * svn_repos_fs_lock_many() instead. + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_lock_many(svn_fs_t *fs, + apr_hash_t *lock_targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Similar to svn_fs_lock_many() but locks only a single @a path and + * returns the lock in @a *lock, allocated in @a pool, or an error. + * + * @since New in 1.2. */ svn_error_t * svn_fs_lock(svn_lock_t **lock, @@ -2286,20 +2835,52 @@ svn_fs_generate_lock_token(const char **token, apr_pool_t *pool); -/** Remove the lock on @a path represented by @a token in @a fs. +/** Remove the locks on the paths in @a unlock_targets in @a fs. + * + * The paths to be unlocked are passed as const char * keys + * of the @a unlock_targets hash with the corresponding lock tokens as + * const char * values. If the token doesn't point to a + * lock, yield an #SVN_ERR_FS_BAD_LOCK_TOKEN error for this path. If + * the token points to an expired lock, yield an + * #SVN_ERR_FS_LOCK_EXPIRED error for this path. If @a fs has no + * username associated with it, yield an #SVN_ERR_FS_NO_USER unless @a + * break_lock is specified. + * + * If the token points to a lock, but the username of @a fs's access + * context doesn't match the lock's owner, yield an + * #SVN_ERR_FS_LOCK_OWNER_MISMATCH. If @a break_lock is TRUE, + * however, don't return error; allow the lock to be "broken" in any + * case. In the latter case, the token shall be @c NULL. * - * If @a token doesn't point to a lock, return #SVN_ERR_FS_BAD_LOCK_TOKEN. - * If @a token points to an expired lock, return #SVN_ERR_FS_LOCK_EXPIRED. - * If @a fs has no username associated with it, return #SVN_ERR_FS_NO_USER - * unless @a break_lock is specified. + * For each path in @a unlock_targets @a lock_callback will be invoked + * passing @a lock_baton and error that apply to path. The @a lock + * passed to the callback will be NULL. @a lock_callback can be NULL + * in which case it is not called and any errors that would have been + * passed to the callback are not reported. * - * If @a token points to a lock, but the username of @a fs's access - * context doesn't match the lock's owner, return - * #SVN_ERR_FS_LOCK_OWNER_MISMATCH. If @a break_lock is TRUE, however, don't - * return error; allow the lock to be "broken" in any case. In the latter - * case, @a token shall be @c NULL. + * The path passed to lock_callback will be allocated in @a result_pool. + * Use @a scratch_pool for temporary allocations. + * + * @note This function is not atomic. If it returns an error, some targets + * may remain locked while others may have been unlocked. + * + * @note You probably don't want to use this directly. Take a look at + * svn_repos_fs_unlock_many() instead. * - * Use @a pool for temporary allocations. + * @since New in 1.9. + */ +svn_error_t * +svn_fs_unlock_many(svn_fs_t *fs, + apr_hash_t *unlock_targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Similar to svn_fs_unlock_many() but only unlocks a single path. + * + * @since New in 1.2. */ svn_error_t * svn_fs_unlock(svn_fs_t *fs, @@ -2312,7 +2893,7 @@ svn_fs_unlock(svn_fs_t *fs, /** If @a path is locked in @a fs, set @a *lock to an svn_lock_t which * represents the lock, allocated in @a pool. * - * If @a path is not locked, set @a *lock to NULL. + * If @a path is not locked or does not exist in HEAD, set @a *lock to NULL. */ svn_error_t * svn_fs_get_lock(svn_lock_t **lock, @@ -2523,6 +3104,113 @@ svn_fs_verify_root(svn_fs_root_t *root, /** @} */ +/** + * @defgroup fs_info Filesystem information subsystem + * @{ + */ + +/** + * A structure that provides some information about a filesystem. + * Returned by svn_fs_info() for #SVN_FS_TYPE_FSFS filesystems. + * + * @note Fields may be added to the end of this structure in future + * versions. Therefore, users shouldn't allocate structures of this + * type, to preserve binary compatibility. + * + * @since New in 1.9. + */ +typedef struct svn_fs_fsfs_info_t { + + /** Filesystem backend (#fs_type), i.e., the string #SVN_FS_TYPE_FSFS. */ + const char *fs_type; + + /** Shard size, or 0 if the filesystem is not currently sharded. */ + int shard_size; + + /** The smallest revision (as #svn_revnum_t) which is not in a pack file. + * @note Zero (0) if (but not iff) the format does not support packing. */ + svn_revnum_t min_unpacked_rev; + + /** TRUE if logical addressing is enabled for this repository. + * FALSE if repository uses physical addressing. */ + svn_boolean_t log_addressing; + /* ### TODO: information about fsfs.conf? rep-cache.db? write locks? */ + + /* If you add fields here, check whether you need to extend svn_fs_info() + or svn_fs_info_dup(). */ +} svn_fs_fsfs_info_t; + +/** + * A structure that provides some information about a filesystem. + * Returned by svn_fs_info() for #SVN_FS_TYPE_FSX filesystems. + * + * @note Fields may be added to the end of this structure in future + * versions. Therefore, users shouldn't allocate structures of this + * type, to preserve binary compatibility. + * + * @since New in 1.9. + */ +typedef struct svn_fs_fsx_info_t { + + /** Filesystem backend (#fs_type), i.e., the string #SVN_FS_TYPE_FSX. */ + const char *fs_type; + + /** Shard size, always > 0. */ + int shard_size; + + /** The smallest revision which is not in a pack file. */ + svn_revnum_t min_unpacked_rev; + + /* If you add fields here, check whether you need to extend svn_fs_info() + or svn_fs_info_dup(). */ + +} svn_fs_fsx_info_t; + +/** @see svn_fs_info + * @since New in 1.9. */ +typedef struct svn_fs_info_placeholder_t { + /** @see svn_fs_type */ + const char *fs_type; + + /* Do not add new fields here, to maintain compatibility with the first + released version of svn_fs_fsfs_info_t. */ +} svn_fs_info_placeholder_t; + +/** + * Set @a *fs_info to a struct describing @a fs. The type of the + * struct depends on the backend: for #SVN_FS_TYPE_FSFS, the struct will be + * of type #svn_fs_fsfs_info_t; for #SVN_FS_TYPE_FSX, it will be of type + * #svn_fs_fsx_info_t; otherwise, the struct is guaranteed to be + * (compatible with) #svn_fs_info_placeholder_t. + * + * @see #svn_fs_fsfs_info_t, #svn_fs_fsx_info_t + * + * @since New in 1.9. + */ +svn_error_t * +svn_fs_info(const svn_fs_info_placeholder_t **fs_info, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Return a duplicate of @a info, allocated in @a result_pool. The returned + * struct will be of the same type as the passed-in struct, which itself + * must have been returned from svn_fs_info() or svn_fs_info_dup(). No part + * of the new structure will be shared with @a info (except static string + * constants). Use @a scratch_pool for temporary allocations. + * + * @see #svn_fs_info_placeholder_t, #svn_fs_fsfs_info_t + * + * @since New in 1.9. + */ +void * +svn_fs_info_dup(const void *info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** @} */ + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/svn_hash.h b/contrib/subversion/subversion/include/svn_hash.h index 46b476006..50ed10a4b 100644 --- a/contrib/subversion/subversion/include/svn_hash.h +++ b/contrib/subversion/subversion/include/svn_hash.h @@ -37,7 +37,6 @@ #include "svn_types.h" #include "svn_io.h" /* for svn_stream_t */ - #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/svn_io.h b/contrib/subversion/subversion/include/svn_io.h index 0082917a8..42eb422bf 100644 --- a/contrib/subversion/subversion/include/svn_io.h +++ b/contrib/subversion/subversion/include/svn_io.h @@ -682,8 +682,15 @@ svn_io_files_contents_three_same_p(svn_boolean_t *same12, 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. +/** Create a file at utf8-encoded path @a file with the contents given + * by the null-terminated string @a contents. + * + * @a file must not already exist. If an error occurs while writing or + * closing the file, attempt to delete the file before returning the error. + * + * Write the data in 'binary' mode (#APR_FOPEN_BINARY). If @a contents + * is @c NULL, create an empty file. + * * Use @a pool for memory allocations. */ svn_error_t * @@ -691,6 +698,38 @@ svn_io_file_create(const char *file, const char *contents, apr_pool_t *pool); +/** Create a file at utf8-encoded path @a file with the contents given + * by @a contents of @a length bytes. + * + * @a file must not already exist. If an error occurs while writing or + * closing the file, attempt to delete the file before returning the error. + * + * Write the data in 'binary' mode (#APR_FOPEN_BINARY). If @a length is + * zero, create an empty file; in this case @a contents may be @c NULL. + * + * Use @a scratch_pool for temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_io_file_create_bytes(const char *file, + const void *contents, + apr_size_t length, + apr_pool_t *scratch_pool); + +/** Create an empty file at utf8-encoded path @a file. + * + * @a file must not already exist. If an error occurs while + * closing the file, attempt to delete the file before returning the error. + * + * Use @a scratch_pool for temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_io_file_create_empty(const char *file, + apr_pool_t *scratch_pool); + /** * Lock file at @a lock_file. If @a exclusive is TRUE, * obtain exclusive lock, otherwise obtain shared lock. @@ -759,6 +798,12 @@ svn_io_unlock_open_file(apr_file_t *lockfile_handle, * Flush any unwritten data from @a file to disk. Use @a pool for * memory allocations. * + * @note This function uses advanced file control operations to flush buffers + * to disk that aren't always accessible and can be very expensive on systems + * that implement flushing on all IO layers, like Windows. Please avoid using + * this function in cases where the file should just work on any network + * filesystem. In many cases a normal svn_io_file_flush() will work just fine. + * * @since New in 1.1. */ svn_error_t * @@ -804,14 +849,16 @@ svn_io_dir_file_copy(const char *src_path, * 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. + * read or written. The write and full read handler 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.9 the stream api was extended to also support + * limited reads via the new svn_stream_read2() api. * - * 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(). + * In Subversion 1.7 mark, seek and 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; @@ -861,7 +908,15 @@ typedef svn_error_t *(*svn_stream_mark_fn_t)(void *baton, * @since New in 1.7. */ typedef svn_error_t *(*svn_stream_seek_fn_t)(void *baton, - const svn_stream_mark_t *mark); + const svn_stream_mark_t *mark); + +/** Poll handler for generic streams that support incomplete reads, @see + * svn_stream_t and svn_stream_data_available(). + * + * @since New in 1.9. + */ +typedef svn_error_t *(*svn_stream_data_available_fn_t)(void *baton, + svn_boolean_t *data_available); /** Create a generic stream. @see svn_stream_t. */ svn_stream_t * @@ -873,7 +928,24 @@ void svn_stream_set_baton(svn_stream_t *stream, void *baton); -/** Set @a stream's read function to @a read_fn */ +/** Set @a stream's read functions to @a read_fn and @a read_full_fn. If + * @a read_full_fn is NULL a default implementation based on multiple calls + * to @a read_fn will be used. + * + * @since New in 1.9. + */ +void +svn_stream_set_read2(svn_stream_t *stream, + svn_read_fn_t read_fn, + svn_read_fn_t read_full_fn); + +/** Set @a stream's read function to @a read_fn. + * + * This function sets only the full read function to read_fn. + * + * @deprecated Provided for backward compatibility with the 1.8 API. + */ +SVN_DEPRECATED void svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn); @@ -912,6 +984,14 @@ void svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn); +/** Set @a stream's data available function to @a data_available_fn + * + * @since New in 1.9. + */ +void +svn_stream_set_data_available(svn_stream_t *stream, + svn_stream_data_available_fn_t data_available); + /** Create a stream that is empty for reading and infinite for writing. */ svn_stream_t * svn_stream_empty(apr_pool_t *pool); @@ -1049,6 +1129,20 @@ svn_error_t * svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool); +/** Set @a *str to a string buffer allocated in @a result_pool that contains + * all data from the current position in @a stream to its end. @a len_hint + * specifies the initial capacity of the string buffer and may be 0. The + * buffer gets automatically resized to fit the actual amount of data being + * read from @a stream. + * + * @since New in 1.9. + */ +svn_error_t * +svn_stringbuf_from_stream(svn_stringbuf_t **str, + svn_stream_t *stream, + apr_size_t len_hint, + apr_pool_t *result_pool); + /** Return a generic stream connected to stringbuf @a str. Allocate the * stream in @a pool. */ @@ -1131,7 +1225,48 @@ svn_stream_checksummed(svn_stream_t *stream, svn_boolean_t read_all, apr_pool_t *pool); -/** Read from a generic stream. @see svn_stream_t. */ +/** Read from a generic stream until @a buffer is filled upto @a *len or + * until EOF is reached. @see svn_stream_t + * + * @since New in 1.9. + */ +svn_error_t * +svn_stream_read_full(svn_stream_t *stream, + char *buffer, + apr_size_t *len); + + +/** Returns @c TRUE if the generic @c stream supports svn_stream_read2(). + * + * @since New in 1.9. + */ +svn_boolean_t +svn_stream_supports_partial_read(svn_stream_t *stream); + +/** Read all currently available upto @a *len into @a buffer. Use + * svn_stream_read_full() if you want to wait for the buffer to be filled + * or EOF. If the stream doesn't support limited reads this function will + * return an #SVN_ERR_STREAM_NOT_SUPPORTED error. + * + * A 0 byte read signals the end of the stream. + * + * @since New in 1.9. + */ +svn_error_t * +svn_stream_read2(svn_stream_t *stream, + char *buffer, + apr_size_t *len); + + +/** Read from a generic stream until the buffer is completely filled or EOF. + * @see svn_stream_t. + * + * @note This function is a wrapper of svn_stream_read_full() now, which name + * better documents the behavior of this function. + * + * @deprecated Provided for backward compatibility with the 1.8 API + */ +SVN_DEPRECATED svn_error_t * svn_stream_read(svn_stream_t *stream, char *buffer, @@ -1209,6 +1344,20 @@ svn_stream_mark(svn_stream_t *stream, svn_error_t * svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark); +/** When a stream supports polling for available data, obtain a boolean + * indicating whether data is waiting to be read. If the stream doesn't + * support polling this function returns a #SVN_ERR_STREAM_NOT_SUPPORTED + * error. + * + * If the data_available callback is implemented and the stream is at the end + * the stream will set @a *data_available to FALSE. + * + * @since New in 1.9. + */ +svn_error_t * +svn_stream_data_available(svn_stream_t *stream, + svn_boolean_t *data_available); + /** 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 @@ -1405,6 +1554,10 @@ typedef svn_error_t * * 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. * + * Allocate the returned stream in @a result_pool. Also arrange for + * @a result_pool to be passed as the @c result_pool parameter to + * @a open_func when it is called. + * * @since New in 1.8. */ svn_stream_t * @@ -2067,6 +2220,28 @@ svn_io_file_seek(apr_file_t *file, apr_off_t *offset, apr_pool_t *pool); +/** Set the file pointer of the #APR_BUFFERED @a file to @a offset. In + * contrast to #svn_io_file_seek, this function will attempt to resize the + * internal data buffer to @a block_size bytes and to read data aligned to + * multiples of that value. The beginning of the block will be returned + * in @a buffer_start, if that is not NULL. + * Uses @a scratch_pool for temporary allocations. + * + * @note Due to limitations of the APR API, the alignment may not be + * successful. If you never use any other seek function on @a file, + * however, you are virtually guaranteed to get at least 4kByte alignment + * for all reads. + * + * @note Calling this for non-buffered files is legal but inefficient. + * + * @since New in 1.9 + */ +svn_error_t * +svn_io_file_aligned_seek(apr_file_t *file, + apr_off_t block_size, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *scratch_pool); /** Wrapper for apr_file_write(). */ svn_error_t * @@ -2075,6 +2250,14 @@ svn_io_file_write(apr_file_t *file, apr_size_t *nbytes, apr_pool_t *pool); +/** Wrapper for apr_file_flush(). + * @since New in 1.9 + */ +svn_error_t * +svn_io_file_flush(apr_file_t *file, + apr_pool_t *scratch_pool); + + /** Wrapper for apr_file_write_full(). */ svn_error_t * @@ -2084,6 +2267,29 @@ svn_io_file_write_full(apr_file_t *file, apr_size_t *bytes_written, apr_pool_t *pool); +/** + * Writes @a nbytes bytes from @a *buf to a temporary file inside the same + * directory as @a *final_path. Then syncs the temporary file to disk and + * closes the file. After this rename the temporary file to @a final_path, + * possibly replacing an existing file. + * + * If @a copy_perms_path is not NULL, copy the permissions applied on @a + * @a copy_perms_path on the temporary file before renaming. + * + * @note This function uses advanced file control operations to flush buffers + * to disk that aren't always accessible and can be very expensive. Avoid + * using this function in cases where the file should just work on any + * network filesystem. + * + * @since New in 1.9. + */ +svn_error_t * +svn_io_write_atomic(const char *final_path, + const void *buf, + apr_size_t nbytes, + const char* copy_perms_path, + apr_pool_t *scratch_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 diff --git a/contrib/subversion/subversion/include/svn_iter.h b/contrib/subversion/subversion/include/svn_iter.h index ab889358a..78044b830 100644 --- a/contrib/subversion/subversion/include/svn_iter.h +++ b/contrib/subversion/subversion/include/svn_iter.h @@ -77,7 +77,7 @@ svn_iter_apr_hash(svn_boolean_t *completed, void *baton, apr_pool_t *pool); -/** Iteration callback used in conjuction with svn_iter_apr_array(). +/** Iteration callback used in conjunction with svn_iter_apr_array(). * * Use @a pool for temporary allocation, it's cleared between invocations. * diff --git a/contrib/subversion/subversion/include/svn_mergeinfo.h b/contrib/subversion/subversion/include/svn_mergeinfo.h index ada70a200..ad1bd47f1 100644 --- a/contrib/subversion/subversion/include/svn_mergeinfo.h +++ b/contrib/subversion/subversion/include/svn_mergeinfo.h @@ -102,7 +102,7 @@ extern "C" { * both acceptable). */ -/* Suffix for SVN_PROP_MERGEINFO revision ranges indicating a given +/** Suffix for SVN_PROP_MERGEINFO revision ranges indicating a given range is non-inheritable. */ #define SVN_MERGEINFO_NONINHERITABLE_STR "*" @@ -113,39 +113,44 @@ extern "C" { * * (a) Strings (@c svn_string_t *) containing "unparsed mergeinfo". * - * (b) @c svn_rangelist_t, called a "rangelist". An array of non- - * overlapping merge ranges (@c svn_merge_range_t *), sorted as said by - * @c svn_sort_compare_ranges(). An empty range list is represented by - * an empty array. Unless specifically noted otherwise, all APIs require - * rangelists that describe only forward ranges, i.e. the range's start - * revision is less than its end revision. + * (b) @c svn_rangelist_t, called a "rangelist". * - * (c) @c svn_mergeinfo_t, called "mergeinfo". A hash mapping merge - * source paths (@c const char *, starting with slashes) to - * non-empty rangelist arrays. A @c NULL hash is used to represent - * no mergeinfo and an empty hash is used to represent empty - * mergeinfo. + * (c) @c svn_mergeinfo_t, called "mergeinfo". * - * (d) @c svn_mergeinfo_catalog_t, called a "mergeinfo catalog". A hash - * mapping paths (@c const char *) to @c svn_mergeinfo_t. + * (d) @c svn_mergeinfo_catalog_t, called a "mergeinfo catalog". * * Both @c svn_mergeinfo_t and @c svn_mergeinfo_catalog_t are just * typedefs for @c apr_hash_t *; there is no static type-checking, and * you still use standard @c apr_hash_t functions to interact with * them. - * - * Note that while the keys of mergeinfos are always absolute from the - * repository root, the keys of a catalog may be relative to something - * else, such as an RA session root. */ +/** An array of non-overlapping merge ranges (@c svn_merge_range_t *), + * sorted as said by @c svn_sort_compare_ranges(). An empty range list is + * represented by an empty array. + * + * Unless specifically noted otherwise, all APIs require rangelists that + * describe only forward ranges, i.e. the range's start revision is less + * than its end revision. */ typedef apr_array_header_t svn_rangelist_t; + +/** A hash mapping merge source paths to non-empty rangelist arrays. + * + * The keys are (@c const char *) absolute paths from the repository root, + * starting with slashes. A @c NULL hash represents no mergeinfo and an + * empty hash represents empty mergeinfo. */ typedef apr_hash_t *svn_mergeinfo_t; + +/** A hash mapping paths (@c const char *) to @c svn_mergeinfo_t. + * + * @note While the keys of #svn_mergeinfo_t are always absolute from the + * repository root, the keys of a catalog may be relative to something + * else, such as an RA session root. + * */ typedef apr_hash_t *svn_mergeinfo_catalog_t; /** Parse the mergeinfo from @a input into @a *mergeinfo. If no * mergeinfo is available, return an empty mergeinfo (never @c NULL). - * Perform temporary allocations in @a pool. * * If @a input is not a grammatically correct @c SVN_PROP_MERGEINFO * property, contains overlapping revision ranges of differing @@ -160,6 +165,9 @@ typedef apr_hash_t *svn_mergeinfo_catalog_t; * @a input may contain relative merge source paths, but these are * converted to absolute paths in @a *mergeinfo. * + * Allocate the result deeply in @a pool. Also perform temporary + * allocations in @a pool. + * * @since New in 1.5. */ svn_error_t * @@ -357,6 +365,8 @@ svn_rangelist_merge(svn_rangelist_t **rangelist, * @c svn_merge_range_t inheritable field when comparing @a whiteboard's * and @a *eraser's rangelists for equality. @see svn_mergeinfo_diff(). * + * Allocate the entire output in @a pool. + * * @since New in 1.5. */ svn_error_t * @@ -411,6 +421,9 @@ svn_mergeinfo_intersect(svn_mergeinfo_t *mergeinfo, * Note: @a rangelist1 and @a rangelist2 must be sorted as said by @c * svn_sort_compare_ranges(). @a *rangelist is guaranteed to be in sorted * order. + * + * Allocate the entire output in @a pool. + * * @since New in 1.5. */ svn_error_t * @@ -444,14 +457,21 @@ svn_rangelist_to_string(svn_string_t **output, const svn_rangelist_t *rangelist, apr_pool_t *pool); -/** Return a deep copy of @c svn_merge_range_t *'s in @a rangelist excluding +/** Remove non-inheritable or inheritable revision ranges from a rangelist. + * + * Set @a *inheritable_rangelist to a deep copy of @a rangelist, excluding * all non-inheritable @c svn_merge_range_t if @a inheritable is TRUE or - * excluding all inheritable @c svn_merge_range_t otherwise. If @a start and - * @a end are valid revisions and @a start is less than or equal to @a end, - * then exclude only the non-inheritable revision ranges that intersect - * inclusively with the range defined by @a start and @a end. If - * @a rangelist contains no elements, return an empty array. Allocate the - * copy in @a result_pool, use @a scratch_pool for temporary allocations. + * excluding all inheritable @c svn_merge_range_t otherwise. + * + * If @a start and @a end are valid revisions and @a start is less than or + * equal to @a end, then exclude only the (non-inheritable or inheritable) + * revision ranges that intersect inclusively with the range defined by + * @a start and @a end. + * + * If there are no remaining ranges, return an empty array. + * + * Allocate the copy in @a result_pool, and use @a scratch_pool for + * temporary allocations. * * @since New in 1.7. */ @@ -477,17 +497,26 @@ svn_rangelist_inheritable(svn_rangelist_t **inheritable_rangelist, svn_revnum_t end, apr_pool_t *pool); -/** Return a deep copy of @a mergeinfo, excluding all non-inheritable - * @c svn_merge_range_t if @a inheritable is TRUE or excluding all - * inheritable @c svn_merge_range_t otherwise. If @a start and @a end - * are valid revisions and @a start is less than or equal to @a end, - * then exclude only the non-inheritable revisions that intersect - * inclusively with the range defined by @a start and @a end. If @a path - * is not NULL remove non-inheritable ranges only for @a path. If all - * ranges are removed for a given path then remove that path as well. - * If all paths are removed or @a rangelist is empty then set - * @a *inheritable_rangelist to an empty array. Allocate the copy in - * @a result_pool, use @a scratch_pool for temporary allocations. +/** Remove non-inheritable or inheritable revision ranges from mergeinfo. + * + * Set @a *inheritable_mergeinfo to a deep copy of @a mergeinfo, excluding + * all non-inheritable @c svn_merge_range_t if @a inheritable is TRUE or + * excluding all inheritable @c svn_merge_range_t otherwise. + * + * If @a start and @a end are valid revisions and @a start is less than or + * equal to @a end, then exclude only the (non-inheritable or inheritable) + * revisions that intersect inclusively with the range defined by @a start + * and @a end. + * + * If @a path is not NULL remove (non-inheritable or inheritable) ranges + * only for @a path. + * + * If all ranges are removed for a given path then remove that path as well. + * If @a mergeinfo is initially empty or all paths are removed from it then + * set @a *inheritable_mergeinfo to an empty mergeinfo. + * + * Allocate the copy in @a result_pool, and use @a scratch_pool for + * temporary allocations. * * @since New in 1.7. */ diff --git a/contrib/subversion/subversion/include/svn_opt.h b/contrib/subversion/subversion/include/svn_opt.h index 25da44fd0..1c85b6168 100644 --- a/contrib/subversion/subversion/include/svn_opt.h +++ b/contrib/subversion/subversion/include/svn_opt.h @@ -24,8 +24,8 @@ * @brief Option and argument parsing for Subversion command lines */ -#ifndef SVN_OPTS_H -#define SVN_OPTS_H +#ifndef SVN_OPT_H +#define SVN_OPT_H #include #include @@ -656,6 +656,10 @@ svn_opt_parse_all_args(apr_array_header_t **args_p, * canonical form if @a path is in canonical form. * * @since New in 1.1. + * @since Since 1.6.5, this returns an error if @a path contains a peg + * specifier with no path before it, such as "@abc". + * @since Since 1.9.0, this no longer returns an error if @a path contains a peg + * specifier with no path before it, such as "@abc". */ svn_error_t * svn_opt_parse_path(svn_opt_revision_t *rev, @@ -776,4 +780,4 @@ svn_opt_print_help(apr_getopt_t *os, } #endif /* __cplusplus */ -#endif /* SVN_OPTS_H */ +#endif /* SVN_OPT_H */ diff --git a/contrib/subversion/subversion/include/svn_path.h b/contrib/subversion/subversion/include/svn_path.h index 347800391..20452c129 100644 --- a/contrib/subversion/subversion/include/svn_path.h +++ b/contrib/subversion/subversion/include/svn_path.h @@ -114,7 +114,7 @@ char * svn_path_join(const char *base, const char *component, apr_pool_t *pool); /** Join multiple components onto a @a base path, allocated in @a pool. The - * components are terminated by a @c NULL. + * components are terminated by a @c SVN_VA_NULL. * * If any component is the empty string, it will be ignored. * @@ -131,7 +131,9 @@ svn_path_join(const char *base, const char *component, apr_pool_t *pool); */ SVN_DEPRECATED char * -svn_path_join_many(apr_pool_t *pool, const char *base, ...); +svn_path_join_many(apr_pool_t *pool, + const char *base, + ...) SVN_NEEDS_SENTINEL_NULL; /** Get the basename of the specified canonicalized @a path. The @@ -716,10 +718,10 @@ svn_path_resolve_repos_relative_url(const char **absolute_url, const char *repos_root_url, apr_pool_t *pool); -/* Return a copy of @a path, allocated from @a pool, for which control - * characters have been escaped using the form \NNN (where NNN is the +/** Return a copy of @a path, allocated from @a pool, for which control + * characters have been escaped using the form "\NNN" (where NNN is the * octal representation of the byte's ordinal value). - * + * * @since New in 1.8. */ const char * svn_path_illegal_path_escape(const char *path, apr_pool_t *pool); diff --git a/contrib/subversion/subversion/include/svn_props.h b/contrib/subversion/subversion/include/svn_props.h index 1f2bbbf0e..e920b03de 100644 --- a/contrib/subversion/subversion/include/svn_props.h +++ b/contrib/subversion/subversion/include/svn_props.h @@ -440,13 +440,20 @@ svn_prop_name_is_valid(const char *prop_name); * @verbatim /trunk: 1-6,9,37-38 /trunk/foo: 10 @endverbatim + * @since New in 1.5. */ #define SVN_PROP_MERGEINFO SVN_PROP_PREFIX "mergeinfo" -/** Property used to record inheritable configuration auto-props. */ +/** Property used to record inheritable configuration auto-props. + * + * @since New in 1.8. + */ #define SVN_PROP_INHERITABLE_AUTO_PROPS SVN_PROP_PREFIX "auto-props" -/** Property used to record inheritable configuration ignores. */ +/** Property used to record inheritable configuration ignores. + * + * @since New in 1.8. + */ #define SVN_PROP_INHERITABLE_IGNORES SVN_PROP_PREFIX "global-ignores" /** Meta-data properties. @@ -671,7 +678,7 @@ svn_prop_name_is_valid(const char *prop_name); */ #define SVN_PROP_TXN_PREFIX SVN_PROP_PREFIX "txn-" -/** Identifies the client version compability level. For clients +/** Identifies the client version compatibility level. For clients * compiled against Subversion libraries, this is @c SVN_VER_NUMBER. * Third-party implementations are advised to use similar formatting * for values of this property. diff --git a/contrib/subversion/subversion/include/svn_ra.h b/contrib/subversion/subversion/include/svn_ra.h index cf6f6129a..a3ab672dd 100644 --- a/contrib/subversion/subversion/include/svn_ra.h +++ b/contrib/subversion/subversion/include/svn_ra.h @@ -134,7 +134,10 @@ typedef svn_error_t * apr_pool_t *pool); -/** A function type for retrieving the youngest revision from a repos. */ +/** A function type for retrieving the youngest revision from a repos. + * @deprecated Provided for backward compatibility with the 1.8 API. + */ +/* ### It seems this type was never used by the API, since 1.0.0. */ typedef svn_error_t *(*svn_ra_get_latest_revnum_func_t)( void *session_baton, svn_revnum_t *latest_revnum); @@ -267,6 +270,64 @@ typedef svn_error_t *(*svn_ra_replay_revfinish_callback_t)( apr_hash_t *rev_props, apr_pool_t *pool); + +/** + * Callback function that checks if an ra_svn tunnel called + * @a tunnel_name is handled by the callbakcs or the default + * implementation. + * + * @a tunnel_baton is the baton as originally passed to ra_open. + * + * @since New in 1.9. + */ +typedef svn_boolean_t (*svn_ra_check_tunnel_func_t)( + void *tunnel_baton, const char *tunnel_name); + +/** + * Callback function for closing a tunnel in ra_svn. + * + * This function will be called when the pool that owns the tunnel + * connection is cleared or destroyed. + * + * @a close_baton is the baton as returned from the + * svn_ra_open_tunnel_func_t. + * + * @a tunnel_baton was returned by the open-tunnel callback. + * + * @since New in 1.9. + */ +typedef void (*svn_ra_close_tunnel_func_t)( + void *close_baton, void *tunnel_baton); + +/** + * Callback function for opening a tunnel in ra_svn. + * + * Given the @a tunnel_name, tunnel @a user and server @a hostname and + * @a port, open a tunnel to the server and return its file handles, + * which are owned by @a pool, in @a request and @a response. + * + * @a request and @a response represent the standard input and output, + * respectively, of the process on the other end of the tunnel. + * + * If @a *close_func is set it will be called with @a close_baton when + * the tunnel is closed. + * + * The optional @a cancel_func callback can be invoked as usual to allow + * the user to preempt potentially lengthy operations. + * + * @a tunnel_baton is the baton as set in the callbacks. + * + * @since New in 1.9. + */ +typedef svn_error_t *(*svn_ra_open_tunnel_func_t)( + svn_stream_t **request, svn_stream_t **response, + svn_ra_close_tunnel_func_t *close_func, void **close_baton, + void *tunnel_baton, + const char *tunnel_name, const char *user, + const char *hostname, int port, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool); + /** * The update Reporter. @@ -535,6 +596,31 @@ typedef struct svn_ra_callbacks2_t */ svn_ra_get_wc_contents_func_t get_wc_contents; + /** Check-tunnel callback + * + * If not @c NULL, and open_tunnel_func is also not @c NULL, this + * callback will be invoked to check if open_tunnel_func should be + * used to create a specific tunnel, or if the default tunnel + * implementation (either built-in or configured in the client + * configuration file) should be used instead. + * @since New in 1.9. + */ + svn_ra_check_tunnel_func_t check_tunnel_func; + + /** Open-tunnel callback + * + * If not @c NULL, this callback will be invoked to create a tunnel + * for a ra_svn connection that needs one, overriding any tunnel + * definitions in the client config file. This callback is used only + * for ra_svn and ignored by the other RA modules. + * @since New in 1.9. + */ + svn_ra_open_tunnel_func_t open_tunnel_func; + + /** A baton used with open_tunnel_func and close_tunnel_func. + * @since New in 1.9. + */ + void *tunnel_baton; } svn_ra_callbacks2_t; /** Similar to svn_ra_callbacks2_t, except that the progress @@ -899,6 +985,10 @@ svn_ra_rev_prop(svn_ra_session_t *session, * Use @a pool for memory allocation. * * @since New in 1.5. + * + * @note Like most commit editors, the returned editor requires that the + * @c copyfrom_path parameter passed to its @c add_file and @c add_directory + * methods is a URL, not a relative path. */ svn_error_t * svn_ra_get_commit_editor3(svn_ra_session_t *session, @@ -1004,11 +1094,10 @@ svn_ra_get_file(svn_ra_session_t *session, * callers want to know, and some don't.) * * If @a props is non @c NULL, set @a *props to contain the properties of - * the directory. This means @em all properties: not just ones controlled by - * the user and stored in the repository fs, but non-tweakable ones - * generated by the SCM system itself (e.g. 'wcprops', 'entryprops', - * etc.) The keys are const char *, values are - * @c svn_string_t *. + * the directory, including properties that are non-tweakable and + * generated by the SCM system itself (such as #svn_prop_wc_kind and + * #svn_prop_entry_kind properties). The keys are const char *, + * values are @c svn_string_t *. * * @since New in 1.4. */ @@ -1463,8 +1552,8 @@ svn_ra_do_diff(svn_ra_session_t *session, * was added or deleted). Each path is an const char *, relative * to the @a session's common parent. * - * If @a limit is non-zero only invoke @a receiver on the first @a limit - * logs. + * If @a limit is greater than zero only invoke @a receiver on the first + * @a limit logs. * * If @a discover_changed_paths, then each call to @a receiver passes a * const apr_hash_t * for the receiver's @a changed_paths argument; @@ -1507,7 +1596,6 @@ svn_ra_do_diff(svn_ra_session_t *session, * * @since New in 1.5. */ - svn_error_t * svn_ra_get_log2(svn_ra_session_t *session, const apr_array_header_t *paths, @@ -1708,6 +1796,12 @@ svn_ra_get_location_segments(svn_ra_session_t *session, * to support reversion of the revision range for @a include_merged_revision * @c FALSE reporting by switching @a end with @a start. * + * @note Prior to Subversion 1.9, this function may request delta handlers + * from @a handler even for empty text deltas. Starting with 1.9, the + * delta handler / baton return arguments passed to @a handler will be + * #NULL unless there is an actual difference in the file contents between + * the current and the previous call. + * * @since New in 1.5. */ svn_error_t * @@ -1811,8 +1905,12 @@ svn_ra_unlock(svn_ra_session_t *session, /** * If @a path is locked, set @a *lock to an svn_lock_t which - * represents the lock, allocated in @a pool. If @a path is not - * locked, set @a *lock to NULL. + * represents the lock, allocated in @a pool. + * + * If @a path is not locked or does not exist in HEAD, set @a *lock to NULL. + * + * @note Before 1.9, this function could return SVN_ERR_FS_NOT_FOUND + * when @a path didn't exist in HEAD on specific ra layers. * * @since New in 1.2. */ diff --git a/contrib/subversion/subversion/include/svn_ra_svn.h b/contrib/subversion/subversion/include/svn_ra_svn.h index 9c556b8b8..c968e4008 100644 --- a/contrib/subversion/subversion/include/svn_ra_svn.h +++ b/contrib/subversion/subversion/include/svn_ra_svn.h @@ -165,9 +165,9 @@ typedef struct svn_ra_svn_item_t typedef svn_error_t *(*svn_ra_svn_edit_callback)(void *baton); /** Initialize a connection structure for the given socket or - * input/output files. + * input/output streams. * - * Either @a sock or @a in_file/@a out_file must be set, not both. + * Either @a sock or @a in_stream/@a out_stream must be set, not both. * @a compression_level specifies the desired network data compression * level (zlib) from 0 (no compression) to 9 (best but slowest). * @@ -184,10 +184,29 @@ typedef svn_error_t *(*svn_ra_svn_edit_callback)(void *baton); * It defines the number of bytes that must have been sent since the last * check before the next check will be made. * + * @note If @a out_stream is an wrapped apr_file_t* the backing file will be + * used for some operations. + * * Allocate the result in @a pool. * + * @since New in 1.9 + */ +svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock, + svn_stream_t *in_stream, + svn_stream_t *out_stream, + int compression_level, + apr_size_t zero_copy_limit, + apr_size_t error_check_interval, + apr_pool_t *result_pool); + + +/** Similar to svn_ra_svn_create_conn4() but only supports apr_file_t handles + * instead of the more generic streams. + * * @since New in 1.8 + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, apr_file_t *in_file, apr_file_t *out_file, @@ -268,6 +287,12 @@ svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn); * * Upon successful completion of the edit, the editor will invoke @a callback * with @a callback_baton as an argument. + * + * @note The @c copyfrom_path parameter passed to the @c add_file and + * @c add_directory methods of the returned editor may be either a URL or a + * relative path, and is transferred verbatim to the receiving end of the + * connection. See svn_ra_svn_drive_editor2() for information on the + * receiving end of the connection. */ void svn_ra_svn_get_editor(const svn_delta_editor_t **editor, @@ -283,6 +308,13 @@ svn_ra_svn_get_editor(const svn_delta_editor_t **editor, * if @a for_replay is TRUE. * * @since New in 1.4. + * + * @note The @c copyfrom_path parameter passed to the @c add_file and + * @c add_directory methods of the receiving editor will be canonicalized + * either as a URL or as a relative path (starting with a slash) according + * to which kind was sent by the driving end of the connection. See + * svn_ra_svn_get_editor() for information on the driving end of the + * connection. */ svn_error_t * svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, diff --git a/contrib/subversion/subversion/include/svn_repos.h b/contrib/subversion/subversion/include/svn_repos.h index 6ffa84257..45b7a8fe5 100644 --- a/contrib/subversion/subversion/include/svn_repos.h +++ b/contrib/subversion/subversion/include/svn_repos.h @@ -66,20 +66,12 @@ enum svn_node_action svn_node_action_replace }; -/** The different policies for processing the UUID in the dumpfile. */ -enum svn_repos_load_uuid -{ - /** only update uuid if the repos has no revisions. */ - svn_repos_load_uuid_default, - /** never update uuid. */ - svn_repos_load_uuid_ignore, - /** always update uuid. */ - svn_repos_load_uuid_force -}; - -/** Callback type for checking authorization on paths produced by (at - * least) svn_repos_dir_delta2(). +/** @defgroup svn_repos_authz_callbacks Repository authorization callbacks + * @{ + */ + +/** Callback type for checking authorization on a path. * * Set @a *allowed to TRUE to indicate that some operation is * authorized for @a path in @a root, or set it to FALSE to indicate @@ -167,24 +159,13 @@ typedef svn_error_t *(*svn_repos_authz_callback_t) void *baton, apr_pool_t *pool); -/** - * Similar to #svn_file_rev_handler_t, but without the @a - * result_of_merge parameter. - * - * @deprecated Provided for backward compatibility with 1.4 API. - * @since New in 1.1. +/** @} */ + + +/** @defgroup svn_repos_notifications Repository notifications + * @{ */ -typedef svn_error_t *(*svn_repos_file_rev_handler_t) - (void *baton, - const char *path, - svn_revnum_t rev, - apr_hash_t *rev_props, - svn_txdelta_window_handler_t *delta_handler, - void **delta_baton, - apr_array_header_t *prop_diffs, - apr_pool_t *pool); - /* Notification system. */ /** The type of action occurring. @@ -251,11 +232,22 @@ typedef enum svn_repos_notify_action_t svn_repos_notify_load_skipped_rev, /** The structure of a revision is being verified. @since New in 1.8. */ - svn_repos_notify_verify_rev_structure + svn_repos_notify_verify_rev_structure, + + /** A revprop shard got packed. @since New in 1.9. */ + svn_repos_notify_pack_revprops, + /** A non-packed revprop shard got removed. @since New in 1.9. */ + svn_repos_notify_cleanup_revprops, + + /** The repository format got bumped. @since New in 1.9. */ + svn_repos_notify_format_bumped, + + /** A revision range was copied. @since New in 1.9. */ + svn_repos_notify_hotcopy_rev_range } svn_repos_notify_action_t; -/** The type of error occurring. +/** The type of warning occurring. * * @since New in 1.7. */ @@ -273,8 +265,33 @@ typedef enum svn_repos_notify_warning_t * @see svn_fs.h:"Directory entry names and directory paths" */ /* ### TODO(doxygen): make that a proper doxygen link */ /* See svn_fs__path_valid(). */ - svn_repos_notify_warning_invalid_fspath + svn_repos_notify_warning_invalid_fspath, + + /** + * Detected a name collision. Reported when the names of two or more + * entries in the same directory differ only in character + * representation (normalization), but are otherwise identical. + * + * @since New in 1.9. + */ + svn_repos_notify_warning_name_collision, + /** + * Detected a mergeinfo path collision. Reported when the paths in + * two or more entries in the same svn:mergeinfo property differ + * only in character representation (normalization), but are + * otherwise identical. + * + * @since New in 1.9. + */ + svn_repos_notify_warning_mergeinfo_collision, + + /** + * Detected invalid mergeinfo. + * + * @since New in 1.9. + */ + svn_repos_notify_warning_invalid_mergeinfo } svn_repos_notify_warning_t; /** @@ -295,25 +312,30 @@ typedef struct svn_repos_notify_t svn_repos_notify_action_t action; /** For #svn_repos_notify_dump_rev_end and #svn_repos_notify_verify_rev_end, - * the revision which just completed. */ + * the revision which just completed. + * For #svn_fs_upgrade_format_bumped, the new format version. */ svn_revnum_t revision; - /** For #svn_repos_notify_warning, the warning object. */ + /** For #svn_repos_notify_warning, the warning message. */ const char *warning_str; + /** For #svn_repos_notify_warning, the warning type. */ svn_repos_notify_warning_t warning; /** For #svn_repos_notify_pack_shard_start, #svn_repos_notify_pack_shard_end, + #svn_repos_notify_pack_revprops, + #svn_repos_notify_cleanup_revprops #svn_repos_notify_pack_shard_start_revprop, and #svn_repos_notify_pack_shard_end_revprop, the shard processed. */ apr_int64_t shard; - /** For #svn_repos_notify_load_node_done, the revision committed. */ + /** For #svn_repos_notify_load_txn_committed, the revision committed. */ svn_revnum_t new_revision; - /** For #svn_repos_notify_load_node_done, the source revision, if + /** For #svn_repos_notify_load_txn_committed, the source revision, if different from @a new_revision, otherwise #SVN_INVALID_REVNUM. - For #svn_repos_notify_load_txn_start, the source revision. */ + For #svn_repos_notify_load_txn_start and + #svn_repos_notify_load_skipped_rev, the source revision. */ svn_revnum_t old_revision; /** For #svn_repos_notify_load_node_start, the action being taken on the @@ -323,6 +345,16 @@ typedef struct svn_repos_notify_t /** For #svn_repos_notify_load_node_start, the path of the node. */ const char *path; + /** For #svn_repos_notify_hotcopy_rev_range, the start of the copied + revision range. + @since New in 1.9. */ + svn_revnum_t start_revision; + + /** For #svn_repos_notify_hotcopy_rev_range, the end of the copied + revision range (might be the same as @a start_revision). + @since New in 1.9. */ + svn_revnum_t end_revision; + /* NOTE: Add new fields at the end to preserve binary compatibility. Also, if you add fields here, you have to update svn_repos_notify_create(). */ @@ -347,7 +379,9 @@ svn_repos_notify_t * svn_repos_notify_create(svn_repos_notify_action_t action, apr_pool_t *result_pool); - + /** @} */ + + /** The repository object. */ typedef struct svn_repos_t svn_repos_t; @@ -366,16 +400,31 @@ svn_repos_find_root_path(const char *path, /** Set @a *repos_p to a repository object for the repository at @a path. * - * Allocate @a *repos_p in @a pool. + * Allocate @a *repos_p in @a result_pool. * * Acquires a shared lock on the repository, and attaches a cleanup - * function to @a pool to remove the lock. If no lock can be acquired, + * function to @a result_pool to remove the lock. If no lock can be acquired, * returns error, with undefined effect on @a *repos_p. If an exclusive * lock is present, this blocks until it's gone. @a fs_config will be * passed to the filesystem initialization function and may be @c NULL. * + * Use @a scratch_pool for temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_open3(svn_repos_t **repos_p, + const char *path, + apr_hash_t *fs_config, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Similar to svn_repos_open3() but without @a scratch_pool. + * + * @deprecated Provided for backward compatibility with 1.8 API. * @since New in 1.7. */ +SVN_DEPRECATED svn_error_t * svn_repos_open2(svn_repos_t **repos_p, const char *path, @@ -478,6 +527,11 @@ svn_error_t * svn_repos_delete(const char *path, apr_pool_t *pool); + +/** @defgroup svn_repos_capabilities Repository capabilities + * @{ + */ + /** * Set @a *has to TRUE if @a repos has @a capability (one of the * capabilities beginning with @c "SVN_REPOS_CAPABILITY_"), else set @@ -496,7 +550,27 @@ svn_repos_has_capability(svn_repos_t *repos, const char *capability, apr_pool_t *pool); -/** @} */ +/** + * Return a set of @a capabilities supported by the running Subversion + * library and by @a repos. (Capabilities supported by this version of + * Subversion but not by @a repos are not listed. This may happen when + * svn_repos_upgrade2() has not been called after a software upgrade.) + * + * The set is represented as a hash whose const char * keys are the set + * members. The values are not defined. + * + * Allocate @a capabilities in @a result_pool and use @a scratch_pool for + * temporary allocations. + * + * @see svn_repos_info_format + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_capabilities(apr_hash_t **capabilities, + svn_repos_t *repos, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** * The capability of doing the right thing with merge-tracking @@ -516,11 +590,46 @@ svn_repos_has_capability(svn_repos_t *repos, * If you add a capability, update svn_repos_capabilities(). */ +/** @} */ + + +/** + * Store in @a repos the client-reported capabilities @a capabilities, + * which must be allocated in memory at least as long-lived as @a repos. + * + * The elements of @a capabilities are 'const char *', a subset of + * the constants beginning with @c SVN_RA_CAPABILITY_. + * @a capabilities is not copied, so changing it later will affect + * what is remembered by @a repos. + * + * @note The capabilities are passed along to the start-commit hook; + * see that hook's template for details. + * + * @note As of Subversion 1.5, there are no error conditions defined, + * so this always returns SVN_NO_ERROR. In future releases it may + * return error, however, so callers should check. + * + * @since New in 1.5. + */ +svn_error_t * +svn_repos_remember_client_capabilities(svn_repos_t *repos, + const apr_array_header_t *capabilities); + /** Return the filesystem associated with repository object @a repos. */ svn_fs_t * svn_repos_fs(svn_repos_t *repos); +/** Return the type of filesystem associated with repository object + * @a repos allocated in @a result_pool. + * + * @see #svn_fs_backend_names + * + * @since New in 1.9. + */ +const char * +svn_repos_fs_type(svn_repos_t *repos, + apr_pool_t *result_pool); /** Make a hot copy of the Subversion repository found at @a src_path * to @a dst_path. @@ -535,8 +644,41 @@ svn_repos_fs(svn_repos_t *repos); * already present in the destination. If incremental hotcopy is not * implemented by the filesystem backend, raise SVN_ERR_UNSUPPORTED_FEATURE. * + * For each revision range copied, the @a notify_func function will be + * called with the @a notify_baton and a notification structure containing + * appropriate values in @c start_revision and @c end_revision (both + * inclusive). @c start_revision might be equal to @c end_revision in + * case the copied range consists of a single revision. Currently, this + * notification is not triggered by the BDB backend. @a notify_func + * may be @c NULL if this notification is not required. + * + * The optional @a cancel_func callback will be invoked with + * @a cancel_baton as usual to allow the user to preempt this potentially + * lengthy operation. + * + * Use @a scratch_pool for temporary allocations. + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_hotcopy3(const char *src_path, + const char *dst_path, + svn_boolean_t clean_logs, + svn_boolean_t incremental, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** + * Like svn_repos_hotcopy3(), but with @a notify_func and @a notify_baton + * always passed as @c NULL. + * * @since New in 1.8. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_repos_hotcopy2(const char *src_path, const char *dst_path, @@ -682,6 +824,11 @@ typedef svn_error_t *(*svn_repos_freeze_func_t)(void *baton, apr_pool_t *pool); * FS backend the repository uses. Repositories are locked in the * order in which they are specified in the array. * + * @note @a freeze_func must not, directly or indirectly, call any function + * that attempts to take out a lock on the underlying repository. These + * include functions for packing, hotcopying, setting revprops and commits. + * Attempts to do so may result in a deadlock. + * * @note On some platforms the exclusive lock does not exclude other * threads in the same process so this function should only be called * by a single threaded process, or by a multi-threaded process when @@ -879,7 +1026,7 @@ svn_repos_hooks_setenv(svn_repos_t *repos, * when using larger values here. Pass 0 for @a zero_copy_limit to * disable this optimization altogether. * - * @a note Never activate this optimization if @a editor might access + * @note Never activate this optimization if @a editor might access * any FSFS data structures (and, hence, caches). So, it is basically * safe for networked editors only. * @@ -1355,10 +1502,9 @@ svn_repos_replay(svn_fs_root_t *root, * filesystem of @a repos, beginning at location 'rev:@a base_path', * where "rev" is the argument given to open_root(). * - * @a repos is a previously opened repository. @a repos_url is the + * @a repos is a previously opened repository. @a repos_url_decoded is the * decoded URL to the base of the repository, and is used to check - * copyfrom paths. copyfrom paths passed to the editor must be full, - * URI-encoded, URLs. @a txn is a filesystem transaction object to use + * copyfrom paths. @a txn is a filesystem transaction object to use * during the commit, or @c NULL to indicate that this function should * create (and fully manage) a new transaction. * @@ -1380,29 +1526,34 @@ svn_repos_replay(svn_fs_root_t *root, * If @a commit_callback is non-NULL, then before @c close_edit returns (but * after the commit has succeeded) @c close_edit will invoke * @a commit_callback with a filled-in #svn_commit_info_t *, @a commit_baton, - * and @a pool or some subpool thereof as arguments. If @a commit_callback + * and @a pool or some subpool thereof as arguments. The @c repos_root field + * of the #svn_commit_info_t is @c NULL. If @a commit_callback * returns an error, that error will be returned from @c close_edit, * otherwise if there was a post-commit hook failure, then that error * will be returned with code SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED. - * (Note that prior to Subversion 1.6, @a commit_callback cannot be NULL; if - * you don't need a callback, pass a dummy function.) + * (Note that prior to Subversion 1.6, @a commit_callback cannot be @c NULL; + * if you don't need a callback, pass a dummy function.) * * Calling @a (*editor)->abort_edit aborts the commit, and will also * abort the commit transaction unless @a txn was supplied (not @c * NULL). Callers who supply their own transactions are responsible * for cleaning them up (either by committing them, or aborting them). * - * @since New in 1.5. + * @since New in 1.5. Since 1.6, @a commit_callback can be @c NULL. * - * @note Yes, @a repos_url is a decoded URL. We realize + * @note Yes, @a repos_url_decoded is a decoded URL. We realize * that's sorta wonky. Sorry about that. + * + * @note Like most commit editors, the returned editor requires that the + * @c copyfrom_path parameter passed to its @c add_file and @c add_directory + * methods is a full, URI-encoded URL, not a relative path. */ 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 *repos_url_decoded, const char *base_path, apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, @@ -1709,16 +1860,6 @@ svn_repos_node_location_segments(svn_repos_t *repos, apr_pool_t *pool); -/* ### other queries we can do someday -- - - * fetch the last revision created by - (once usernames become revision properties!) - * fetch the last revision where was modified - -*/ - - - /* ---------------------------------------------------------------*/ /* Retrieving log messages. */ @@ -1740,7 +1881,7 @@ svn_repos_node_location_segments(svn_repos_t *repos, * show all revisions regardless of what paths were changed in those * revisions. * - * If @a limit is non-zero then only invoke @a receiver on the first + * If @a limit is greater than zero then only invoke @a receiver on the first * @a limit logs. * * If @a discover_changed_paths, then each call to @a receiver passes a @@ -1946,6 +2087,12 @@ svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, * the revision range for @a include_merged_revision @c FALSE reporting by * switching @a start with @a end. * + * @note Prior to Subversion 1.9, this function may request delta handlers + * from @a handler even for empty text deltas. Starting with 1.9, the + * delta handler / baton return arguments passed to @a handler will be + * #NULL unless there is an actual difference in the file contents between + * the current and the previous call. + * * @since New in 1.5. */ svn_error_t * @@ -1960,6 +2107,23 @@ svn_repos_get_file_revs2(svn_repos_t *repos, void *handler_baton, apr_pool_t *pool); +/** + * Similar to #svn_file_rev_handler_t, but without the @a + * result_of_merge parameter. + * + * @deprecated Provided for backward compatibility with 1.4 API. + * @since New in 1.1. + */ +typedef svn_error_t *(*svn_repos_file_rev_handler_t) + (void *baton, + const char *path, + svn_revnum_t rev, + apr_hash_t *rev_props, + svn_txdelta_window_handler_t *delta_handler, + void **delta_baton, + apr_array_header_t *prop_diffs, + apr_pool_t *pool); + /** * Similar to svn_repos_get_file_revs2(), with @a include_merged_revisions * set to FALSE. @@ -2087,19 +2251,50 @@ svn_repos_fs_begin_txn_for_update(svn_fs_txn_t **txn_p, * @{ */ -/** Like svn_fs_lock(), but invoke the @a repos's pre- and - * post-lock hooks before and after the locking action. Use @a pool - * for any necessary allocations. +/** Like svn_fs_lock_many(), but invoke the @a repos's pre- and + * post-lock hooks before and after the locking action. + * + * The pre-lock is run for every path in @a targets. Those targets for + * which the pre-lock is successful are passed to svn_fs_lock_many and + * the post-lock is run for those that are successfully locked. + * Pre-lock hook errors are passed to @a lock_callback. * - * If the pre-lock hook or svn_fs_lock() fails, throw the original - * error to caller. If an error occurs when running the post-lock - * hook, return the original error wrapped with - * SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED. If the caller sees this - * error, it knows that the lock succeeded anyway. + * For each path in @a targets @a lock_callback will be invoked + * passing @a lock_baton and the lock and error that apply to path. + * @a lock_callback can be NULL in which case it is not called and any + * errors that would have been passed to the callback are not reported. + * + * If an error occurs when running the post-lock hook the error is + * returned wrapped with #SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED. If the + * caller sees this error, it knows that some locks succeeded. * * The pre-lock hook may cause a different token to be used for the - * lock, instead of @a token; see the pre-lock-hook documentation for - * more. + * lock, instead of the token supplied; see the pre-lock-hook + * documentation for more. + * + * The lock and path passed to @a lock_callback will be allocated in + * @a result_pool. Use @a scratch_pool for temporary allocations. + * + * @note This function is not atomic. If it returns an error, some targets + * may remain unlocked while others may have been locked. + * + * @see svn_fs_lock_many + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_fs_lock_many(svn_repos_t *repos, + apr_hash_t *lock_targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Similar to svn_repos_fs_lock_many() but locks only a single path. * * @since New in 1.2. */ @@ -2116,15 +2311,46 @@ svn_repos_fs_lock(svn_lock_t **lock, apr_pool_t *pool); -/** Like svn_fs_unlock(), but invoke the @a repos's pre- and - * post-unlock hooks before and after the unlocking action. Use @a - * pool for any necessary allocations. +/** Like svn_fs_unlock_many(), but invoke the @a repos's pre- and + * post-unlock hooks before and after the unlocking action. + * + * The pre-unlock hook is run for every path in @a targets. Those + * targets for which the pre-unlock is successful are passed to + * svn_fs_unlock_many and the post-unlock is run for those that are + * successfully unlocked. Pre-unlock hook errors are passed to @a + * lock_callback. + * + * For each path in @a targets @a lock_callback will be invoked + * passing @a lock_baton and error that apply to path. The lock + * passed to the callback will be NULL. @a lock_callback can be NULL + * in which case it is not called and any errors that would have been + * passed to the callback are not reported. + * + * If an error occurs when running the post-unlock hook, return the + * original error wrapped with #SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED. + * If the caller sees this error, it knows that some unlocks + * succeeded. + * + * The path passed to @a lock_callback will be allocated in @a result_pool. + * Use @a scratch_pool for temporary allocations. * - * If the pre-unlock hook or svn_fs_unlock() fails, throw the original - * error to caller. If an error occurs when running the post-unlock - * hook, return the original error wrapped with - * SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED. If the caller sees this - * error, it knows that the unlock succeeded anyway. + * @note This function is not atomic. If it returns an error, some targets + * may remain locked while others may have been unlocked. + * + * @see svn_fs_unlock_many + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_fs_unlock_many(svn_repos_t *repos, + apr_hash_t *unlock_targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** Similar to svn_repos_fs_unlock_many() but only unlocks a single path. * * @since New in 1.2. */ @@ -2178,6 +2404,12 @@ svn_repos_fs_get_locks(apr_hash_t **locks, /** @} */ +/** @defgroup svn_repos_properties Versioned and Unversioned Properties + * + * Prop-changing and prop-reading wrappers for libsvn_fs routines. + * @{ + */ + /** * Like svn_fs_change_rev_prop2(), but validate the name and value of the * property and invoke the @a repos's pre- and post-revprop-change hooks @@ -2210,12 +2442,9 @@ svn_repos_fs_change_rev_prop4(svn_repos_t *repos, const char *name, const svn_string_t *const *old_value_p, const svn_string_t *new_value, - svn_boolean_t - use_pre_revprop_change_hook, - svn_boolean_t - use_post_revprop_change_hook, - svn_repos_authz_func_t - authz_read_func, + svn_boolean_t use_pre_revprop_change_hook, + svn_boolean_t use_post_revprop_change_hook, + svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool); @@ -2234,12 +2463,9 @@ svn_repos_fs_change_rev_prop3(svn_repos_t *repos, const char *author, const char *name, const svn_string_t *new_value, - svn_boolean_t - use_pre_revprop_change_hook, - svn_boolean_t - use_post_revprop_change_hook, - svn_repos_authz_func_t - authz_read_func, + svn_boolean_t use_pre_revprop_change_hook, + svn_boolean_t use_post_revprop_change_hook, + svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool); @@ -2257,8 +2483,7 @@ svn_repos_fs_change_rev_prop2(svn_repos_t *repos, const char *author, const char *name, const svn_string_t *new_value, - svn_repos_authz_func_t - authz_read_func, + svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool); @@ -2299,8 +2524,7 @@ svn_repos_fs_revision_prop(svn_string_t **value_p, svn_repos_t *repos, svn_revnum_t rev, const char *propname, - svn_repos_authz_func_t - authz_read_func, + svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool); @@ -2325,21 +2549,10 @@ svn_error_t * svn_repos_fs_revision_proplist(apr_hash_t **table_p, svn_repos_t *repos, svn_revnum_t rev, - svn_repos_authz_func_t - authz_read_func, + svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool); - - -/* ---------------------------------------------------------------*/ - -/* Prop-changing wrappers for libsvn_fs routines. */ - -/* NOTE: svn_repos_fs_change_rev_prop() also exists, but is located - above with the hook-related functions. */ - - /** Validating wrapper for svn_fs_change_node_prop() (which see for * argument descriptions). * @@ -2348,9 +2561,9 @@ svn_repos_fs_revision_proplist(apr_hash_t **table_p, * @a value and return SVN_ERR_BAD_PROPERTY_VALUE if it is invalid for the * property. * - * @note Currently, the only properties validated are the "svn:" properties - * #SVN_PROP_REVISION_LOG and #SVN_PROP_REVISION_DATE. This may change - * in future releases. + * @note Originally, the only properties validated were the "svn:" properties + * #SVN_PROP_REVISION_LOG and #SVN_PROP_REVISION_DATE. For the current + * validation rules see the private function svn_repos__validate_prop(). */ svn_error_t * svn_repos_fs_change_node_prop(svn_fs_root_t *root, @@ -2359,6 +2572,36 @@ svn_repos_fs_change_node_prop(svn_fs_root_t *root, const svn_string_t *value, apr_pool_t *pool); +/** + * Set @a *inherited_values to a depth-first ordered array of + * #svn_prop_inherited_item_t * structures (the path_or_url members of + * which are relative filesystem paths) representing the properties + * inherited by @a path in @a root. If no properties are inherited, + * then set @a *inherited_values to an empty array. + * + * if @a propname is NULL then retrieve all explicit and/or inherited + * properties. Otherwise retrieve only the properties named @a propname. + * + * If optional @a authz_read_func is non-NULL, then use this function + * (along with optional @a authz_read_baton) to check the readability + * of each parent path from which properties are inherited. Silently omit + * properties for unreadable parent paths. + * + * Allocate @a *inherited_props in @a result_pool. Use @a scratch_pool for + * temporary allocations. + * + * @since New in 1.8. + */ +svn_error_t * +svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props, + svn_fs_root_t *root, + const char *path, + const char *propname, + svn_repos_authz_func_t authz_read_func, + void *authz_read_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /** Validating wrapper for svn_fs_change_txn_prop() (which see for * argument descriptions). See svn_repos_fs_change_txn_props() for more * information. @@ -2380,6 +2623,8 @@ svn_repos_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props, apr_pool_t *pool); +/** @} */ + /* ---------------------------------------------------------------*/ @@ -2470,12 +2715,34 @@ svn_repos_node_editor(const svn_delta_editor_t **editor, svn_repos_node_t * svn_repos_node_from_baton(void *edit_baton); +/** + * Return repository format information for @a repos. + * + * Set @a *repos_format to the repository format number of @a repos, which is + * an integer that increases when incompatible changes are made (such as + * by #svn_repos_upgrade2). + * + * Set @a *supports_version to the version number of the minimum Subversion + * GA release that can read and write @a repos; allocate it in + * @a result_pool. Use @a scratch_pool for temporary allocations. + * + * @see svn_fs_info_format, svn_repos_capabilities + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_info_format(int *repos_format, + svn_version_t **supports_version, + svn_repos_t *repos, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /** @} */ /* ---------------------------------------------------------------*/ /** - * @defgroup svn_repos_dump_load Dumping and loading filesystem data + * @defgroup svn_repos_dump_load Dumping, loading and verifying filesystem data * @{ * * The filesystem 'dump' format contains nothing but the abstract @@ -2539,26 +2806,132 @@ svn_repos_node_from_baton(void *edit_baton); #define SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM \ SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 -/** - * Verify the contents of the file system in @a repos. +/** The different policies for processing the UUID in the dumpfile. */ +enum svn_repos_load_uuid +{ + /** only update uuid if the repos has no revisions. */ + svn_repos_load_uuid_default, + /** never update uuid. */ + svn_repos_load_uuid_ignore, + /** always update uuid. */ + svn_repos_load_uuid_force +}; + +/** Callback type for use with svn_repos_verify_fs3(). @a revision + * and @a verify_err are the details of a single verification failure + * that occurred during the svn_repos_verify_fs3() call. @a baton is + * the same baton given to svn_repos_verify_fs3(). @a scratch_pool is + * provided for the convenience of the implementor, who should not + * expect it to live longer than a single callback call. * - * If @a feedback_stream is not @c NULL, write feedback to it (lines of - * the form "* Verified revision %ld\n"). + * @a verify_err will be cleared and becomes invalid after the callback + * returns, use svn_error_dup() to preserve the error. If a callback uses + * @a verify_err as the return value or as a part of the return value, it + * should also call svn_error_dup() for @a verify_err. Implementors of this + * callback are forbidden to call svn_error_clear() for @a verify_err. + * + * @see svn_repos_verify_fs3 * - * If @a start_rev is #SVN_INVALID_REVNUM, then start verifying at - * revision 0. If @a end_rev is #SVN_INVALID_REVNUM, then verify - * through the @c HEAD revision. + * @since New in 1.9. + */ +typedef svn_error_t *(*svn_repos_verify_callback_t)(void *baton, + svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool); + +/** + * Verify the contents of the file system in @a repos. * - * For every verified revision call @a notify_func with @a rev set to - * the verified revision and @a warning_text @c NULL. For warnings call @a - * notify_func with @a warning_text set. + * Verify the revisions from @a start_rev to @a end_rev inclusive. If + * @a start_rev is #SVN_INVALID_REVNUM, start at revision 0; if @a end_rev + * is #SVN_INVALID_REVNUM, end at the head revision. @a start_rev must be + * older than or equal to @a end_rev. If revision 0 is included in the + * range, then also verify "global invariants" of the repository, as + * described in svn_fs_verify(). + * + * If @a check_normalization is @c TRUE, report any name collisions + * within the same directory or svn:mergeinfo property where the names + * differ only in character representation, but are otherwise + * identical. + * + * If @a metadata_only is @c TRUE, backends that have a concept of separate + * metadata verification will only perform that and skip the more expensive + * file context reconstruction and verification. For FSFS format 7+ and + * FSX, this allows for a very fast check against external corruption. + * + * If @a verify_callback is not @c NULL, call it with @a verify_baton upon + * receiving an FS-specific structure failure or a revision verification + * failure. Set @c revision callback argument to #SVN_INVALID_REVNUM or + * to the revision number respectively. Set @c verify_err to svn_error_t + * describing the reason of the failure. @c verify_err will be cleared + * after the callback returns, use svn_error_dup() to preserve the error. + * If @a verify_callback returns an error different from #SVN_NO_ERROR, + * stop verifying the repository and immediately return the error from + * @a verify_callback. + * + * If @a verify_callback is @c NULL, this function returns the first + * encountered verification error or #SVN_NO_ERROR if there were no failures + * during the verification. Errors that prevent the verification process + * from continuing, such as #SVN_ERR_CANCELLED, are returned immediately + * and do not trigger an invocation of @a verify_callback. + * + * If @a notify_func is not null, then call it with @a notify_baton and + * with a notification structure in which the fields are set as follows. + * (For a warning that does not apply to a specific revision, the revision + * number is #SVN_INVALID_REVNUM.) + * + * For each FS-specific structure warning: + * @c action = svn_repos_notify_verify_rev_structure + * @c revision = the revision or #SVN_INVALID_REVNUM + * + * For each revision verification warning: + * @c action = #svn_repos_notify_warning + * @c warning and @c warning_str fields set accordingly + * ### TODO: Set @c revision = the revision? + * + * For each successfully verified revision: + * @c action = #svn_repos_notify_verify_rev_end + * @c revision = the revision + * + * At the end: + * @c action = svn_repos_notify_verify_end + * ### Do we really need a callback to tell us the function we + * called has reached its end and is about to return? + * ### Not sent, currently, if a FS structure error is found. * * If @a cancel_func is not @c NULL, call it periodically with @a * cancel_baton as argument to see if the caller wishes to cancel the * verification. * + * Use @a scratch_pool for temporary allocation. + * + * @see svn_repos_verify_callback_t + * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_verify_fs3(svn_repos_t *repos, + svn_revnum_t start_rev, + svn_revnum_t end_rev, + svn_boolean_t check_normalization, + svn_boolean_t metadata_only, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_repos_verify_callback_t verify_callback, + void *verify_baton, + svn_cancel_func_t cancel, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/** + * Like svn_repos_verify_fs3(), but with @a verify_callback and + * @a verify_baton set to @c NULL and with @a check_normalization + * and @a metadata_only set to @c FALSE. + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_repos_verify_fs2(svn_repos_t *repos, svn_revnum_t start_rev, @@ -2571,7 +2944,10 @@ svn_repos_verify_fs2(svn_repos_t *repos, /** * Similar to svn_repos_verify_fs2(), but with a feedback_stream instead of - * handling feedback via the notify_func handler + * handling feedback via the notify_func handler. + * + * If @a feedback_stream is not @c NULL, write feedback to it (lines of + * the form "* Verified revision %ld\n"). * * @since New in 1.5. * @deprecated Provided for backward compatibility with the 1.6 API. @@ -2588,15 +2964,13 @@ svn_repos_verify_fs(svn_repos_t *repos, /** * Dump the contents of the filesystem within already-open @a repos into - * writable @a dumpstream. Begin at revision @a start_rev, and dump every - * revision up through @a end_rev. Use @a pool for all allocation. If - * non-@c NULL, send feedback to @a feedback_stream. If @a dumpstream is + * writable @a dumpstream. If @a dumpstream is * @c NULL, this is effectively a primitive verify. It is not complete, - * however; svn_repos_verify_fs2() and svn_fs_verify(). + * however; see instead svn_repos_verify_fs3(). * - * If @a start_rev is #SVN_INVALID_REVNUM, then start dumping at revision - * 0. If @a end_rev is #SVN_INVALID_REVNUM, then dump through the @c HEAD - * revision. + * Begin at revision @a start_rev, and dump every revision up through + * @a end_rev. If @a start_rev is #SVN_INVALID_REVNUM, start at revision + * 0. If @a end_rev is #SVN_INVALID_REVNUM, end at the head revision. * * If @a incremental is @c TRUE, the first revision dumped will be a diff * against the previous revision (usually it looks like a full dump of @@ -2609,14 +2983,37 @@ svn_repos_verify_fs(svn_repos_t *repos, * be done with full plain text. A dump with @a use_deltas set cannot * be loaded by Subversion 1.0.x. * - * If @a notify_func is not @c NULL, then for every dumped revision call - * @a notify_func with @a rev set to the dumped revision and @a warning_text - * @c NULL. For warnings call @a notify_func with @a warning_text. + * If @a notify_func is not null, then call it with @a notify_baton and + * with a notification structure in which the fields are set as follows. + * (For a warning or error notification that does not apply to a specific + * revision, the revision number is #SVN_INVALID_REVNUM.) + * + * For each warning: + * @c action = #svn_repos_notify_warning + * @c warning and @c warning_str fields set accordingly + * ### TODO: Set @c revision = the revision or #SVN_INVALID_REVNUM? + * + * For each successfully dumped revision: + * @c action = #svn_repos_notify_dump_rev_end + * @c revision = the revision + * + * At the end: + * @c action = svn_repos_notify_verify_end + * ### Do we really need a callback to tell us the function we + * called has reached its end and is about to return? + * + * At the end, if there were certain warnings previously: + * @c action = #svn_repos_notify_warning + * @c warning and @c warning_str fields set accordingly, + * reiterating the existence of previous warnings + * ### This is a presentation issue. Caller could do this itself. * * If @a cancel_func is not @c NULL, it is called periodically with * @a cancel_baton as argument to see if the client wishes to cancel * the dump. * + * Use @a scratch_pool for temporary allocation. + * * @since New in 1.7. */ svn_error_t * @@ -2709,6 +3106,11 @@ svn_repos_dump_fs(svn_repos_t *repos, * node properties (those in the svn: namespace) against established * rules for those things. * + * If @a ignore_dates is set, ignore any revision datestamps found in + * @a dumpstream, allowing the revisions created by the load process + * to be stamped as if they were newly created via the normal commit + * process. + * * If non-NULL, use @a notify_func and @a notify_baton to send notification * of events to the caller. * @@ -2716,8 +3118,32 @@ svn_repos_dump_fs(svn_repos_t *repos, * @a cancel_baton as argument to see if the client wishes to cancel * the load. * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_load_fs5(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_boolean_t ignore_dates, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool); + +/** Similar to svn_repos_load_fs5(), but with @a ignore_dates + * always passed as FALSE. + * * @since New in 1.8. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_repos_load_fs4(svn_repos_t *repos, svn_stream_t *dumpstream, @@ -2856,9 +3282,10 @@ typedef struct svn_repos_parse_fns3_t /** For a given @a node_baton, remove all properties. */ svn_error_t *(*remove_node_props)(void *node_baton); - /** For a given @a node_baton, receive a writable @a stream capable of - * receiving the node's fulltext. After writing the fulltext, call - * the stream's close() function. + /** For a given @a node_baton, set @a stream to a writable stream + * capable of receiving the node's fulltext. The parser will write + * the fulltext to the stream and then close the stream to signal + * completion. * * If a @c NULL is returned instead of a stream, the vtable is * indicating that no text is desired, and the parser will not @@ -2869,8 +3296,9 @@ typedef struct svn_repos_parse_fns3_t /** For a given @a node_baton, set @a handler and @a handler_baton * to a window handler and baton capable of receiving a delta - * against the node's previous contents. A NULL window will be - * sent to the handler after all the windows are sent. + * against the node's previous contents. The parser will send all + * the windows of data to this handler, and will then send a NULL + * window to signal completion. * * If a @c NULL is returned instead of a handler, the vtable is * indicating that no delta is desired, and the parser will not @@ -2927,6 +3355,12 @@ typedef struct svn_repos_parse_fns3_t * but still allow expansion of the format: most headers do not have * to be handled explicitly. * + * ### [JAF] Wouldn't it be more efficient to support a start/end rev + * range here than only supporting it in receivers such as + * svn_repos_get_fs_build_parser4()? This parser could then skip over + * chunks of the input stream before the oldest required rev, and + * could stop reading entirely after the youngest required rev. + * * @since New in 1.8. */ svn_error_t * @@ -2946,26 +3380,72 @@ svn_repos_parse_dumpstream3(svn_stream_t *stream, * to operate on the fs. * * @a start_rev and @a end_rev act as filters, the lower and upper - * (inclusive) range values of revisions in @a dumpstream which will + * (inclusive) range values of revisions which will * be loaded. Either both of these values are #SVN_INVALID_REVNUM (in * which case no revision-based filtering occurs at all), or both are * valid revisions (where @a start_rev is older than or equivalent to - * @a end_rev). + * @a end_rev). They refer to dump stream revision numbers rather than + * committed revision numbers. + * + * If @a use_history is true, then when the parser encounters a node that + * is added-with-history, it will require 'copy-from' history to exist in + * the repository at the relative (adjusted) copy-from revision and path. + * It will perform a copy from that source location, and will fail if no + * suitable source exists there. If @a use_history is false, then it will + * instead convert every copy to a plain add. * - * If @a use_history is set, then the parser will require relative - * 'copyfrom' history to exist in the repository when it encounters - * nodes that are added-with-history. + * ### The 'use_history=FALSE' case is unused and untested in Subversion. + * It seems to me it would not work with a deltas dumpfile (a driver + * that calls the @c apply_textdelta method), as it would not have + * access to the delta base text. + * + * If @a use_pre_commit_hook is set, call the repository's pre-commit + * hook before committing each loaded revision. + * + * If @a use_post_commit_hook is set, call the repository's + * post-commit hook after committing each loaded revision. * * If @a validate_props is set, then validate Subversion revision and * node properties (those in the svn: namespace) against established * rules for those things. * + * If @a ignore_dates is set, ignore any revision datestamps found in + * @a dumpstream, allowing the revisions created by the load process + * to be stamped as if they were newly created via the normal commit + * process. + * * If @a parent_dir is not NULL, then the parser will reparent all the * loaded nodes, from root to @a parent_dir. The directory @a parent_dir * must be an existing directory in the repository. * + * @since New in 1.9. + */ +svn_error_t * +svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **parser, + 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_boolean_t use_pre_commit_hook, + svn_boolean_t use_post_commit_hook, + svn_boolean_t ignore_dates, + svn_repos_notify_func_t notify_func, + void *notify_baton, + apr_pool_t *pool); + +/** + * Similar to svn_repos_get_fs_build_parser5(), but with the + * @c use_pre_commit_hook, @c use_post_commit_hook and @c ignore_dates + * arguments all false. + * * @since New in 1.8. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **parser, void **parse_baton, @@ -2981,10 +3461,11 @@ svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **parser, apr_pool_t *pool); + /** * A vtable that is driven by svn_repos_parse_dumpstream2(). * Similar to #svn_repos_parse_fns3_t except that it lacks - * the delete_node_property and apply_textdelta callbacks. + * the magic_header_record callback. * * @deprecated Provided for backward compatibility with the 1.7 API. */ @@ -3312,7 +3793,7 @@ svn_repos_authz_check_access(svn_authz_t *authz, typedef enum svn_repos_revision_access_level_t { /** no access allowed to the revision properties and all changed-paths - * information. */ + * information. */ svn_repos_revision_access_none, /** access granted to some (svn:date and svn:author) revision properties and * changed-paths information on paths the read has access to. */ @@ -3342,61 +3823,6 @@ svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level, void *authz_read_baton, apr_pool_t *pool); -/** - * Set @a *inherited_values to a depth-first ordered array of - * #svn_prop_inherited_item_t * structures (the path_or_url members of - * which are relative filesystem paths) representing the properties - * inherited by @a path in @a root. If no properties are inherited, - * then set @a *inherited_values to an empty array. - * - * if @a propname is NULL then retrieve all explicit and/or inherited - * properties. Otherwise retrieve only the properties named @a propname. - * - * If optional @a authz_read_func is non-NULL, then use this function - * (along with optional @a authz_read_baton) to check the readability - * of each parent path from which properties are inherited. Silently omit - * properties for unreadable parent paths. - * - * Allocate @a *inherited_props in @a result_pool. Use @a scratch_pool for - * temporary allocations. - * - * @since New in 1.8. - */ -svn_error_t * -svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props, - svn_fs_root_t *root, - const char *path, - const char *propname, - svn_repos_authz_func_t authz_read_func, - void *authz_read_baton, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - -/** Capabilities **/ - -/** - * Store in @a repos the client-reported capabilities @a capabilities, - * which must be allocated in memory at least as long-lived as @a repos. - * - * The elements of @a capabilities are 'const char *', a subset of - * the constants beginning with @c SVN_RA_CAPABILITY_. - * @a capabilities is not copied, so changing it later will affect - * what is remembered by @a repos. - * - * @note The capabilities are passed along to the start-commit hook; - * see that hook's template for details. - * - * @note As of Subversion 1.5, there are no error conditions defined, - * so this always returns SVN_NO_ERROR. In future releases it may - * return error, however, so callers should check. - * - * @since New in 1.5. - */ -svn_error_t * -svn_repos_remember_client_capabilities(svn_repos_t *repos, - const apr_array_header_t *capabilities); - #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/include/svn_sorts.h b/contrib/subversion/subversion/include/svn_sorts.h index b9e05e525..3a6a838a3 100644 --- a/contrib/subversion/subversion/include/svn_sorts.h +++ b/contrib/subversion/subversion/include/svn_sorts.h @@ -52,16 +52,7 @@ extern "C" { /** This structure is used to hold a key/value from a hash table. * @note Private. For use by Subversion's own code only. See issue #1644. */ -typedef struct svn_sort__item_t { - /** pointer to the key */ - const void *key; - - /** size of the key */ - apr_ssize_t klen; - - /** pointer to the value */ - void *value; -} svn_sort__item_t; +typedef struct svn_sort__item_t svn_sort__item_t; /** Compare two @c svn_sort__item_t's, returning an integer greater than, @@ -149,73 +140,6 @@ int svn_sort_compare_ranges(const void *a, const void *b); -/** Sort @a ht according to its keys, return an @c apr_array_header_t - * containing @c svn_sort__item_t structures holding those keys and values - * (i.e. for each @c svn_sort__item_t @a item in the returned array, - * @a item->key and @a item->size are the hash key, and @a item->value points to - * the hash value). - * - * Storage is shared with the original hash, not copied. - * - * @a comparison_func should take two @c svn_sort__item_t's and return an - * integer greater than, equal to, or less than 0, according as the first item - * is greater than, equal to, or less than the second. - * - * @note Private. For use by Subversion's own code only. See issue #1644. - * - * @note This function and the @c svn_sort__item_t should go over to APR. - */ -apr_array_header_t * -svn_sort__hash(apr_hash_t *ht, - int (*comparison_func)(const svn_sort__item_t *, - const svn_sort__item_t *), - apr_pool_t *pool); - -/* Return the lowest index at which the element @a *key should be inserted into - * the array @a array, according to the ordering defined by @a compare_func. - * The array must already be sorted in the ordering defined by @a compare_func. - * @a compare_func is defined as for the C stdlib function bsearch(). - * - * @note Private. For use by Subversion's own code only. - */ -int -svn_sort__bsearch_lower_bound(const void *key, - const apr_array_header_t *array, - int (*compare_func)(const void *, const void *)); - -/* Insert a shallow copy of @a *new_element into the array @a array at the index - * @a insert_index, growing the array and shuffling existing elements along to - * make room. - * - * @note Private. For use by Subversion's own code only. - */ -void -svn_sort__array_insert(const void *new_element, - apr_array_header_t *array, - int insert_index); - - -/* Remove @a elements_to_delete elements starting at @a delete_index from the - * array @a arr. If @a delete_index is not a valid element of @a arr, - * @a elements_to_delete is not greater than zero, or - * @a delete_index + @a elements_to_delete is greater than @a arr->nelts, - * then do nothing. - * - * @note Private. For use by Subversion's own code only. - */ -void -svn_sort__array_delete(apr_array_header_t *arr, - int delete_index, - int elements_to_delete); - -/* Reverse the order of elements in @a array, in place. - * - * @note Private. For use by Subversion's own code only. - */ -void -svn_sort__array_reverse(apr_array_header_t *array, - apr_pool_t *scratch_pool); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/include/svn_string.h b/contrib/subversion/subversion/include/svn_string.h index d8ce02b03..82c6fd693 100644 --- a/contrib/subversion/subversion/include/svn_string.h +++ b/contrib/subversion/subversion/include/svn_string.h @@ -140,6 +140,8 @@ svn_string_create_empty(apr_pool_t *pool); /** Create a new string copied from a generic string of bytes, @a bytes, of * length @a size bytes. @a bytes is NOT assumed to be null-terminated, but * the new string will be. + * + * @since Since 1.9, @a bytes can be NULL if @a size is zero. */ svn_string_t * svn_string_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool); @@ -168,7 +170,11 @@ svn_string_createv(apr_pool_t *pool, const char *fmt, va_list ap) svn_boolean_t svn_string_isempty(const svn_string_t *str); -/** Return a duplicate of @a original_string. */ +/** Return a duplicate of @a original_string. + * + * @since Since 1.9, @a original_string can be NULL in which case NULL will + * be returned. + */ svn_string_t * svn_string_dup(const svn_string_t *original_string, apr_pool_t *pool); @@ -205,6 +211,8 @@ svn_stringbuf_create(const char *cstring, apr_pool_t *pool); /** Create a new stringbuf copied from the generic string of bytes, @a bytes, * of length @a size bytes. @a bytes is NOT assumed to be null-terminated, * but the new stringbuf will be. + * + * @since Since 1.9, @a bytes can be NULL if @a size is zero. */ svn_stringbuf_t * svn_stringbuf_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool); @@ -232,6 +240,15 @@ svn_stringbuf_create_ensure(apr_size_t minimum_size, apr_pool_t *pool); svn_stringbuf_t * svn_stringbuf_create_from_string(const svn_string_t *str, apr_pool_t *pool); +/** Create a new stringbuf using the given @a str as initial buffer. + * Allocate the result in @a pool. In contrast to #svn_stringbuf_create, + * the contents of @a str may change when the stringbuf gets modified. + * + * @since New in 1.9 + */ +svn_stringbuf_t * +svn_stringbuf_create_wrap(char *str, apr_pool_t *pool); + /** Create a new stringbuf by printf-style formatting using @a fmt and the * variable arguments, which are as appropriate for apr_psprintf(). */ @@ -288,25 +305,37 @@ svn_stringbuf_fillchar(svn_stringbuf_t *str, unsigned char c); * The advantages extend beyond the actual call because the reduced * register pressure allows for more optimization within the caller. * - * reallocs if necessary. @a targetstr is affected, nothing else is. + * Reallocs if necessary. @a targetstr is affected, nothing else is. * @since New in 1.7. */ void svn_stringbuf_appendbyte(svn_stringbuf_t *targetstr, char byte); -/** Append an array of bytes onto @a targetstr. +/** Append the array of bytes @a bytes of length @a count onto @a targetstr. * - * reallocs if necessary. @a targetstr is affected, nothing else is. + * Reallocs if necessary. @a targetstr is affected, nothing else is. + * + * @since 1.9 @a bytes can be NULL if @a count is zero. */ void svn_stringbuf_appendbytes(svn_stringbuf_t *targetstr, const char *bytes, apr_size_t count); +/** Append @a byte @a count times onto @a targetstr. + * + * Reallocs if necessary. @a targetstr is affected, nothing else is. + * @since New in 1.9. + */ +void +svn_stringbuf_appendfill(svn_stringbuf_t *targetstr, + char byte, + apr_size_t count); + /** Append the stringbuf @c appendstr onto @a targetstr. * - * reallocs if necessary. @a targetstr is affected, nothing else is. + * Reallocs if necessary. @a targetstr is affected, nothing else is. */ void svn_stringbuf_appendstr(svn_stringbuf_t *targetstr, @@ -314,22 +343,25 @@ svn_stringbuf_appendstr(svn_stringbuf_t *targetstr, /** Append the C string @a cstr onto @a targetstr. * - * reallocs if necessary. @a targetstr is affected, nothing else is. + * Reallocs if necessary. @a targetstr is affected, nothing else is. */ void svn_stringbuf_appendcstr(svn_stringbuf_t *targetstr, const char *cstr); -/** Read @a count bytes from @a bytes and insert them into @a str at - * position @a pos and following. The resulting string will be - * @c count+str->len bytes long. If @c pos is larger or equal to the - * number of bytes currently used in @a str, simply append @a bytes. +/** Insert into @a str at position @a pos an array of bytes @a bytes + * which is @a count bytes long. + * + * The resulting string will be @c count+str->len bytes long. If + * @a pos is larger than or equal to @c str->len, simply append @a bytes. * * Reallocs if necessary. @a str is affected, nothing else is. * - * @note The inserted string may be a sub-range if @a str. + * @note The inserted string may be a sub-range of @a str. * * @since New in 1.8. + * + * @since Since 1.9, @a bytes can be NULL if @a count is zero. */ void svn_stringbuf_insert(svn_stringbuf_t *str, @@ -337,9 +369,10 @@ svn_stringbuf_insert(svn_stringbuf_t *str, const char *bytes, apr_size_t count); -/** Removes @a count bytes from @a str, starting at position @a pos. - * If that range exceeds the current string data, @a str gets truncated - * at @a pos. If the latter is larger or equal to @c str->pos, this will +/** Remove @a count bytes from @a str, starting at position @a pos. + * + * If that range exceeds the current string data, truncate @a str at + * @a pos. If @a pos is larger than or equal to @c str->len, this will * be a no-op. Otherwise, the resulting string will be @c str->len-count * bytes long. * @@ -351,8 +384,8 @@ svn_stringbuf_remove(svn_stringbuf_t *str, apr_size_t count); /** Replace in @a str the substring which starts at @a pos and is @a - * old_count bytes long with a new substring @a bytes (which is @a - * new_count bytes long). + * old_count bytes long with a new substring @a bytes which is @a + * new_count bytes long. * * This is faster but functionally equivalent to the following sequence: * @code @@ -361,6 +394,8 @@ svn_stringbuf_remove(svn_stringbuf_t *str, * @endcode * * @since New in 1.8. + * + * @since Since 1.9, @a bytes can be NULL if @a new_count is zero. */ void svn_stringbuf_replace(svn_stringbuf_t *str, @@ -407,9 +442,11 @@ svn_string_compare_stringbuf(const svn_string_t *str1, * @{ */ -/** Divide @a input into substrings along @a sep_chars boundaries, return an - * array of copies of those substrings (plain const char*), allocating both - * the array and the copies in @a pool. +/** Divide @a input into substrings, interpreting any char from @a sep + * as a token separator. + * + * Return an array of copies of those substrings (plain const char*), + * allocating both the array and the copies in @a pool. * * None of the elements added to the array contain any of the * characters in @a sep_chars, and none of the new elements are empty @@ -504,6 +541,7 @@ svn_cstring_casecmp(const char *str1, const char *str2); * Assume that the number is represented in base @a base. * Raise an error if conversion fails (e.g. due to overflow), or if the * converted number is smaller than @a minval or larger than @a maxval. + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ @@ -516,6 +554,7 @@ svn_cstring_strtoi64(apr_int64_t *n, const char *str, * Parse the C string @a str into a 64 bit number, and return it in @a *n. * Assume that the number is represented in base 10. * Raise an error if conversion fails (e.g. due to overflow). + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ @@ -526,6 +565,7 @@ svn_cstring_atoi64(apr_int64_t *n, const char *str); * Parse the C string @a str into a 32 bit number, and return it in @a *n. * Assume that the number is represented in base 10. * Raise an error if conversion fails (e.g. due to overflow). + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ @@ -537,6 +577,7 @@ svn_cstring_atoi(int *n, const char *str); * it in @a *n. Assume that the number is represented in base @a base. * Raise an error if conversion fails (e.g. due to overflow), or if the * converted number is smaller than @a minval or larger than @a maxval. + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ @@ -549,6 +590,7 @@ svn_cstring_strtoui64(apr_uint64_t *n, const char *str, * Parse the C string @a str into an unsigned 64 bit number, and return * it in @a *n. Assume that the number is represented in base 10. * Raise an error if conversion fails (e.g. due to overflow). + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ @@ -559,12 +601,23 @@ svn_cstring_atoui64(apr_uint64_t *n, const char *str); * Parse the C string @a str into an unsigned 32 bit number, and return * it in @a *n. Assume that the number is represented in base 10. * Raise an error if conversion fails (e.g. due to overflow). + * Leading whitespace in @a str is skipped in a locale-dependent way. * * @since New in 1.7. */ svn_error_t * svn_cstring_atoui(unsigned int *n, const char *str); +/** + * Skip the common prefix @a prefix from the C string @a str, and return + * a pointer to the next character after the prefix. + * Return @c NULL if @a str does not start with @a prefix. + * + * @since New in 1.9. + */ +const char * +svn_cstring_skip_prefix(const char *str, const char *prefix); + /** @} */ /** @} */ diff --git a/contrib/subversion/subversion/include/svn_types.h b/contrib/subversion/subversion/include/svn_types.h index 1ad21942e..f1a0850bc 100644 --- a/contrib/subversion/subversion/include/svn_types.h +++ b/contrib/subversion/subversion/include/svn_types.h @@ -32,6 +32,7 @@ #include /* for ULONG_MAX */ #include /* for apr_size_t, apr_int64_t, ... */ +#include #include /* for apr_status_t */ #include /* for apr_pool_t */ #include /* for apr_hash_t */ @@ -63,6 +64,50 @@ extern "C" { # endif #endif + +/** Macro used to mark experimental functions. + * + * @since New in 1.9. + */ +#ifndef SVN_EXPERIMENTAL +# if !defined(SWIGPERL) && !defined(SWIGPYTHON) && !defined(SWIGRUBY) +# if defined(__has_attribute) +# if __has_attribute(__warning__) +# define SVN_EXPERIMENTAL __attribute__((warning("experimental function used"))) +# else +# define SVN_EXPERIMENTAL +# endif +# elif !defined(__llvm__) && defined(__GNUC__) \ + && (__GNUC__ >= 4 || (__GNUC__==3 && __GNUC_MINOR__>=1)) +# define SVN_EXPERIMENTAL __attribute__((warning("experimental function used"))) +# elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define SVN_EXPERIMENTAL __declspec(deprecated("experimental function used")) +# else +# define SVN_EXPERIMENTAL +# endif +# else +# define SVN_EXPERIMENTAL +# endif +#endif + +/** Macro used to mark functions that require a final null sentinel argument. + * + * @since New in 1.9. + */ +#ifndef SVN_NEEDS_SENTINEL_NULL +# if defined(__has_attribute) +# if __has_attribute(__sentinel__) +# define SVN_NEEDS_SENTINEL_NULL __attribute__((sentinel)) +# else +# define SVN_NEEDS_SENTINEL_NULL +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) +# define SVN_NEEDS_SENTINEL_NULL __attribute__((sentinel)) +# else +# define SVN_NEEDS_SENTINEL_NULL +# endif +#endif + /** Indicate whether the current platform supports unaligned data access. * @@ -104,6 +149,26 @@ typedef int svn_boolean_t; #endif /* FALSE */ + +/* Declaration of a unique type, never defined, for the SVN_VA_NULL macro. + * + * NOTE: Private. Not for direct use by third-party code. + */ +struct svn__null_pointer_constant_stdarg_sentinel_t; + +/** Null pointer constant used as a sentinel in variable argument lists. + * + * Use of this macro ensures that the argument is of the correct size when a + * pointer is expected. (The macro @c NULL is not defined as a pointer on + * all systems, and the arguments to variadic functions are not converted + * automatically to the expected type.) + * + * @since New in 1.9. + */ +#define SVN_VA_NULL ((struct svn__null_pointer_constant_stdarg_sentinel_t*)0) +/* See? (char*)NULL -- They have the same length, but the cast looks ugly. */ + + /** Subversion error object. * @@ -186,21 +251,26 @@ typedef struct svn_version_t svn_version_t; * These functions enable the caller to dereference an APR hash table index * without type casts or temporary variables. * - * ### These are private, and may go away when APR implements them natively. + * These functions are provided by APR itself from version 1.5. + * Definitions are provided here for when using older versions of APR. * @{ */ +#if !APR_VERSION_AT_LEAST(1, 5, 0) + /** Return the key of the hash table entry indexed by @a hi. */ const void * -svn__apr_hash_index_key(const apr_hash_index_t *hi); +apr_hash_this_key(apr_hash_index_t *hi); /** Return the key length of the hash table entry indexed by @a hi. */ apr_ssize_t -svn__apr_hash_index_klen(const apr_hash_index_t *hi); +apr_hash_this_key_len(apr_hash_index_t *hi); /** Return the value of the hash table entry indexed by @a hi. */ void * -svn__apr_hash_index_val(const apr_hash_index_t *hi); +apr_hash_this_val(apr_hash_index_t *hi); + +#endif /** @} */ @@ -1001,7 +1071,6 @@ typedef svn_error_t *(*svn_log_message_receiver_t)( const char *message, apr_pool_t *pool); - /** Callback function type for commits. * diff --git a/contrib/subversion/subversion/include/svn_version.h b/contrib/subversion/subversion/include/svn_version.h index ccb5e4210..92ce825ce 100644 --- a/contrib/subversion/subversion/include/svn_version.h +++ b/contrib/subversion/subversion/include/svn_version.h @@ -61,7 +61,7 @@ extern "C" { * Modify when new functionality is added or new interfaces are * defined, but all changes are backward compatible. */ -#define SVN_VER_MINOR 8 +#define SVN_VER_MINOR 9 /** * Patch number. @@ -70,7 +70,7 @@ extern "C" { * * @since New in 1.1. */ -#define SVN_VER_PATCH 14 +#define SVN_VER_PATCH 4 /** @deprecated Provided for backward compatibility with the 1.0 API. */ @@ -82,7 +82,7 @@ extern "C" { /** Version tag: a string describing the version. * - * This tag remains " (dev build)" in the repository so that we can + * This tag remains " (under development)" 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. * @@ -93,7 +93,7 @@ extern "C" { * * Always change this at the same time as SVN_VER_NUMTAG. */ -#define SVN_VER_TAG " (r1692801)" +#define SVN_VER_TAG " (r1740329)" /** Number tag: a string describing the version. @@ -114,12 +114,10 @@ extern "C" { /** 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. + * file version. Its value remains 0 in the repository except in release + * tags where it is the revision from which the tag was created. */ -#define SVN_VER_REVISION 1692801 +#define SVN_VER_REVISION 1740329 /* Version strings composed from the above definitions. */ @@ -177,10 +175,25 @@ struct svn_version_t * Generate the implementation of a version query function. * * @since New in 1.1. + * @since Since 1.9, embeds a string into the compiled object + * file that can be queried with the 'what' utility. */ -#define SVN_VERSION_BODY \ - SVN_VERSION_DEFINE(versioninfo); \ - return &versioninfo +#define SVN_VERSION_BODY \ + static struct versioninfo_t \ + { \ + const char *const str; \ + const svn_version_t num; \ + } const versioninfo = \ + { \ + "@(#)" SVN_VERSION, \ + { \ + SVN_VER_MAJOR, \ + SVN_VER_MINOR, \ + SVN_VER_PATCH, \ + SVN_VER_NUMTAG \ + } \ + }; \ + return &versioninfo.num /** * Check library version compatibility. Return #TRUE if the client's @@ -192,6 +205,8 @@ struct svn_version_t * unreleased library. A development client is always compatible with * a previous released library. * + * @note Implements the #svn_ver_check_list2.@a comparator interface. + * * @since New in 1.1. */ svn_boolean_t @@ -201,6 +216,8 @@ svn_ver_compatible(const svn_version_t *my_version, /** * Check if @a my_version and @a lib_version encode the same version number. * + * @note Implements the #svn_ver_check_list2.@a comparator interface. + * * @since New in 1.2. */ svn_boolean_t @@ -228,11 +245,32 @@ typedef struct svn_version_checklist_t * 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() + * @a my_version is considered to be compatible with a version in @a checklist + * if @a comparator returns #TRUE when called with @a my_version as the first + * parammeter and the @a checklist version as the second parameter. * - * @since New in 1.1. + * @see svn_ver_compatible(), svn_ver_equal() + * + * @note Subversion's own code invariably uses svn_ver_equal() as @a comparator, + * since the cmdline tools sometimes use non-public APIs (such as utility + * functions that haven't been promoted to svn_cmdline.h). Third-party code + * SHOULD use svn_ver_compatible() as @a comparator. + * + * @since New in 1.9. */ 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 *)); + +/** Similar to svn_ver_check_list2(), with @a comparator set to + * #svn_ver_compatible. + * + * @deprecated Provided for backward compatibility with 1.8 API. + */ +SVN_DEPRECATED +svn_error_t * svn_ver_check_list(const svn_version_t *my_version, const svn_version_checklist_t *checklist); @@ -268,6 +306,11 @@ typedef struct svn_version_extended_t svn_version_extended_t; * retrieve (for example, the OS release name, list of shared * libraries, etc.). Use @a pool for all allocations. * + * @note This function may allocate significant auxiliary resources + * (memory and file descriptors) in @a pool. It is recommended to + * copy the returned data to suitable longer-lived memory and clear + * @a pool after calling this function. + * * @since New in 1.8. */ const svn_version_extended_t * diff --git a/contrib/subversion/subversion/include/svn_wc.h b/contrib/subversion/subversion/include/svn_wc.h index 37210ff05..010597bd6 100644 --- a/contrib/subversion/subversion/include/svn_wc.h +++ b/contrib/subversion/subversion/include/svn_wc.h @@ -907,13 +907,15 @@ svn_wc_external_item_dup(const svn_wc_external_item_t *item, * * Allocate the table, keys, and values in @a pool. * - * Use @a parent_directory only in constructing error strings. + * @a defining_directory is the path or URL of the directory on which + * the svn:externals property corresponding to @a desc is set. + * @a defining_directory is only used when constructing error strings. * * @since New in 1.5. */ svn_error_t * svn_wc_parse_externals_description3(apr_array_header_t **externals_p, - const char *parent_directory, + const char *defining_directory, const char *desc, svn_boolean_t canonicalize_url, apr_pool_t *pool); @@ -1255,8 +1257,24 @@ typedef enum svn_wc_notify_action_t * copy + delete. The notified path is the move source (the deleted path). * ### TODO: Provide path to move destination as well? * @since New in 1.8. */ - svn_wc_notify_move_broken + svn_wc_notify_move_broken, + + /** Running cleanup on an external module. + * @since New in 1.9. */ + svn_wc_notify_cleanup_external, + + /** The operation failed because the operation (E.g. commit) is only valid + * if the operation includes this path. + * @since New in 1.9. */ + svn_wc_notify_failed_requires_target, + /** Running info on an external module. + * @since New in 1.9. */ + svn_wc_notify_info_external, + + /** Finalizing commit. + * @since New in 1.9. */ + svn_wc_notify_commit_finalizing } svn_wc_notify_action_t; @@ -1732,6 +1750,7 @@ svn_wc_conflict_version_t * svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version, apr_pool_t *pool); + /** A struct that describes a conflict that has occurred in the * working copy. * @@ -1756,8 +1775,10 @@ typedef struct svn_wc_conflict_description2_t /** The path that is in conflict (for a tree conflict, it is the victim) */ const char *local_abspath; - /** The node type of the path being operated on (for a tree conflict, - * ### which version?) */ + /** The node type of the local node involved in this conflict. + * For a tree conflict, this is the node kind of the tree conflict victim. + * For the left/right node kinds of the incoming conflicting change see + * src_left_version->node_kind and src_right_version->node_kind. */ svn_node_kind_t node_kind; /** What sort of conflict are we describing? */ @@ -1776,13 +1797,19 @@ typedef struct svn_wc_conflict_description2_t * (Only if @c kind is 'text', else undefined.) */ const char *mime_type; - /** The action being attempted on the conflicted node or property. - * (When @c kind is 'text', this action must be 'edit'.) */ + /** The incoming action being attempted on the conflicted node or property. + * When @c kind is 'text', this action must be 'edit', but generally it can + * be any kind of possible change. */ svn_wc_conflict_action_t action; - /** The state of the target node or property, relative to its merge-left - * source, that is the reason for the conflict. - * (When @c kind is 'text', this reason must be 'edited'.) */ + /** The local change or state of the target node or property, relative + * to its merge-left source, that conflicts with the incoming action. + * When @c kind is 'text', this must be 'edited', but generally it can + * be any kind of possible change. + * Note that 'local' does not always refer to a working copy. A change + * can be local to the target branch of a merge operation, for example, + * and is not necessarily visible in a working copy of the target branch + * at any given revision. */ svn_wc_conflict_reason_t reason; /** If this is text-conflict and involves the merging of two files @@ -1817,7 +1844,8 @@ typedef struct svn_wc_conflict_description2_t /** my locally-edited version of the file */ const char *my_abspath; - /** merged version; may contain conflict markers */ + /** merged version; may contain conflict markers + * ### For property conflicts, this contains 'their_abspath'. */ const char *merged_file; /** The operation that exposed the conflict. @@ -1831,8 +1859,44 @@ typedef struct svn_wc_conflict_description2_t /** Info on the "merge-right source" or "their" version of incoming change. */ const svn_wc_conflict_version_t *src_right_version; - /* Remember to adjust svn_wc__conflict_description2_dup() - * if you add new fields to this struct. */ + /** For property conflicts, the absolute path to the .prej file. + * @since New in 1.9. */ + const char *prop_reject_abspath; + + /** For property conflicts, the local base value of the property, i.e. the + * value of the property as of the BASE revision of the working copy. + * For conflicts created during update/switch this contains the + * post-update/switch property value. The pre-update/switch value can + * be found in prop_value_incoming_old. + * Only set if available, so might be @c NULL. + * @since New in 1.9. */ + const svn_string_t *prop_value_base; + + /** For property conflicts, the local working value of the property, + * i.e. the value of the property in the working copy, possibly with + * local modiciations. + * Only set if available, so might be @c NULL. + * @since New in 1.9. */ + const svn_string_t *prop_value_working; + + /** For property conflicts, the incoming old value of the property, + * i.e. the value the property had at @c src_left_version. + * Only set if available, so might be @c NULL. + * @since New in 1.9 */ + const svn_string_t *prop_value_incoming_old; + + /** For property conflicts, the incoming new value of the property, + * i.e. the value the property had at @c src_right_version. + * Only set if available, so might be @c NULL. + * @since New in 1.9 */ + const svn_string_t *prop_value_incoming_new; + +/* NOTE: Add new fields at the end to preserve binary compatibility. + Also, if you add fields here, you have to update + svn_wc_conflict_description2_dup and perhaps + svn_wc_conflict_description_create_text2, + svn_wc_conflict_description_create_prop2, and + svn_wc_conflict_description_create_tree2. */ } svn_wc_conflict_description2_t; @@ -1928,7 +1992,7 @@ typedef struct svn_wc_conflict_description_t } svn_wc_conflict_description_t; /** - * Allocate an #svn_wc_conflict_description_t structure in @a result_pool, + * Allocate an #svn_wc_conflict_description2_t structure in @a result_pool, * initialize to represent a text conflict, and return it. * * Set the @c local_abspath field of the created struct to @a local_abspath @@ -1960,7 +2024,7 @@ svn_wc_conflict_description_create_text(const char *path, apr_pool_t *pool); /** - * Allocate an #svn_wc_conflict_description_t structure in @a result_pool, + * Allocate an #svn_wc_conflict_description2_t structure in @a result_pool, * initialize to represent a property conflict, and return it. * * Set the @c local_abspath field of the created struct to @a local_abspath @@ -1994,13 +2058,13 @@ svn_wc_conflict_description_create_prop(const char *path, apr_pool_t *pool); /** - * Allocate an #svn_wc_conflict_description_t structure in @a pool, + * Allocate an #svn_wc_conflict_description2_t structure in @a pool, * initialize to represent a tree conflict, and return it. * * Set the @c local_abspath field of the created struct to @a local_abspath * (which must be an absolute path), the @c kind field to - * #svn_wc_conflict_kind_tree, the @c node_kind to @a node_kind, the @c - * operation to @a operation, the @c src_left_version field to + * #svn_wc_conflict_kind_tree, the @c local_node_kind to @a local_node_kind, + * the @c operation to @a operation, the @c src_left_version field to * @a src_left_version, and the @c src_right_version field to * @a src_right_version. * @@ -2040,8 +2104,21 @@ svn_wc_conflict_description_create_tree( /** Return a duplicate of @a conflict, allocated in @a result_pool. * A deep copy of all members will be made. * + * @since New in 1.9. + */ +svn_wc_conflict_description2_t * +svn_wc_conflict_description2_dup( + const svn_wc_conflict_description2_t *conflict, + apr_pool_t *result_pool); + + +/** Like svn_wc_conflict_description2_dup(), but is improperly named + * as a private function when it is intended to be a public API. + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_wc_conflict_description2_t * svn_wc__conflict_description2_dup( const svn_wc_conflict_description2_t *conflict, @@ -2054,9 +2131,15 @@ svn_wc__conflict_description2_dup( */ typedef enum svn_wc_conflict_choice_t { + /** Undefined; for internal use only. + This value is never returned in svn_wc_conflict_result_t. + * @since New in 1.9 + */ + svn_wc_conflict_choose_undefined = -1, + /** Don't resolve the conflict now. Let libsvn_wc mark the path 'conflicted', so user can run 'svn resolved' later. */ - svn_wc_conflict_choose_postpone, + svn_wc_conflict_choose_postpone = 0, /** If there were files to choose from, select one as a way of resolving the conflict here and now. libsvn_wc will then do the @@ -2069,7 +2152,7 @@ typedef enum svn_wc_conflict_choice_t svn_wc_conflict_choose_mine_conflict, /**< own (for conflicted hunks) */ svn_wc_conflict_choose_merged, /**< merged version */ - /* @since New in 1.8. */ + /** @since New in 1.8. */ svn_wc_conflict_choose_unspecified /**< undecided */ } svn_wc_conflict_choice_t; @@ -2102,6 +2185,14 @@ typedef struct svn_wc_conflict_result_t NULL) in the user's working copy. */ svn_boolean_t save_merged; + /** If not NULL, this is the new merged property, used when choosing + * #svn_wc_conflict_choose_merged. This value is prefered over using + * merged_file. + * + * @since New in 1.9. + */ + const svn_string_t *merged_value; + } svn_wc_conflict_result_t; @@ -2111,7 +2202,8 @@ typedef struct svn_wc_conflict_result_t * * Set the @c choice field of the structure to @a choice, @c merged_file * to @a merged_file, and @c save_merged to false. Make only a shallow - * copy of the pointer argument @a merged_file. + * copy of the pointer argument @a merged_file. @a merged_file may be + * NULL if setting merged_file is not needed. * * @since New in 1.5. */ @@ -3743,6 +3835,13 @@ typedef struct svn_wc_status3_t * @since New in 1.8. */ svn_boolean_t file_external; + + /** The actual kind of the node in the working copy. May differ from + * @a kind on obstructions, deletes, etc. #svn_node_unknown if unavailable. + * + * @since New in 1.9 */ + svn_node_kind_t actual_kind; + /* NOTE! Please update svn_wc_dup_status3() when adding new fields here. */ } svn_wc_status3_t; @@ -4544,10 +4643,9 @@ svn_wc_delete(const char *path, * addition to the working copy. The added node will have the properties * provided in @a props, or none if that is NULL. * - * Check and canonicalize the properties in the same way as - * svn_wc_prop_set4(). Return an error and don't add the node if the - * properties are not valid on this node. Unlike svn_wc_prop_set4() - * there is no option to skip some of the checks and canonicalizations. + * Unless @a skip_checks is TRUE, check and canonicalize the properties in the + * same way as svn_wc_prop_set4(). Return an error and don't add the node if + * the properties are not valid on this node. * * ### The error code on validity check failure should be specified, and * preferably should be a single code. @@ -4559,10 +4657,30 @@ svn_wc_delete(const char *path, * If @a local_abspath does not exist as file, directory or symlink, return * #SVN_ERR_WC_PATH_NOT_FOUND. * + * If @a notify_func is non-NULL, invoke it with @a notify_baton to report + * the item being added. + * * ### TODO: Split into add_dir, add_file, add_symlink? * + * @since New in 1.9. + */ +svn_error_t * +svn_wc_add_from_disk3(svn_wc_context_t *wc_ctx, + const char *local_abspath, + const apr_hash_t *props, + svn_boolean_t skip_checks, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool); + +/** + * Similar to svn_wc_add_from_disk3(), but always passes FALSE for + * @a skip_checks + * * @since New in 1.8. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -5070,6 +5188,12 @@ svn_wc_committed_queue_create(apr_pool_t *pool); * ### seems to be not a set of changes but rather the new complete set of * ### props. And it's renamed to 'new_dav_cache' inside; why? * + * If @a is_committed is @c TRUE, the node will be processed as committed. This + * turns the node and its implied descendants as the new unmodified state at + * the new specified revision. Unless @a recurse is TRUE, changes on + * descendants are not committed as changes directly. In this case they should + * be queueud as their own changes. + * * If @a remove_lock is @c TRUE, any entryprops related to a repository * lock will be removed. * @@ -5107,7 +5231,25 @@ svn_wc_committed_queue_create(apr_pool_t *pool); * Temporary allocations will be performed in @a scratch_pool, and persistent * allocations will use the same pool as @a queue used when it was created. * + * @since New in 1.9. + */ +svn_error_t * +svn_wc_queue_committed4(svn_wc_committed_queue_t *queue, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_boolean_t recurse, + svn_boolean_t is_committed, + 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); + +/** Similar to svn_wc_queue_committed4, but with is_committed always + * TRUE. + * * @since New in 1.7. + * @deprecated Provided for backwards compatibility with the 1.8 API. */ svn_error_t * svn_wc_queue_committed3(svn_wc_committed_queue_t *queue, @@ -7093,7 +7235,7 @@ svn_wc_merge_prop_diffs(svn_wc_notify_state_t *state, * the copy/move source (even if the copy-/move-here replaces a locally * deleted file). * - * If @a local_abspath refers to an unversioned or non-existing path, return + * If @a local_abspath refers to an unversioned or non-existent path, return * @c SVN_ERR_WC_PATH_NOT_FOUND. Use @a wc_ctx to access the working copy. * @a contents may not be @c NULL (unlike @a *contents). * @@ -7142,19 +7284,59 @@ svn_wc_get_pristine_copy_path(const char *path, /** - * Recurse from @a local_abspath, cleaning up unfinished log business. Perform - * any temporary allocations in @a scratch_pool. Any working copy locks under - * @a local_abspath will be taken over and then cleared by this function. + * Recurse from @a local_abspath, cleaning up unfinished tasks. Perform + * any temporary allocations in @a scratch_pool. If @a break_locks is TRUE + * Any working copy locks under @a local_abspath will be taken over and then + * cleared by this function. + * WARNING: If @a break_locks is TRUE there is no mechanism that will protect + * locks that are still being used. * - * WARNING: there is no mechanism that will protect locks that are still being - * used. + * If @a fix_recorded_timestamps is TRUE the recorded timestamps of unmodified + * files will be updated, which will improve performance of future is-modified + * checks. + * + * If @a clear_dav_cache is @c TRUE, the caching of DAV information for older + * mod_dav served repositories is cleared. This clearing invalidates some + * cached information used for pre-HTTPv2 repositories. + * + * If @a vacuum_pristines is TRUE, try to remove unreferenced pristines from + * the working copy. (Will not remove anything unless the obtained lock applies + * to the entire working copy) * * If @a cancel_func is non-NULL, invoke it with @a cancel_baton at various * points during the operation. If it returns an error (typically * #SVN_ERR_CANCELLED), return that error immediately. * + * If @a notify_func is non-NULL, invoke it with @a notify_baton to report + * the progress of the operation. + * + * @note In 1.9, @a notify_func does not get called at all. This may change + * in later releases. + * + * @since New in 1.9. + */ +svn_error_t * +svn_wc_cleanup4(svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool); + +/** + * Similar to svn_wc_cleanup4() but will always break locks, fix recorded + * timestamps, clear the dav cache and vacuum pristines. This function also + * doesn't support notifications. + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_wc_cleanup3(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -7379,6 +7561,13 @@ svn_wc_relocate(const char *path, * changelists. If @a changelist_filter is empty (or altogether @c NULL), * no changelist filtering occurs. * + * If @a clear_changelists is TRUE, then changelist information for the + * paths is cleared. + * + * If @a metadata_only is TRUE, the working copy files are untouched, but + * if there are conflict marker files attached to these files these + * markers are removed. + * * If @a cancel_func is non-NULL, call it with @a cancel_baton at * various points during the reversion process. If it returns an * error (typically #SVN_ERR_CANCELLED), return that error @@ -7395,8 +7584,29 @@ svn_wc_relocate(const char *path, * If @a path is not under version control, return the error * #SVN_ERR_UNVERSIONED_RESOURCE. * + * @since New in 1.9. + */ +svn_error_t * +svn_wc_revert5(svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t use_commit_times, + const apr_array_header_t *changelist_filter, + svn_boolean_t clear_changelists, + 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); + +/** Similar to svn_wc_revert5() but with @a clear_changelists always set to + * FALSE and @a metadata_only set to FALSE. + * * @since New in 1.7. + * @deprecated Provided for backward compatibility with the 1.8 API. */ +SVN_DEPRECATED svn_error_t * svn_wc_revert4(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -7875,7 +8085,8 @@ typedef struct svn_wc_revision_status_t svn_boolean_t switched; /**< Is anything switched? */ svn_boolean_t modified; /**< Is anything modified? */ - /** Whether any WC paths are at a depth other than #svn_depth_infinity. + /** Whether any WC paths are at a depth other than #svn_depth_infinity or + * are user excluded. * @since New in 1.5. */ svn_boolean_t sparse_checkout; @@ -8020,7 +8231,17 @@ typedef svn_error_t *(*svn_changelist_receiver_t) (void *baton, /** - * ### TODO: Doc string, please. + * Beginning at @a local_abspath, crawl to @a depth to discover every path in + * or under @a local_abspath which belongs to one of the changelists in @a + * changelist_filter (an array of const char * changelist names). + * If @a changelist_filter is @c NULL, discover paths with any changelist. + * Call @a callback_func (with @a callback_baton) each time a + * changelist-having path is discovered. + * + * @a local_abspath is a local WC path. + * + * If @a cancel_func is not @c NULL, invoke it passing @a cancel_baton + * during the recursive walk. * * @since New in 1.7. */ diff --git a/contrib/subversion/subversion/include/svn_x509.h b/contrib/subversion/subversion/include/svn_x509.h new file mode 100644 index 000000000..eabe3ed46 --- /dev/null +++ b/contrib/subversion/subversion/include/svn_x509.h @@ -0,0 +1,201 @@ +/** + * @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_x509.h + * @brief Subversion's X509 parser + */ + +#ifndef SVN_X509_H +#define SVN_X509_H + +#include +#include +#include + +#include "svn_error.h" +#include "svn_checksum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SVN_X509_OID_COMMON_NAME "\x55\x04\x03" +#define SVN_X509_OID_COUNTRY "\x55\x04\x06" +#define SVN_X509_OID_LOCALITY "\x55\x04\x07" +#define SVN_X509_OID_STATE "\x55\x04\x08" +#define SVN_X509_OID_ORGANIZATION "\x55\x04\x0A" +#define SVN_X509_OID_ORG_UNIT "\x55\x04\x0B" +#define SVN_X509_OID_EMAIL "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" + +/** + * Representation of parsed certificate info. + * + * @since New in 1.9. + */ +typedef struct svn_x509_certinfo_t svn_x509_certinfo_t; + +/** + * Representation of an atttribute in an X.509 name (e.g. Subject or Issuer) + * + * @since New in 1.9. + */ +typedef struct svn_x509_name_attr_t svn_x509_name_attr_t; + +/** + * Parse x509 @a der certificate data from @a buf with length @a + * buflen and return certificate information in @a *certinfo, + * allocated in @a result_pool. + * + * @note This function has been written with the intent of display data in a + * certificate for a user to see. As a result, it does not do much + * validation on the data it parses from the certificate. It does not + * for instance verify that the certificate is signed by the issuer. It + * does not verify a trust chain. It does not error on critical + * extensions it does not know how to parse. So while it can be used as + * part of a certificate validation scheme, it can't be used alone for + * that purpose. + * + * @since New in 1.9. + */ +svn_error_t * +svn_x509_parse_cert(svn_x509_certinfo_t **certinfo, + const char *buf, + apr_size_t buflen, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Returns a deep copy of the @a attr, allocated in @a result_pool. + * May use @a scratch_pool for temporary allocations. + * @since New in 1.9. + */ +svn_x509_name_attr_t * +svn_x509_name_attr_dup(const svn_x509_name_attr_t *attr, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Returns the OID of @a attr as encoded in the certificate. The + * length of the OID will be set in @a len. + * @since New in 1.9. + */ +const unsigned char * +svn_x509_name_attr_get_oid(const svn_x509_name_attr_t *attr, apr_size_t *len); + +/** + * Returns the value of @a attr as a UTF-8 C string. + * @since New in 1.9. + */ +const char * +svn_x509_name_attr_get_value(const svn_x509_name_attr_t *attr); + + +/** + * Returns a deep copy of @a certinfo, allocated in @a result_pool. + * May use @a scratch_pool for temporary allocations. + * @since New in 1.9. + */ +svn_x509_certinfo_t * +svn_x509_certinfo_dup(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/** + * Returns the subject DN from @a certinfo. + * @since New in 1.9. + */ +const char * +svn_x509_certinfo_get_subject(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool); + +/** + * Returns a list of the attributes for the subject in the @a certinfo. + * Each member of the list is of type svn_x509_name_attr_t. + * + * @since New in 1.9. + */ +const apr_array_header_t * +svn_x509_certinfo_get_subject_attrs(const svn_x509_certinfo_t *certinfo); + +/** + * Returns the cerficiate issuer DN from @a certinfo. + * @since New in 1.9. + */ +const char * +svn_x509_certinfo_get_issuer(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool); + +/** + * Returns a list of the attributes for the issuer in the @a certinfo. + * Each member of the list is of type svn_x509_name_attr_t. + * + * @since New in 1.9. + */ +const apr_array_header_t * +svn_x509_certinfo_get_issuer_attrs(const svn_x509_certinfo_t *certinfo); + +/** + * Returns the start of the certificate validity period from @a certinfo. + * + * @since New in 1.9. + */ +apr_time_t +svn_x509_certinfo_get_valid_from(const svn_x509_certinfo_t *certinfo); + +/** + * Returns the end of the certificate validity period from @a certinfo. + * + * @since New in 1.9. + */ +const apr_time_t +svn_x509_certinfo_get_valid_to(const svn_x509_certinfo_t *certinfo); + +/** + * Returns the digest (fingerprint) from @a certinfo + * @since New in 1.9. + */ +const svn_checksum_t * +svn_x509_certinfo_get_digest(const svn_x509_certinfo_t *certinfo); + +/** + * Returns an array of (const char*) host names from @a certinfo. + * + * @since New in 1.9. + */ +const apr_array_header_t * +svn_x509_certinfo_get_hostnames(const svn_x509_certinfo_t *certinfo); + +/** + * Given an @a oid return a null-terminated C string representation. + * For example an OID with the bytes "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" + * would be converted to the string "1.2.840.113549.1.9.1". Returns + * NULL if the @oid can't be represented as a string. + * + * @since New in 1.9. */ +const char * +svn_x509_oid_to_string(const unsigned char *oid, apr_size_t oid_len, + apr_pool_t *scratch_pool, apr_pool_t *result_pool); + +#ifdef __cplusplus +} +#endif +#endif /* SVN_X509_H */ diff --git a/contrib/subversion/subversion/include/svn_xml.h b/contrib/subversion/subversion/include/svn_xml.h index 90969be7e..8791b1437 100644 --- a/contrib/subversion/subversion/include/svn_xml.h +++ b/contrib/subversion/subversion/include/svn_xml.h @@ -312,7 +312,7 @@ svn_xml_make_header(svn_stringbuf_t **str, * If @a *str is @c NULL, set @a *str to a new stringbuf allocated * in @a pool, else append to the existing stringbuf there. * - * Take the tag's attributes from varargs, a NULL-terminated list of + * Take the tag's attributes from varargs, a SVN_VA_NULL-terminated list of * alternating char * key and char * val. Do xml-escaping * on each val. * @@ -323,7 +323,7 @@ svn_xml_make_open_tag(svn_stringbuf_t **str, apr_pool_t *pool, enum svn_xml_open_tag_style style, const char *tagname, - ...); + ...) SVN_NEEDS_SENTINEL_NULL; /** Like svn_xml_make_open_tag(), but takes a @c va_list instead of being diff --git a/contrib/subversion/subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring.pc.in b/contrib/subversion/subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring.pc.in new file mode 100644 index 000000000..a768b6d5b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_auth_gnome_keyring/libsvn_auth_gnome_keyring.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_auth_gnome_keyring +Description: Subversion GNOME Keyring Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ gnome-keyring-1 +Requires.private: libsvn_subr +Libs: -L${libdir} -lsvn_auth_gnome_keyring +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_auth_kwallet/kwallet.cpp b/contrib/subversion/subversion/libsvn_auth_kwallet/kwallet.cpp index e1a345e7d..74e4eaf84 100644 --- a/contrib/subversion/subversion/libsvn_auth_kwallet/kwallet.cpp +++ b/contrib/subversion/subversion/libsvn_auth_kwallet/kwallet.cpp @@ -47,6 +47,7 @@ #include "svn_auth.h" #include "svn_config.h" #include "svn_error.h" +#include "svn_hash.h" #include "svn_io.h" #include "svn_pools.h" #include "svn_string.h" @@ -135,34 +136,36 @@ get_wid(void) return wid; } +/* Forward definition */ +static apr_status_t +kwallet_terminate(void *data); + static KWallet::Wallet * get_wallet(QString wallet_name, apr_hash_t *parameters) { KWallet::Wallet *wallet = - static_cast (apr_hash_get(parameters, - "kwallet-wallet", - APR_HASH_KEY_STRING)); - if (! wallet && ! apr_hash_get(parameters, - "kwallet-opening-failed", - APR_HASH_KEY_STRING)) + static_cast (svn_hash_gets(parameters, + "kwallet-wallet")); + if (! wallet && ! svn_hash_gets(parameters, "kwallet-opening-failed")) { wallet = KWallet::Wallet::openWallet(wallet_name, get_wid(), KWallet::Wallet::Synchronous); - } - if (wallet) - { - apr_hash_set(parameters, - "kwallet-wallet", - APR_HASH_KEY_STRING, - wallet); - } - else - { - apr_hash_set(parameters, - "kwallet-opening-failed", - APR_HASH_KEY_STRING, - ""); + + if (wallet) + { + svn_hash_sets(parameters, "kwallet-wallet", wallet); + + apr_pool_cleanup_register(apr_hash_pool_get(parameters), + parameters, kwallet_terminate, + apr_pool_cleanup_null); + + svn_hash_sets(parameters, "kwallet-initialized", ""); + } + else + { + svn_hash_sets(parameters, "kwallet-opening-failed", ""); + } } return wallet; } @@ -171,14 +174,12 @@ static apr_status_t kwallet_terminate(void *data) { apr_hash_t *parameters = static_cast (data); - if (apr_hash_get(parameters, "kwallet-initialized", APR_HASH_KEY_STRING)) + if (svn_hash_gets(parameters, "kwallet-initialized")) { KWallet::Wallet *wallet = get_wallet(NULL, parameters); delete wallet; - apr_hash_set(parameters, - "kwallet-initialized", - APR_HASH_KEY_STRING, - NULL); + svn_hash_sets(parameters, "kwallet-wallet", NULL); + svn_hash_sets(parameters, "kwallet-initialized", NULL); } return APR_SUCCESS; } @@ -236,10 +237,6 @@ kwallet_password_get(svn_boolean_t *done, KWallet::Wallet *wallet = get_wallet(wallet_name, parameters); if (wallet) { - apr_hash_set(parameters, - "kwallet-initialized", - APR_HASH_KEY_STRING, - ""); if (wallet->setFolder(folder)) { QString q_password; @@ -254,9 +251,6 @@ kwallet_password_get(svn_boolean_t *done, } } - apr_pool_cleanup_register(pool, parameters, kwallet_terminate, - apr_pool_cleanup_null); - return SVN_NO_ERROR; } @@ -310,10 +304,6 @@ kwallet_password_set(svn_boolean_t *done, KWallet::Wallet *wallet = get_wallet(wallet_name, parameters); if (wallet) { - apr_hash_set(parameters, - "kwallet-initialized", - APR_HASH_KEY_STRING, - ""); if (! wallet->hasFolder(folder)) { wallet->createFolder(folder); @@ -329,9 +319,6 @@ kwallet_password_set(svn_boolean_t *done, } } - apr_pool_cleanup_register(pool, parameters, kwallet_terminate, - apr_pool_cleanup_null); - return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_auth_kwallet/libsvn_auth_kwallet.pc.in b/contrib/subversion/subversion/libsvn_auth_kwallet/libsvn_auth_kwallet.pc.in new file mode 100644 index 000000000..fa65cfa47 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_auth_kwallet/libsvn_auth_kwallet.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_auth_kwallet +Description: Subversion KWallet Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_subr +Libs: -L${libdir} -lsvn_auth_kwallet @SVN_KWALLET_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_client/add.c b/contrib/subversion/subversion/libsvn_client/add.c index f121bc87d..ce7891afb 100644 --- a/contrib/subversion/subversion/libsvn_client/add.c +++ b/contrib/subversion/subversion/libsvn_client/add.c @@ -48,6 +48,7 @@ #include "private/svn_client_private.h" #include "private/svn_wc_private.h" #include "private/svn_ra_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_magic.h" #include "svn_private_config.h" @@ -168,8 +169,8 @@ get_auto_props_for_pattern(apr_hash_t *properties, hi != NULL; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); - const char *propval = svn__apr_hash_index_val(hi); + const char *propname = apr_hash_this_key(hi); + const char *propval = apr_hash_this_val(hi); svn_string_t *propval_str = svn_string_create_empty(apr_hash_pool_get(properties)); @@ -206,8 +207,8 @@ svn_client__get_paths_auto_props(apr_hash_t **properties, hi != NULL; hi = apr_hash_next(hi)) { - const char *pattern = svn__apr_hash_index_key(hi); - apr_hash_t *propvals = svn__apr_hash_index_val(hi); + const char *pattern = apr_hash_this_key(hi); + apr_hash_t *propvals = apr_hash_this_val(hi); get_auto_props_for_pattern(*properties, mimetype, &have_executable, svn_dirent_basename(path, scratch_pool), @@ -316,7 +317,8 @@ add_file(const char *local_abspath, } /* Add the file */ - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath, properties, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, properties, + FALSE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, pool)); return SVN_NO_ERROR; @@ -378,7 +380,8 @@ add_dir_recursive(const char *dir_abspath, iterpool = svn_pool_create(scratch_pool); /* Add this directory to revision control. */ - err = svn_wc_add_from_disk2(ctx->wc_ctx, dir_abspath, NULL /*props*/, + err = svn_wc_add_from_disk3(ctx->wc_ctx, dir_abspath, NULL /*props*/, + FALSE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, iterpool); if (err) @@ -424,8 +427,8 @@ add_dir_recursive(const char *dir_abspath, version control. */ for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_io_dirent2_t *dirent = apr_hash_this_val(hi); const char *abspath; svn_pool_clear(iterpool); @@ -704,15 +707,12 @@ svn_client__get_all_auto_props(apr_hash_t **autoprops, for (i = 0; i < inherited_config_auto_props->nelts; i++) { - apr_hash_index_t *hi; svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( inherited_config_auto_props, i, svn_prop_inherited_item_t *); + const svn_string_t *propval = + svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS); - for (hi = apr_hash_first(scratch_pool, elt->prop_hash); - hi; - hi = apr_hash_next(hi)) { - const svn_string_t *propval = svn__apr_hash_index_val(hi); const char *ch = propval->data; svn_stringbuf_t *config_auto_prop_pattern; svn_stringbuf_t *config_auto_prop_val; @@ -768,59 +768,6 @@ svn_client__get_all_auto_props(apr_hash_t **autoprops, return SVN_NO_ERROR; } -svn_error_t *svn_client__get_inherited_ignores(apr_array_header_t **ignores, - const char *path_or_url, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_opt_revision_t rev; - apr_hash_t *explicit_ignores; - apr_array_header_t *inherited_ignores; - svn_boolean_t target_is_url = svn_path_is_url(path_or_url); - svn_string_t *explicit_prop; - int i; - - if (target_is_url) - rev.kind = svn_opt_revision_head; - else - rev.kind = svn_opt_revision_working; - - SVN_ERR(svn_client_propget5(&explicit_ignores, &inherited_ignores, - SVN_PROP_INHERITABLE_IGNORES, path_or_url, - &rev, &rev, NULL, svn_depth_empty, NULL, ctx, - scratch_pool, scratch_pool)); - - explicit_prop = svn_hash_gets(explicit_ignores, path_or_url); - - if (explicit_prop) - { - svn_prop_inherited_item_t *new_iprop = - apr_palloc(scratch_pool, sizeof(*new_iprop)); - new_iprop->path_or_url = path_or_url; - new_iprop->prop_hash = apr_hash_make(scratch_pool); - svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_IGNORES, - explicit_prop); - APR_ARRAY_PUSH(inherited_ignores, - svn_prop_inherited_item_t *) = new_iprop; - } - - *ignores = apr_array_make(result_pool, 16, sizeof(const char *)); - - for (i = 0; i < inherited_ignores->nelts; i++) - { - svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( - inherited_ignores, i, svn_prop_inherited_item_t *); - svn_string_t *ignore_val = svn_hash_gets(elt->prop_hash, - SVN_PROP_INHERITABLE_IGNORES); - if (ignore_val) - svn_cstring_split_append(*ignores, ignore_val->data, "\n\r\t\v ", - FALSE, result_pool); - } - - return SVN_NO_ERROR; -} - /* The main logic of the public svn_client_add5. * * EXISTING_PARENT_ABSPATH is the absolute path to the first existing @@ -841,7 +788,7 @@ add(const char *local_abspath, svn_magic__cookie_t *magic_cookie; apr_array_header_t *ignores = NULL; - svn_magic__init(&magic_cookie, scratch_pool); + SVN_ERR(svn_magic__init(&magic_cookie, ctx->config, scratch_pool)); if (existing_parent_abspath) { @@ -876,8 +823,9 @@ add(const char *local_abspath, parent_abspath, local_abspath); SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, parent_abspath, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, parent_abspath, NULL /*props*/, + FALSE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } @@ -1169,8 +1117,8 @@ mkdir_urls(const apr_array_header_t *urls, } } } - qsort(targets->elts, targets->nelts, targets->elt_size, - svn_sort_compare_paths); + + svn_sort__array(targets, svn_sort_compare_paths); /* ### This reparent may be problematic in limited-authz-to-common-parent ### scenarios (compare issue #3242). See also issue #3649. */ @@ -1228,53 +1176,58 @@ mkdir_urls(const apr_array_header_t *urls, pool)); /* Call the path-based editor driver. */ - err = svn_delta_path_driver2(editor, edit_baton, targets, TRUE, - path_driver_cb_func, (void *)editor, pool); + err = svn_error_trace( + svn_delta_path_driver2(editor, edit_baton, targets, TRUE, + path_driver_cb_func, (void *)editor, 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)); + svn_error_trace(editor->abort_edit(edit_baton, pool))); + } + + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(common, + svn_wc_notify_commit_finalizing, + pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } /* Close the edit. */ - return editor->close_edit(edit_baton, pool); + return svn_error_trace(editor->close_edit(edit_baton, pool)); } svn_error_t * -svn_client__make_local_parents(const char *path, +svn_client__make_local_parents(const char *local_abspath, svn_boolean_t make_parents, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { svn_error_t *err; svn_node_kind_t orig_kind; - SVN_ERR(svn_io_check_path(path, &orig_kind, pool)); + SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool)); if (make_parents) - SVN_ERR(svn_io_make_dir_recursively(path, pool)); + SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool)); else - SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool)); + SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); - /* Should no longer use svn_depth_empty to indicate that only the directory - itself is added, since it not only constraints the operation depth, but - also defines the depth of the target directory now. Moreover, the new - directory will have no children at all.*/ - err = svn_client_add5(path, svn_depth_infinity, FALSE, FALSE, FALSE, - make_parents, ctx, pool); + err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE, + make_parents, ctx, scratch_pool); /* If we created a new directory, but couldn't add it to version control, then delete it. */ if (err && (orig_kind == svn_node_none)) { - /* ### If this returns an error, should we link it onto - err instead, so that the user is warned that we just - created an unversioned directory? */ - - svn_error_clear(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); + err = svn_error_compose_create(err, + svn_io_remove_dir2(local_abspath, FALSE, + NULL, NULL, + scratch_pool)); } return svn_error_trace(err); @@ -1303,23 +1256,25 @@ svn_client_mkdir4(const apr_array_header_t *paths, else { /* This is a regular "mkdir" + "svn add" */ - apr_pool_t *subpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); int i; for (i = 0; i < paths->nelts; i++) { const char *path = APR_ARRAY_IDX(paths, i, const char *); - svn_pool_clear(subpool); + svn_pool_clear(iterpool); /* See if the user wants us to stop. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); + SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool)); + SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx, - subpool)); + iterpool)); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); } return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_client/blame.c b/contrib/subversion/subversion/libsvn_client/blame.c index 188fdd2bd..b9363e20d 100644 --- a/contrib/subversion/subversion/libsvn_client/blame.c +++ b/contrib/subversion/subversion/libsvn_client/blame.c @@ -34,6 +34,7 @@ #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_props.h" +#include "svn_hash.h" #include "svn_sorts.h" #include "private/svn_wc_private.h" @@ -73,16 +74,16 @@ struct diff_baton { const struct rev *rev; }; -/* The baton used for a file revision. */ +/* The baton used for a file revision. Lives the entire operation */ struct file_rev_baton { svn_revnum_t start_rev, end_rev; + svn_boolean_t backwards; const char *target; svn_client_ctx_t *ctx; const svn_diff_file_options_t *diff_options; /* name of file containing the previous revision of the file */ const char *last_filename; - struct rev *rev; /* the rev for which blame is being assigned - during a diff */ + struct rev *last_rev; /* the rev of the last modification */ struct blame_chain *chain; /* the original blame chain. */ const char *repos_root_url; /* To construct a url */ apr_pool_t *mainpool; /* lives during the whole sequence of calls */ @@ -91,22 +92,32 @@ struct file_rev_baton { /* These are used for tracking merged revisions. */ svn_boolean_t include_merged_revisions; - svn_boolean_t merged_revision; struct blame_chain *merged_chain; /* the merged blame chain. */ /* name of file containing the previous merged revision of the file */ const char *last_original_filename; /* pools for files which may need to persist for more than one rev. */ apr_pool_t *filepool; apr_pool_t *prevfilepool; + + svn_boolean_t check_mime_type; + + /* When blaming backwards we have to use the changes + on the *next* revision, as the interesting change + happens when we move to the previous revision */ + svn_revnum_t last_revnum; + apr_hash_t *last_props; }; -/* The baton used by the txdelta window handler. */ +/* The baton used by the txdelta window handler. Allocated per revision */ struct delta_baton { /* Our underlying handler/baton that we wrap */ svn_txdelta_window_handler_t wrapped_handler; void *wrapped_baton; struct file_rev_baton *file_rev_baton; + svn_stream_t *source_stream; /* the delta source */ const char *filename; + svn_boolean_t is_merged_revision; + struct rev *rev; /* the rev struct for the current revision */ }; @@ -280,6 +291,8 @@ add_file_blame(const char *last_file, struct blame_chain *chain, struct rev *rev, const svn_diff_file_options_t *diff_options, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { if (!last_file) @@ -298,32 +311,28 @@ add_file_blame(const char *last_file, /* We have a previous file. Get the diff and adjust blame info. */ SVN_ERR(svn_diff_file_diff_2(&diff, last_file, cur_file, diff_options, pool)); - SVN_ERR(svn_diff_output(diff, &diff_baton, &output_fns)); + SVN_ERR(svn_diff_output2(diff, &diff_baton, &output_fns, + cancel_func, cancel_baton)); } return SVN_NO_ERROR; } -/* The delta window handler for the text delta between the previously seen - * revision and the revision currently being handled. - * - * Record the blame information for this revision in BATON->file_rev_baton. - * - * Implements svn_txdelta_window_handler_t. +/* Record the blame information for the revision in BATON->file_rev_baton. */ static svn_error_t * -window_handler(svn_txdelta_window_t *window, void *baton) +update_blame(void *baton) { struct delta_baton *dbaton = baton; struct file_rev_baton *frb = dbaton->file_rev_baton; struct blame_chain *chain; - /* Call the wrapped handler first. */ - SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton)); - - /* We patiently wait for the NULL window marking the end. */ - if (window) - return SVN_NO_ERROR; + /* Close the source file used for the delta. + It is important to do this early, since otherwise, they will be deleted + before all handles are closed, which leads to failures on some platforms + when new tempfiles are to be created. */ + if (dbaton->source_stream) + SVN_ERR(svn_stream_close(dbaton->source_stream)); /* If we are including merged revisions, we need to add each rev to the merged chain. */ @@ -334,19 +343,23 @@ window_handler(svn_txdelta_window_t *window, void *baton) /* Process this file. */ SVN_ERR(add_file_blame(frb->last_filename, - dbaton->filename, chain, frb->rev, - frb->diff_options, frb->currpool)); + dbaton->filename, chain, dbaton->rev, + frb->diff_options, + frb->ctx->cancel_func, frb->ctx->cancel_baton, + frb->currpool)); /* If we are including merged revisions, and the current revision is not a merged one, we need to add its blame info to the chain for the original line of history. */ - if (frb->include_merged_revisions && ! frb->merged_revision) + if (frb->include_merged_revisions && ! dbaton->is_merged_revision) { apr_pool_t *tmppool; SVN_ERR(add_file_blame(frb->last_original_filename, - dbaton->filename, frb->chain, frb->rev, - frb->diff_options, frb->currpool)); + dbaton->filename, frb->chain, dbaton->rev, + frb->diff_options, + frb->ctx->cancel_func, frb->ctx->cancel_baton, + frb->currpool)); /* This filename could be around for a while, potentially, so use the longer lifetime pool, and switch it with the previous one*/ @@ -374,6 +387,32 @@ window_handler(svn_txdelta_window_t *window, void *baton) return SVN_NO_ERROR; } +/* The delta window handler for the text delta between the previously seen + * revision and the revision currently being handled. + * + * Record the blame information for this revision in BATON->file_rev_baton. + * + * Implements svn_txdelta_window_handler_t. + */ +static svn_error_t * +window_handler(svn_txdelta_window_t *window, void *baton) +{ + struct delta_baton *dbaton = baton; + + /* Call the wrapped handler first. */ + if (dbaton->wrapped_handler) + SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton)); + + /* We patiently wait for the NULL window marking the end. */ + if (window) + return SVN_NO_ERROR; + + /* Diff and update blame info. */ + SVN_ERR(update_blame(baton)); + + return SVN_NO_ERROR; +} + /* Calculate and record blame information for one revision of the file, * by comparing the file content against the previously seen revision. @@ -402,6 +441,26 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, /* Clear the current pool. */ svn_pool_clear(frb->currpool); + if (frb->check_mime_type) + { + apr_hash_t *props = svn_prop_array_to_hash(prop_diffs, frb->currpool); + const char *value; + + frb->check_mime_type = FALSE; /* Only check first */ + + value = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); + + if (value && svn_mime_type_is_binary(value)) + { + return svn_error_createf( + SVN_ERR_CLIENT_IS_BINARY_FILE, NULL, + _("Cannot calculate blame information for binary file '%s'"), + (svn_path_is_url(frb->target) + ? frb->target + : svn_dirent_local_style(frb->target, pool))); + } + } + if (frb->ctx->notify_func2) { svn_wc_notify_t *notify @@ -422,72 +481,112 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, if (frb->ctx->cancel_func) SVN_ERR(frb->ctx->cancel_func(frb->ctx->cancel_baton)); - /* If there were no content changes, we couldn't care less about this - revision now. Note that we checked the mime type above, so things - work if the user just changes the mime type in a commit. + /* If there were no content changes and no (potential) merges, we couldn't + care less about this revision now. Note that we checked the mime type + above, so things work if the user just changes the mime type in a commit. Also note that we don't switch the pools in this case. This is important, since the tempfile will be removed by the pool and we need the tempfile from the last revision with content changes. */ - if (!content_delta_handler) + if (!content_delta_handler + && (!frb->include_merged_revisions || merged_revision)) return SVN_NO_ERROR; - frb->merged_revision = merged_revision; - /* Create delta baton. */ - delta_baton = apr_palloc(frb->currpool, sizeof(*delta_baton)); + delta_baton = apr_pcalloc(frb->currpool, sizeof(*delta_baton)); /* Prepare the text delta window handler. */ if (frb->last_filename) - SVN_ERR(svn_stream_open_readonly(&last_stream, frb->last_filename, + SVN_ERR(svn_stream_open_readonly(&delta_baton->source_stream, frb->last_filename, frb->currpool, pool)); else - last_stream = svn_stream_empty(frb->currpool); + /* Means empty stream below. */ + delta_baton->source_stream = NULL; + last_stream = svn_stream_disown(delta_baton->source_stream, pool); - if (frb->include_merged_revisions && !frb->merged_revision) + if (frb->include_merged_revisions && !merged_revision) filepool = frb->filepool; else filepool = frb->currpool; SVN_ERR(svn_stream_open_unique(&cur_stream, &delta_baton->filename, NULL, svn_io_file_del_on_pool_cleanup, - filepool, pool)); - - /* Get window handler for applying delta. */ - svn_txdelta_apply(last_stream, cur_stream, NULL, NULL, - frb->currpool, - &delta_baton->wrapped_handler, - &delta_baton->wrapped_baton); + filepool, filepool)); /* Wrap the window handler with our own. */ delta_baton->file_rev_baton = frb; - *content_delta_handler = window_handler; - *content_delta_baton = delta_baton; + delta_baton->is_merged_revision = merged_revision; /* Create the rev structure. */ - frb->rev = apr_pcalloc(frb->mainpool, sizeof(struct rev)); + delta_baton->rev = apr_pcalloc(frb->mainpool, sizeof(struct rev)); - if (revnum < frb->start_rev) + if (frb->backwards) { - /* We shouldn't get more than one revision before the starting - revision (unless of including merged revisions). */ - SVN_ERR_ASSERT((frb->last_filename == NULL) - || frb->include_merged_revisions); + /* Use from last round... + SVN_INVALID_REVNUM on first, which is exactly + what we want */ + delta_baton->rev->revision = frb->last_revnum; + delta_baton->rev->rev_props = frb->last_props; + + /* Store for next delta */ + if (revnum >= MIN(frb->start_rev, frb->end_rev)) + { + frb->last_revnum = revnum; + frb->last_props = svn_prop_hash_dup(rev_props, frb->mainpool); + } + /* Else: Not needed on last rev */ + } + else if (merged_revision + || (revnum >= MIN(frb->start_rev, frb->end_rev))) + { + /* 1+ for the "youngest to oldest" blame */ + SVN_ERR_ASSERT(revnum <= 1 + MAX(frb->end_rev, frb->start_rev)); - /* The file existed before start_rev; generate no blame info for - lines from this revision (or before). */ - frb->rev->revision = SVN_INVALID_REVNUM; + /* Set values from revision props. */ + delta_baton->rev->revision = revnum; + delta_baton->rev->rev_props = svn_prop_hash_dup(rev_props, frb->mainpool); } else { - SVN_ERR_ASSERT(revnum <= frb->end_rev); + /* We shouldn't get more than one revision outside the + specified range (unless we alsoe receive merged revisions) */ + SVN_ERR_ASSERT((frb->last_filename == NULL) + || frb->include_merged_revisions); - /* Set values from revision props. */ - frb->rev->revision = revnum; - frb->rev->rev_props = svn_prop_hash_dup(rev_props, frb->mainpool); + /* The file existed before start_rev; generate no blame info for + lines from this revision (or before). + + This revision specifies the state as it was at the start revision */ + + delta_baton->rev->revision = SVN_INVALID_REVNUM; } if (frb->include_merged_revisions) - frb->rev->path = apr_pstrdup(frb->mainpool, path); + delta_baton->rev->path = apr_pstrdup(frb->mainpool, path); + + /* Keep last revision for postprocessing after all changes */ + frb->last_rev = delta_baton->rev; + + /* Handle all delta - even if it is empty. + We must do the latter to "merge" blame info from other branches. */ + if (content_delta_handler) + { + /* Proper delta - get window handler for applying delta. + svn_ra_get_file_revs2 will drive the delta editor. */ + svn_txdelta_apply(last_stream, cur_stream, NULL, NULL, + frb->currpool, + &delta_baton->wrapped_handler, + &delta_baton->wrapped_baton); + *content_delta_handler = window_handler; + *content_delta_baton = delta_baton; + } + else + { + /* Apply an empty delta, i.e. simply copy the old contents. + We can't simply use the existing file due to the pool rotation logic. + Trigger the blame update magic. */ + SVN_ERR(svn_stream_copy3(last_stream, cur_stream, NULL, NULL, pool)); + SVN_ERR(update_blame(delta_baton)); + } return SVN_NO_ERROR; } @@ -572,7 +671,6 @@ svn_client_blame5(const char *target, struct file_rev_baton frb; svn_ra_session_t *ra_session; svn_revnum_t start_revnum, end_revnum; - svn_client__pathrev_t *end_loc; struct blame *walk, *walk_merged = NULL; apr_pool_t *iterpool; svn_stream_t *last_stream; @@ -590,44 +688,77 @@ svn_client_blame5(const char *target, SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool)); /* Get an RA plugin for this filesystem object. */ - SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &end_loc, - target, NULL, peg_revision, end, + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, + target, NULL, peg_revision, + peg_revision, ctx, pool)); - end_revnum = end_loc->rev; SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx, target_abspath_or_url, ra_session, start, pool)); - if (end_revnum < start_revnum) - return svn_error_create - (SVN_ERR_CLIENT_BAD_REVISION, NULL, - _("Start revision must precede end revision")); + SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx, + target_abspath_or_url, ra_session, + end, pool)); + + { + svn_client__pathrev_t *loc; + svn_opt_revision_t younger_end; + younger_end.kind = svn_opt_revision_number; + younger_end.value.number = MAX(start_revnum, end_revnum); + + SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session, + target, peg_revision, + &younger_end, + ctx, pool)); + + /* Make the session point to the real URL. */ + SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool)); + } /* We check the mime-type of the yougest revision before getting all the older revisions. */ - if (!ignore_mime_type) + if (!ignore_mime_type + && start_revnum < end_revnum) { apr_hash_t *props; - apr_hash_index_t *hi; + const char *mime_type = NULL; - SVN_ERR(svn_client_propget5(&props, NULL, SVN_PROP_MIME_TYPE, - target_abspath_or_url, peg_revision, - end, NULL, svn_depth_empty, NULL, ctx, - pool, pool)); + if (svn_path_is_url(target) + || start_revnum > end_revnum + || (end->kind != svn_opt_revision_working + && end->kind != svn_opt_revision_base)) + { + SVN_ERR(svn_ra_get_file(ra_session, "", end_revnum, NULL, NULL, + &props, pool)); - /* props could be keyed on URLs or paths depending on the - peg_revision and end values so avoid using the key. */ - hi = apr_hash_first(pool, props); - if (hi) + mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); + } + else { - svn_string_t *value; + const svn_string_t *value; + + if (end->kind == svn_opt_revision_working) + SVN_ERR(svn_wc_prop_get2(&value, ctx->wc_ctx, + target_abspath_or_url, + SVN_PROP_MIME_TYPE, + pool, pool)); + else + { + SVN_ERR(svn_wc_get_pristine_props(&props, ctx->wc_ctx, + target_abspath_or_url, + pool, pool)); - /* Should only be one value */ - SVN_ERR_ASSERT(apr_hash_count(props) == 1); + value = props ? svn_hash_gets(props, SVN_PROP_MIME_TYPE) + : NULL; + } - value = svn__apr_hash_index_val(hi); - if (value && svn_mime_type_is_binary(value->data)) + mime_type = value ? value->data : NULL; + } + + if (mime_type) + { + if (svn_mime_type_is_binary(mime_type)) return svn_error_createf (SVN_ERR_CLIENT_IS_BINARY_FILE, 0, _("Cannot calculate blame information for binary file '%s'"), @@ -643,6 +774,7 @@ svn_client_blame5(const char *target, frb.diff_options = diff_options; frb.include_merged_revisions = include_merged_revisions; frb.last_filename = NULL; + frb.last_rev = NULL; frb.last_original_filename = NULL; frb.chain = apr_palloc(pool, sizeof(*frb.chain)); frb.chain->blame = NULL; @@ -655,6 +787,10 @@ svn_client_blame5(const char *target, frb.merged_chain->avail = NULL; frb.merged_chain->pool = pool; } + frb.backwards = (frb.start_rev > frb.end_rev); + frb.last_revnum = SVN_INVALID_REVNUM; + frb.last_props = NULL; + frb.check_mime_type = (frb.backwards && !ignore_mime_type); SVN_ERR(svn_ra_get_repos_root2(ra_session, &frb.repos_root_url, pool)); @@ -675,8 +811,10 @@ svn_client_blame5(const char *target, if available so that we can know what was actually changed in the start revision. */ SVN_ERR(svn_ra_get_file_revs2(ra_session, "", - start_revnum - (start_revnum > 0 ? 1 : 0), - end_revnum, include_merged_revisions, + frb.backwards ? start_revnum + : MAX(0, start_revnum-1), + end_revnum, + include_merged_revisions, file_rev_handler, &frb, pool)); if (end->kind == svn_opt_revision_working) @@ -732,7 +870,8 @@ svn_client_blame5(const char *target, ctx->cancel_baton, pool)); SVN_ERR(add_file_blame(frb.last_filename, temppath, frb.chain, NULL, - frb.diff_options, pool)); + frb.diff_options, + ctx->cancel_func, ctx->cancel_baton, pool)); frb.last_filename = temppath; } @@ -762,7 +901,7 @@ svn_client_blame5(const char *target, the most recently changed revision. ### Is this really what we want to do here? Do the sematics of copy change? */ if (!frb.chain->blame) - frb.chain->blame = blame_create(frb.chain, frb.rev, 0); + frb.chain->blame = blame_create(frb.chain, frb.last_rev, 0); normalize_blames(frb.chain, frb.merged_chain, pool); walk_merged = frb.merged_chain->blame; diff --git a/contrib/subversion/subversion/libsvn_client/cat.c b/contrib/subversion/subversion/libsvn_client/cat.c index 7c58f8848..8c6aac85a 100644 --- a/contrib/subversion/subversion/libsvn_client/cat.c +++ b/contrib/subversion/subversion/libsvn_client/cat.c @@ -176,18 +176,21 @@ svn_client__get_normalized_stream(svn_stream_t **normal_stream, } svn_error_t * -svn_client_cat2(svn_stream_t *out, +svn_client_cat3(apr_hash_t **returned_props, + svn_stream_t *out, const char *path_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, + svn_boolean_t expand_keywords, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_session_t *ra_session; svn_client__pathrev_t *loc; svn_string_t *eol_style; svn_string_t *keywords; - apr_hash_t *props; + apr_hash_t *props = NULL; const char *repos_root_url; svn_stream_t *output = out; svn_error_t *err; @@ -201,8 +204,6 @@ svn_client_cat2(svn_stream_t *out, } else { - peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision, - path_or_url); revision = svn_cl__rev_default_to_peg(revision, peg_revision); } @@ -213,32 +214,39 @@ svn_client_cat2(svn_stream_t *out, const char *local_abspath; svn_stream_t *normal_stream; - SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, pool)); + SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, + scratch_pool)); SVN_ERR(svn_client__get_normalized_stream(&normal_stream, ctx->wc_ctx, - local_abspath, revision, TRUE, FALSE, + local_abspath, revision, + expand_keywords, FALSE, ctx->cancel_func, ctx->cancel_baton, - pool, pool)); + scratch_pool, scratch_pool)); /* We don't promise to close output, so disown it to ensure we don't. */ - output = svn_stream_disown(output, pool); + output = svn_stream_disown(output, scratch_pool); + + if (returned_props) + SVN_ERR(svn_wc_prop_list2(returned_props, ctx->wc_ctx, local_abspath, + result_pool, scratch_pool)); return svn_error_trace(svn_stream_copy3(normal_stream, output, ctx->cancel_func, - ctx->cancel_baton, pool)); + ctx->cancel_baton, scratch_pool)); } /* Get an RA plugin for this filesystem object. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, path_or_url, NULL, peg_revision, - revision, ctx, pool)); + revision, ctx, scratch_pool)); /* Find the repos root URL */ - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, scratch_pool)); /* Grab some properties we need to know in order to figure out if anything special needs to be done with this file. */ - err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, &props, pool); + err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, &props, + result_pool); if (err) { if (err->apr_err == SVN_ERR_FS_NOT_FILE) @@ -272,7 +280,7 @@ svn_client_cat2(svn_stream_t *out, } - if (keywords) + if (keywords && expand_keywords) { svn_string_t *cmt_rev, *cmt_date, *cmt_author; apr_time_t when = 0; @@ -281,24 +289,45 @@ svn_client_cat2(svn_stream_t *out, cmt_date = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE); cmt_author = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR); if (cmt_date) - SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool)); + SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, scratch_pool)); SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, cmt_rev->data, loc->url, repos_root_url, when, cmt_author ? cmt_author->data : NULL, - pool)); + scratch_pool)); } else kw = NULL; /* Interject a translating stream */ - output = svn_subst_stream_translated(svn_stream_disown(out, pool), - eol_str, FALSE, kw, TRUE, pool); + output = svn_subst_stream_translated(svn_stream_disown(out, + scratch_pool), + eol_str, FALSE, kw, TRUE, + scratch_pool); + } + + if (returned_props) + { + /* filter entry and WC props */ + apr_hash_index_t *hi; + const void *key; + apr_ssize_t klen; + + for (hi = apr_hash_first(scratch_pool, props); + hi; hi = apr_hash_next(hi)) + { + apr_hash_this(hi, &key, &klen, NULL); + if (!svn_wc_is_normal_prop(key)) + apr_hash_set(props, key, klen, NULL); + } + + *returned_props = props; } - SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL, pool)); + SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL, + scratch_pool)); if (out != output) /* Close the interjected stream */ diff --git a/contrib/subversion/subversion/libsvn_client/checkout.c b/contrib/subversion/subversion/libsvn_client/checkout.c index 41be776f1..0d20e24e1 100644 --- a/contrib/subversion/subversion/libsvn_client/checkout.c +++ b/contrib/subversion/subversion/libsvn_client/checkout.c @@ -67,6 +67,7 @@ initialize_area(const char *local_abspath, svn_error_t * svn_client__checkout_internal(svn_revnum_t *result_rev, + svn_boolean_t *timestamp_sleep, const char *url, const char *local_abspath, const svn_opt_revision_t *peg_revision, @@ -74,18 +75,16 @@ svn_client__checkout_internal(svn_revnum_t *result_rev, svn_depth_t depth, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, - svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { svn_node_kind_t kind; - apr_pool_t *session_pool = svn_pool_create(pool); - svn_ra_session_t *ra_session; svn_client__pathrev_t *pathrev; /* Sanity check. Without these, the checkout is meaningless. */ SVN_ERR_ASSERT(local_abspath != NULL); - SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool)); + SVN_ERR_ASSERT(svn_uri_is_canonical(url, scratch_pool)); SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); /* Fulfill the docstring promise of svn_client_checkout: */ @@ -94,15 +93,38 @@ svn_client__checkout_internal(svn_revnum_t *result_rev, && (revision->kind != svn_opt_revision_head)) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); - /* Get the RA connection. */ - SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, - url, NULL, peg_revision, revision, - ctx, session_pool)); + /* Get the RA connection, if needed. */ + if (ra_session) + { + svn_error_t *err = svn_ra_reparent(ra_session, url, scratch_pool); + + if (err) + { + if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) + { + svn_error_clear(err); + ra_session = NULL; + } + else + return svn_error_trace(err); + } + else + { + SVN_ERR(svn_client__resolve_rev_and_url(&pathrev, + ra_session, url, + peg_revision, revision, + ctx, scratch_pool)); + } + } - pathrev = svn_client__pathrev_dup(pathrev, pool); - SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, pool)); + if (!ra_session) + { + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, + url, NULL, peg_revision, + revision, ctx, scratch_pool)); + } - svn_pool_destroy(session_pool); + SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, scratch_pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, @@ -112,31 +134,35 @@ svn_client__checkout_internal(svn_revnum_t *result_rev, (SVN_ERR_UNSUPPORTED_FEATURE , NULL, _("URL '%s' refers to a file, not a directory"), pathrev->url); - SVN_ERR(svn_io_check_path(local_abspath, &kind, pool)); + SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); if (kind == svn_node_none) { /* Bootstrap: create an incomplete working-copy root dir. Its entries file should only have an entry for THIS_DIR with a URL, revnum, and an 'incomplete' flag. */ - SVN_ERR(svn_io_make_dir_recursively(local_abspath, pool)); - SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool)); + SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool)); + SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, + scratch_pool)); } else if (kind == svn_node_dir) { int wc_format; const char *entry_url; - SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath, pool)); + SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath, + scratch_pool)); + if (! wc_format) { - SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool)); + SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, + scratch_pool)); } else { /* Get PATH's URL. */ SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath, - pool, pool)); + scratch_pool, scratch_pool)); /* If PATH's existing URL matches the incoming one, then just update. This allows 'svn co' to restart an @@ -146,24 +172,25 @@ svn_client__checkout_internal(svn_revnum_t *result_rev, SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' is already a working copy for a" " different URL"), - svn_dirent_local_style(local_abspath, pool)); + svn_dirent_local_style(local_abspath, scratch_pool)); } } else { return svn_error_createf(SVN_ERR_WC_NODE_KIND_CHANGE, NULL, _("'%s' already exists and is not a directory"), - svn_dirent_local_style(local_abspath, pool)); + svn_dirent_local_style(local_abspath, + scratch_pool)); } /* Have update fix the incompleteness. */ - SVN_ERR(svn_client__update_internal(result_rev, local_abspath, - revision, depth, TRUE, + SVN_ERR(svn_client__update_internal(result_rev, timestamp_sleep, + local_abspath, revision, depth, TRUE, ignore_externals, allow_unver_obstructions, TRUE /* adds_as_modification */, - FALSE, FALSE, - timestamp_sleep, ctx, pool)); + FALSE, FALSE, ra_session, + ctx, scratch_pool)); return SVN_NO_ERROR; } @@ -186,10 +213,12 @@ svn_client_checkout3(svn_revnum_t *result_rev, SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - err = svn_client__checkout_internal(result_rev, URL, local_abspath, + err = svn_client__checkout_internal(result_rev, &sleep_here, + URL, local_abspath, peg_revision, revision, depth, ignore_externals, - allow_unver_obstructions, &sleep_here, + allow_unver_obstructions, + NULL /* ra_session */, ctx, pool); if (sleep_here) svn_io_sleep_for_timestamps(local_abspath, pool); diff --git a/contrib/subversion/subversion/libsvn_client/cleanup.c b/contrib/subversion/subversion/libsvn_client/cleanup.c index b15e824d3..e3fffdcaf 100644 --- a/contrib/subversion/subversion/libsvn_client/cleanup.c +++ b/contrib/subversion/subversion/libsvn_client/cleanup.c @@ -32,32 +32,234 @@ #include "svn_client.h" #include "svn_config.h" #include "svn_dirent_uri.h" +#include "svn_hash.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. ***/ +struct cleanup_status_walk_baton +{ + svn_boolean_t break_locks; + svn_boolean_t fix_timestamps; + svn_boolean_t clear_dav_cache; + svn_boolean_t vacuum_pristines; + svn_boolean_t remove_unversioned_items; + svn_boolean_t remove_ignored_items; + svn_boolean_t include_externals; + svn_client_ctx_t *ctx; +}; + +/* Forward declararion. */ +static svn_error_t * +cleanup_status_walk(void *baton, + const char *local_abspath, + const svn_wc_status3_t *status, + apr_pool_t *scratch_pool); + +static svn_error_t * +do_cleanup(const char *local_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, + svn_boolean_t remove_unversioned_items, + svn_boolean_t remove_ignored_items, + svn_boolean_t include_externals, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_wc_cleanup4(ctx->wc_ctx, + local_abspath, + break_locks, + fix_timestamps, + clear_dav_cache, + vacuum_pristines, + ctx->cancel_func, ctx->cancel_baton, + ctx->notify_func2, ctx->notify_baton2, + scratch_pool)); + + if (fix_timestamps) + svn_io_sleep_for_timestamps(local_abspath, scratch_pool); + + if (remove_unversioned_items || remove_ignored_items || include_externals) + { + struct cleanup_status_walk_baton b; + apr_array_header_t *ignores; + + b.break_locks = break_locks; + b.fix_timestamps = fix_timestamps; + b.clear_dav_cache = clear_dav_cache; + b.vacuum_pristines = vacuum_pristines; + b.remove_unversioned_items = remove_unversioned_items; + b.remove_ignored_items = remove_ignored_items; + b.include_externals = include_externals; + b.ctx = ctx; + + SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool)); + + SVN_WC__CALL_WITH_WRITE_LOCK( + svn_wc_walk_status(ctx->wc_ctx, local_abspath, + svn_depth_infinity, + TRUE, /* get all */ + remove_ignored_items, + TRUE, /* ignore textmods */ + ignores, + cleanup_status_walk, &b, + ctx->cancel_func, + ctx->cancel_baton, + scratch_pool), + ctx->wc_ctx, + local_abspath, + FALSE /* lock_anchor */, + scratch_pool); + } + + return SVN_NO_ERROR; +} + + +/* An implementation of svn_wc_status_func4_t. */ +static svn_error_t * +cleanup_status_walk(void *baton, + const char *local_abspath, + const svn_wc_status3_t *status, + apr_pool_t *scratch_pool) +{ + struct cleanup_status_walk_baton *b = baton; + svn_node_kind_t kind_on_disk; + svn_wc_notify_t *notify; + + if (status->node_status == svn_wc_status_external && b->include_externals) + { + svn_error_t *err; + + SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool)); + if (kind_on_disk == svn_node_dir) + { + if (b->ctx->notify_func2) + { + notify = svn_wc_create_notify(local_abspath, + svn_wc_notify_cleanup_external, + scratch_pool); + b->ctx->notify_func2(b->ctx->notify_baton2, notify, + scratch_pool); + } + + err = do_cleanup(local_abspath, + b->break_locks, + b->fix_timestamps, + b->clear_dav_cache, + b->vacuum_pristines, + b->remove_unversioned_items, + b->remove_ignored_items, + TRUE /* include_externals */, + b->ctx, scratch_pool); + if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + else + SVN_ERR(err); + } + + return SVN_NO_ERROR; + } + + if (status->node_status == svn_wc_status_ignored) + { + if (!b->remove_ignored_items) + return SVN_NO_ERROR; + } + else if (status->node_status == svn_wc_status_unversioned) + { + if (!b->remove_unversioned_items) + return SVN_NO_ERROR; + } + else + return SVN_NO_ERROR; + + SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool)); + switch (kind_on_disk) + { + case svn_node_file: + case svn_node_symlink: + SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool)); + break; + case svn_node_dir: + SVN_ERR(svn_io_remove_dir2(local_abspath, FALSE, + b->ctx->cancel_func, b->ctx->cancel_baton, + scratch_pool)); + break; + case svn_node_none: + default: + return SVN_NO_ERROR; + } + + if (b->ctx->notify_func2) + { + notify = svn_wc_create_notify(local_abspath, svn_wc_notify_delete, + scratch_pool); + notify->kind = kind_on_disk; + b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool); + } + + return SVN_NO_ERROR; +} + svn_error_t * -svn_client_cleanup(const char *path, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) +svn_client_cleanup2(const char *dir_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, + svn_boolean_t include_externals, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - const char *local_abspath; - svn_error_t *err; + SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); - if (svn_path_is_url(path)) - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, - _("'%s' is not a local path"), path); + SVN_ERR(do_cleanup(dir_abspath, + break_locks, + fix_recorded_timestamps, + clear_dav_cache, + vacuum_pristines, + FALSE /* remove_unversioned_items */, + FALSE /* remove_ignored_items */, + include_externals, + ctx, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client_vacuum(const char *dir_abspath, + svn_boolean_t remove_unversioned_items, + svn_boolean_t remove_ignored_items, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t vacuum_pristines, + svn_boolean_t include_externals, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); - SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); + SVN_ERR(do_cleanup(dir_abspath, + FALSE /* break_locks */, + fix_recorded_timestamps, + FALSE /* clear_dav_cache */, + vacuum_pristines, + remove_unversioned_items, + remove_ignored_items, + include_externals, + ctx, scratch_pool)); - err = svn_wc_cleanup3(ctx->wc_ctx, local_abspath, ctx->cancel_func, - ctx->cancel_baton, scratch_pool); - svn_io_sleep_for_timestamps(path, scratch_pool); - return svn_error_trace(err); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/client.h b/contrib/subversion/subversion/libsvn_client/client.h index f13688828..58354cf9b 100644 --- a/contrib/subversion/subversion/libsvn_client/client.h +++ b/contrib/subversion/subversion/libsvn_client/client.h @@ -45,60 +45,48 @@ extern "C" { #endif /* __cplusplus */ -/* Set *REVNUM to the revision number identified by REVISION. - - If REVISION->kind is svn_opt_revision_number, just use - REVISION->value.number, ignoring LOCAL_ABSPATH and RA_SESSION. - - Else if REVISION->kind is svn_opt_revision_committed, - svn_opt_revision_previous, or svn_opt_revision_base, or - svn_opt_revision_working, then the revision can be identified - purely based on the working copy's administrative information for - LOCAL_ABSPATH, so RA_SESSION is ignored. If LOCAL_ABSPATH is not - under revision control, return SVN_ERR_UNVERSIONED_RESOURCE, or if - LOCAL_ABSPATH is null, return SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED. - - Else if REVISION->kind is svn_opt_revision_date or - svn_opt_revision_head, then RA_SESSION is used to retrieve the - revision from the repository (using REVISION->value.date in the - former case), and LOCAL_ABSPATH is ignored. If RA_SESSION is null, - return SVN_ERR_CLIENT_RA_ACCESS_REQUIRED. - - Else if REVISION->kind is svn_opt_revision_unspecified, set - *REVNUM to SVN_INVALID_REVNUM. - - If YOUNGEST_REV is non-NULL, it is an in/out parameter. If - *YOUNGEST_REV is valid, use it as the youngest revision in the - repository (regardless of reality) -- don't bother to lookup the - true value for HEAD, and don't return any value in *REVNUM greater - than *YOUNGEST_REV. If *YOUNGEST_REV is not valid, and a HEAD - lookup is required to populate *REVNUM, then also populate - *YOUNGEST_REV with the result. This is useful for making multiple - serialized calls to this function with a basically static view of - the repository, avoiding race conditions which could occur between - multiple invocations with HEAD lookup requests. - - Else return SVN_ERR_CLIENT_BAD_REVISION. - - Use SCRATCH_POOL for any temporary allocation. */ -svn_error_t * -svn_client__get_revision_number(svn_revnum_t *revnum, - svn_revnum_t *youngest_rev, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - svn_ra_session_t *ra_session, - const svn_opt_revision_t *revision, - apr_pool_t *scratch_pool); + +/* Private client context. + * + * This is what is actually allocated by svn_client_create_context2(), + * which then returns the address of the public_ctx member. */ +typedef struct svn_client__private_ctx_t +{ + /* Reserved field, always zero, to detect misuse of the private + context as a public client context. */ + apr_uint64_t magic_null; + + /* Reserved field, always set to a known magic number, to identify + this struct as the private client context. */ + apr_uint64_t magic_id; + + /* Total number of bytes transferred over network across all RA sessions. */ + apr_off_t total_progress; + + /* The public context. */ + svn_client_ctx_t public_ctx; +} svn_client__private_ctx_t; + + +/* Given a public client context CTX, return the private context + within which it is allocated. */ +svn_client__private_ctx_t * +svn_client__get_private_ctx(svn_client_ctx_t *ctx); /* Set *ORIGINAL_REPOS_RELPATH and *ORIGINAL_REVISION to the original location that served as the source of the copy from which PATH_OR_URL at REVISION was created, or NULL and SVN_INVALID_REVNUM (respectively) if PATH_OR_URL at - REVISION was not the result of a copy operation. */ + REVISION was not the result of a copy operation. + + If RA_SESSION is not NULL it is an existing session to the repository that + might be reparented temporarily to obtain the information. + */ 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_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -212,7 +200,6 @@ svn_client__repos_location_segments(apr_array_header_t **segments, See also svn_client__calc_youngest_common_ancestor() to find youngest common ancestor for already fetched history-as-mergeinfo information. - See also svn_client__youngest_common_ancestor(). */ svn_error_t * svn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, @@ -422,17 +409,6 @@ svn_error_t *svn_client__get_all_auto_props(apr_hash_t **autoprops, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Get a list of ignore patterns defined by the svn:global-ignores - properties set on, or inherited by, PATH_OR_URL. Store the collected - patterns as const char * elements in the array *IGNORES. Allocate - *IGNORES and its contents in RESULT_POOL. Use SCRATCH_POOL for - temporary allocations. */ -svn_error_t *svn_client__get_inherited_ignores(apr_array_header_t **ignores, - const char *path_or_url, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* The main logic for client deletion from a working copy. Deletes PATH from CTX->WC_CTX. If PATH (or any item below a directory PATH) is modified the delete will fail and return an error unless FORCE or KEEP_LOCAL @@ -467,10 +443,10 @@ svn_client__wc_delete_many(const apr_array_header_t *targets, apr_pool_t *pool); -/* Make PATH and add it to the working copy, optionally making all the - intermediate parent directories if MAKE_PARENTS is TRUE. */ +/* Make LOCAL_ABSPATH and add it to the working copy, optionally making all + the intermediate parent directories if MAKE_PARENTS is TRUE. */ svn_error_t * -svn_client__make_local_parents(const char *path, +svn_client__make_local_parents(const char *local_abspath, svn_boolean_t make_parents, svn_client_ctx_t *ctx, apr_pool_t *pool); @@ -519,10 +495,14 @@ svn_client__make_local_parents(const char *path, (with depth=empty) any parent directories of the requested update target which are missing from the working copy. + If RA_SESSION is NOT NULL, it may be used to avoid creating a new + session. The session may point to a different URL after returning. + NOTE: You may not specify both INNERUPDATE and MAKE_PARENTS as true. */ svn_error_t * svn_client__update_internal(svn_revnum_t *result_rev, + svn_boolean_t *timestamp_sleep, const char *local_abspath, const svn_opt_revision_t *revision, svn_depth_t depth, @@ -532,7 +512,7 @@ svn_client__update_internal(svn_revnum_t *result_rev, svn_boolean_t adds_as_modification, svn_boolean_t make_parents, svn_boolean_t innerupdate, - svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool); @@ -548,11 +528,6 @@ svn_client__update_internal(svn_revnum_t *result_rev, DEPTH must be a definite depth, not (e.g.) svn_depth_unknown. - RA_CACHE is a pointer to a cache of information for the URL at - REVISION based on the PEG_REVISION. Any information not in - *RA_CACHE is retrieved by a round-trip to the repository. RA_CACHE - may be NULL which indicates that no cache information is available. - If IGNORE_EXTERNALS is true, do no externals processing. Set *TIMESTAMP_SLEEP to TRUE if a sleep is required; otherwise do not @@ -564,10 +539,12 @@ svn_client__update_internal(svn_revnum_t *result_rev, the repos are tolerated; if FALSE, these obstructions cause the checkout to fail. - If INNERCHECKOUT is true, no anchor check is performed on the target. + If RA_SESSION is NOT NULL, it may be used to avoid creating a new + session. The session may point to a different URL after returning. */ svn_error_t * svn_client__checkout_internal(svn_revnum_t *result_rev, + svn_boolean_t *timestamp_sleep, const char *URL, const char *local_abspath, const svn_opt_revision_t *peg_revision, @@ -575,7 +552,7 @@ svn_client__checkout_internal(svn_revnum_t *result_rev, svn_depth_t depth, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, - svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool); @@ -707,24 +684,28 @@ svn_client__get_diff_editor2(const svn_delta_editor_t **editor, /*** Editor for diff summary ***/ -/* Set *CALLBACKS and *CALLBACK_BATON to a set of diff callbacks that will - report a diff summary, i.e. only providing information about the changed - items without the text deltas. +/* Set *DIFF_PROCESSOR to a diff processor that will report a diff summary + to SUMMARIZE_FUNC. + + P_ROOT_RELPATH will return a pointer to a string that must be set to + the root of the operation before the processor is called. - TARGET is the target path, relative to the anchor, of the diff. + ORIGINAL_PATH specifies the original path and will be used with + **ANCHOR_PATH to create paths as the user originally provided them + to the diff function. SUMMARIZE_FUNC is called with SUMMARIZE_BATON as parameter by the created callbacks for each changed item. */ svn_error_t * svn_client__get_diff_summarize_callbacks( - svn_wc_diff_callbacks4_t **callbacks, - void **callback_baton, - const char *target, - svn_boolean_t reversed, + const svn_diff_tree_processor_t **diff_processor, + const char ***p_root_relpath, svn_client_diff_summarize_func_t summarize_func, void *summarize_baton, - apr_pool_t *pool); + const char *original_target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* ---------------------------------------------------------------- */ @@ -921,11 +902,6 @@ svn_client__get_copy_committables(svn_client__committables_t **committables, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* A qsort()-compatible sort routine for sorting an array of - svn_client_commit_item_t *'s by their URL member. */ -int svn_client__sort_commit_item_urls(const void *a, const void *b); - - /* Rewrite the COMMIT_ITEMS array to be sorted by URL. Also, discover a common *BASE_URL for the items in the array, and rewrite those items' URLs to be relative to that *BASE_URL. @@ -939,18 +915,6 @@ svn_client__condense_commit_items(const char **base_url, apr_array_header_t *commit_items, apr_pool_t *pool); - -/* Like svn_ra_stat() on the ra session root, but with a compatibility - hack for pre-1.2 svnserve that don't support this api. */ -svn_error_t * -svn_client__ra_stat_compatible(svn_ra_session_t *ra_session, - svn_revnum_t rev, - svn_dirent_t **dirent_p, - apr_uint32_t dirent_fields, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool); - - /* Commit the items in the COMMIT_ITEMS array using EDITOR/EDIT_BATON to describe the committed local mods. Prior to this call, COMMIT_ITEMS should have been run through (and BASE_URL generated @@ -1016,6 +980,9 @@ svn_client__do_commit(const char *base_url, change *TIMESTAMP_SLEEP. The output will be valid even if the function returns an error. + If RA_SESSION is NOT NULL, it may be used to avoid creating a new + session. The session may point to a different URL after returning. + Use POOL for temporary allocation. */ svn_error_t * svn_client__handle_externals(apr_hash_t *externals_new, @@ -1024,6 +991,7 @@ svn_client__handle_externals(apr_hash_t *externals_new, const char *target_abspath, svn_depth_t requested_depth, svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool); @@ -1157,7 +1125,61 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool); +/* Produce a diff with depth DEPTH between two files or two directories at + * LEFT_ABSPATH1 and RIGHT_ABSPATH, using the provided diff callbacks to + * show changes in files. The files and directories involved may be part of + * a working copy or they may be unversioned. For versioned files, show + * property changes, too. + * + * If ANCHOR_ABSPATH is not null, set it to the anchor of the diff before + * the first processor call. (The anchor is LEFT_ABSPATH or an ancestor of it) + */ +svn_error_t * +svn_client__arbitrary_nodes_diff(const char **root_relpath, + svn_boolean_t *root_is_dir, + const char *left_abspath, + const char *right_abspath, + svn_depth_t depth, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Helper for the remote case of svn_client_propget. + * + * If PROPS is not null, then get the value of property PROPNAME in + * REVNUM, using RA_SESSION. Store the value ('svn_string_t *') in + * PROPS, under the path key "TARGET_PREFIX/TARGET_RELATIVE" + * ('const char *'). + * + * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a + * depth-first ordered array of svn_prop_inherited_item_t * structures + * representing the PROPNAME properties inherited by the target. If + * INHERITABLE_PROPS in not null and no inheritable properties are found, + * then set *INHERITED_PROPS to an empty array. + * + * Recurse according to DEPTH, similarly to svn_client_propget3(). + * + * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE". + * Yes, caller passes this; it makes the recursion more efficient :-). + * + * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary + * work in SCRATCH_POOL. The two pools can be the same; recursive + * calls may use a different SCRATCH_POOL, however. + */ +svn_error_t * +svn_client__remote_propget(apr_hash_t *props, + apr_array_header_t **inherited_props, + const char *propname, + const char *target_prefix, + const char *target_relative, + svn_node_kind_t kind, + svn_revnum_t revnum, + svn_ra_session_t *ra_session, + svn_depth_t depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/libsvn_client/cmdline.c b/contrib/subversion/subversion/libsvn_client/cmdline.c index a17f8c478..a850b4e68 100644 --- a/contrib/subversion/subversion/libsvn_client/cmdline.c +++ b/contrib/subversion/subversion/libsvn_client/cmdline.c @@ -87,7 +87,7 @@ check_root_url_of_target(const char **root_url, if ((err->apr_err == SVN_ERR_ENTRY_NOT_FOUND) || (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) || (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) - || (err->apr_err == SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED) + || (err->apr_err == SVN_ERR_RA_CANNOT_CREATE_SESSION) || (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)) { svn_error_clear(err); @@ -200,6 +200,15 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p, SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev, utf8_target, pool)); + /* Reject the form "@abc", a peg specifier with no path. */ + if (true_target[0] == '\0' && peg_rev[0] != '\0') + { + return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, + _("'%s' is just a peg revision. " + "Maybe try '%s@' instead?"), + utf8_target, utf8_target); + } + /* URLs and wc-paths get treated differently. */ if (svn_path_is_url(true_target)) { @@ -278,7 +287,7 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p, } } - target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); + target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL); if (rel_url_found) { @@ -338,7 +347,7 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p, SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, abs_target, pool)); - target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); + target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL); } APR_ARRAY_PUSH(*targets_p, const char *) = target; diff --git a/contrib/subversion/subversion/libsvn_client/commit.c b/contrib/subversion/subversion/libsvn_client/commit.c index 07fdce19c..4a945c887 100644 --- a/contrib/subversion/subversion/libsvn_client/commit.c +++ b/contrib/subversion/subversion/libsvn_client/commit.c @@ -45,6 +45,7 @@ #include "client.h" #include "private/svn_wc_private.h" #include "private/svn_ra_private.h" +#include "private/svn_sorts_private.h" #include "svn_private_config.h" @@ -110,7 +111,8 @@ get_ra_editor(const svn_delta_editor_t **editor, continue; svn_pool_clear(iterpool); - SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL, + SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, + NULL, NULL, ctx->wc_ctx, item->path, FALSE, pool, iterpool)); if (relpath) @@ -203,8 +205,8 @@ collect_lock_tokens(apr_hash_t **result, for (hi = apr_hash_first(pool, all_tokens); hi; hi = apr_hash_next(hi)) { - const char *url = svn__apr_hash_index_key(hi); - const char *token = svn__apr_hash_index_val(hi); + const char *url = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); const char *relpath = svn_uri_skip_ancestor(base_url, url, pool); if (relpath) @@ -238,91 +240,30 @@ post_process_commit_item(svn_wc_committed_queue_t *queue, loop_recurse = TRUE; remove_lock = (! keep_locks && (item->state_flags - & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)); + & (SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN + | SVN_CLIENT_COMMIT_ITEM_ADD + | SVN_CLIENT_COMMIT_ITEM_DELETE))); - /* When the node was deleted (or replaced), we need to always remove the - locks, as they're invalidated on the server. We cannot honor the + /* When the node was deleted (or replaced), we need to always remove the + locks, as they're invalidated on the server. We cannot honor the SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN flag here because it does not tell us whether we have locked children. */ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) remove_lock = TRUE; - return svn_wc_queue_committed3(queue, wc_ctx, item->path, - loop_recurse, item->incoming_prop_changes, + return svn_error_trace( + svn_wc_queue_committed4(queue, wc_ctx, item->path, + loop_recurse, + 0 != (item->state_flags & + (SVN_CLIENT_COMMIT_ITEM_ADD + | SVN_CLIENT_COMMIT_ITEM_DELETE + | SVN_CLIENT_COMMIT_ITEM_TEXT_MODS + | SVN_CLIENT_COMMIT_ITEM_PROP_MODS)), + item->incoming_prop_changes, remove_lock, !keep_changelists, - sha1_checksum, scratch_pool); -} - - -static svn_error_t * -check_nonrecursive_dir_delete(svn_wc_context_t *wc_ctx, - const char *target_abspath, - svn_depth_t depth, - apr_pool_t *scratch_pool) -{ - svn_node_kind_t kind; - - SVN_ERR_ASSERT(depth != svn_depth_infinity); - - SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, target_abspath, - TRUE, FALSE, scratch_pool)); - - - /* ### TODO(sd): This check is slightly too strict. It should be - ### possible to: - ### - ### * delete a directory containing only files when - ### depth==svn_depth_files; - ### - ### * delete a directory containing only files and empty - ### subdirs when depth==svn_depth_immediates. - ### - ### But for now, we insist on svn_depth_infinity if you're - ### going to delete a directory, because we're lazy and - ### trying to get depthy commits working in the first place. - ### - ### This would be fairly easy to fix, though: just, well, - ### check the above conditions! - ### - ### GJS: I think there may be some confusion here. there is - ### the depth of the commit, and the depth of a checked-out - ### directory in the working copy. Delete, by its nature, will - ### always delete all of its children, so it seems a bit - ### strange to worry about what is in the working copy. - */ - if (kind == svn_node_dir) - { - svn_wc_schedule_t schedule; - - /* ### Looking at schedule is probably enough, no need for - pristine compare etc. */ - SVN_ERR(svn_wc__node_get_schedule(&schedule, NULL, - wc_ctx, target_abspath, - scratch_pool)); - - if (schedule == svn_wc_schedule_delete - || schedule == svn_wc_schedule_replace) - { - const apr_array_header_t *children; - - SVN_ERR(svn_wc__node_get_children(&children, wc_ctx, - target_abspath, TRUE, - scratch_pool, scratch_pool)); - - if (children->nelts > 0) - return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, - _("Cannot delete the directory '%s' " - "in a non-recursive commit " - "because it has children"), - svn_dirent_local_style(target_abspath, - scratch_pool)); - } - } - - return SVN_NO_ERROR; + sha1_checksum, scratch_pool)); } - /* Given a list of committables described by their common base abspath BASE_ABSPATH and a list of relative dirents TARGET_RELPATHS determine which absolute paths must be locked to commit all these targets and @@ -393,8 +334,8 @@ determine_lock_targets(apr_array_header_t **lock_targets, hi = apr_hash_next(hi)) { const char *common; - const char *wcroot_abspath = svn__apr_hash_index_key(hi); - apr_array_header_t *wc_targets = svn__apr_hash_index_val(hi); + const char *wcroot_abspath = apr_hash_this_key(hi); + apr_array_header_t *wc_targets = apr_hash_this_val(hi); svn_pool_clear(iterpool); @@ -420,8 +361,7 @@ determine_lock_targets(apr_array_header_t **lock_targets, SVN_ERR(svn_dirent_condense_targets(&common, &wc_targets, wc_targets, FALSE, iterpool, iterpool)); - qsort(wc_targets->elts, wc_targets->nelts, wc_targets->elt_size, - svn_sort_compare_paths); + svn_sort__array(wc_targets, svn_sort_compare_paths); if (wc_targets->nelts == 0 || !svn_path_is_empty(APR_ARRAY_IDX(wc_targets, 0, const char*)) @@ -599,6 +539,7 @@ svn_client_commit6(const apr_array_header_t *targets, const char *current_abspath; const char *notify_prefix; int depth_empty_after = -1; + apr_hash_t *move_youngest = NULL; int i; SVN_ERR_ASSERT(depth != svn_depth_unknown && depth != svn_depth_exclude); @@ -673,26 +614,6 @@ svn_client_commit6(const apr_array_header_t *targets, base_abspath, pool); - /* If a non-recursive commit is desired, do not allow a deleted directory - as one of the targets. */ - if (depth != svn_depth_infinity && ! commit_as_operations) - for (i = 0; i < rel_targets->nelts; i++) - { - const char *relpath = APR_ARRAY_IDX(rel_targets, i, const char *); - const char *target_abspath; - - svn_pool_clear(iterpool); - - target_abspath = svn_dirent_join(base_abspath, relpath, iterpool); - - cmt_err = svn_error_trace( - check_nonrecursive_dir_delete(ctx->wc_ctx, target_abspath, - depth, iterpool)); - - if (cmt_err) - goto cleanup; - } - /* Crawl the working copy for commit items. */ { struct check_url_kind_baton cukb; @@ -741,7 +662,7 @@ svn_client_commit6(const apr_array_header_t *targets, apr_hash_index_t *hi = apr_hash_first(iterpool, committables->by_repository); - commit_items = svn__apr_hash_index_val(hi); + commit_items = apr_hash_this_val(hi); } /* If our array of targets contains only locks (and no actual file @@ -789,62 +710,12 @@ svn_client_commit6(const apr_array_header_t *targets, if (cmt_err) goto cleanup; - if (moved_from_abspath && delete_op_root_abspath && - strcmp(moved_from_abspath, delete_op_root_abspath) == 0) - + if (moved_from_abspath && delete_op_root_abspath) { - svn_boolean_t found_delete_half = - (svn_hash_gets(committables->by_path, delete_op_root_abspath) - != NULL); - - if (!found_delete_half) - { - const char *delete_half_parent_abspath; - - /* The delete-half isn't in the commit target list. - * However, it might itself be the child of a deleted node, - * either because of another move or a deletion. - * - * For example, consider: mv A/B B; mv B/C C; commit; - * C's moved-from A/B/C is a child of the deleted A/B. - * A/B/C does not appear in the commit target list, but - * A/B does appear. - * (Note that moved-from information is always stored - * relative to the BASE tree, so we have 'C moved-from - * A/B/C', not 'C moved-from B/C'.) - * - * An example involving a move and a delete would be: - * mv A/B C; rm A; commit; - * Now C is moved-from A/B which does not appear in the - * commit target list, but A does appear. - */ - - /* Scan upwards for a deletion op-root from the - * delete-half's parent directory. */ - delete_half_parent_abspath = - svn_dirent_dirname(delete_op_root_abspath, iterpool); - if (strcmp(delete_op_root_abspath, - delete_half_parent_abspath) != 0) - { - const char *parent_delete_op_root_abspath; - - cmt_err = svn_error_trace( - svn_wc__node_get_deleted_ancestor( - &parent_delete_op_root_abspath, - ctx->wc_ctx, delete_half_parent_abspath, - iterpool, iterpool)); - if (cmt_err) - goto cleanup; - - if (parent_delete_op_root_abspath) - found_delete_half = - (svn_hash_gets(committables->by_path, - parent_delete_op_root_abspath) - != NULL); - } - } + svn_client_commit_item3_t *delete_half = + svn_hash_gets(committables->by_path, delete_op_root_abspath); - if (!found_delete_half) + if (!delete_half) { cmt_err = svn_error_createf( SVN_ERR_ILLEGAL_TARGET, NULL, @@ -854,8 +725,32 @@ svn_client_commit6(const apr_array_header_t *targets, svn_dirent_local_style(item->path, iterpool), svn_dirent_local_style(delete_op_root_abspath, iterpool)); + + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify( + delete_op_root_abspath, + svn_wc_notify_failed_requires_target, + iterpool); + notify->err = cmt_err; + + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); + } + goto cleanup; } + else if (delete_half->revision == item->copyfrom_rev) + { + /* Ok, now we know that we perform an out-of-date check + on the copyfrom location. Remember this for a fixup + round right before committing. */ + + if (!move_youngest) + move_youngest = apr_hash_make(pool); + + svn_hash_sets(move_youngest, item->path, item); + } } } @@ -885,6 +780,19 @@ svn_client_commit6(const apr_array_header_t *targets, svn_dirent_local_style(item->path, iterpool), svn_dirent_local_style(copy_op_root_abspath, iterpool)); + + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify( + copy_op_root_abspath, + svn_wc_notify_failed_requires_target, + iterpool); + notify->err = cmt_err; + + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); + } + goto cleanup; } } @@ -941,6 +849,37 @@ svn_client_commit6(const apr_array_header_t *targets, if (cmt_err) goto cleanup; + if (move_youngest != NULL) + { + apr_hash_index_t *hi; + svn_revnum_t youngest; + + SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest, pool)); + + for (hi = apr_hash_first(iterpool, move_youngest); + hi; + hi = apr_hash_next(hi)) + { + svn_client_commit_item3_t *item = apr_hash_this_val(hi); + + /* We delete the original side with its original revision and will + receive an out-of-date error if that node changed since that + revision. + + The copy is of that same revision and we know that this revision + didn't change between this revision and youngest. So we can just + as well commit a copy from youngest. + + Note that it is still possible to see gaps between the delete and + copy revisions as the repository might handle multiple commits + at the same time (or when an out of date proxy is involved), but + in general it should decrease the number of gaps. */ + + if (item->copyfrom_rev < youngest) + item->copyfrom_rev = youngest; + } + } + cmt_err = svn_error_trace( get_ra_editor(&editor, &edit_baton, ra_session, ctx, log_msg, commit_items, revprop_table, @@ -996,6 +935,9 @@ svn_client_commit6(const apr_array_header_t *targets, commit_info->author, ctx->cancel_func, ctx->cancel_baton, iterpool); + + if (bump_err) + goto cleanup; } cleanup: @@ -1004,16 +946,16 @@ svn_client_commit6(const apr_array_header_t *targets, working copies. */ if (timestamp_sleep) { - const char *wcroot_abspath; - svn_error_t *err = svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, + const char *sleep_abspath; + svn_error_t *err = svn_wc__get_wcroot(&sleep_abspath, ctx->wc_ctx, base_abspath, pool, pool); if (err) { svn_error_clear(err); - wcroot_abspath = NULL; + sleep_abspath = base_abspath; } - svn_io_sleep_for_timestamps(wcroot_abspath, pool); + svn_io_sleep_for_timestamps(sleep_abspath, pool); } /* Abort the commit if it is still in progress. */ diff --git a/contrib/subversion/subversion/libsvn_client/commit_util.c b/contrib/subversion/subversion/libsvn_client/commit_util.c index a32ec5d3e..1f3d87783 100644 --- a/contrib/subversion/subversion/libsvn_client/commit_util.c +++ b/contrib/subversion/subversion/libsvn_client/commit_util.c @@ -40,11 +40,11 @@ #include "svn_hash.h" #include -#include /* for qsort() */ #include "svn_private_config.h" #include "private/svn_wc_private.h" #include "private/svn_client_private.h" +#include "private/svn_sorts_private.h" /*** Uncomment this to turn on commit driver debugging. ***/ /* @@ -62,10 +62,12 @@ fixup_commit_error(const char *local_abspath, apr_pool_t *scratch_pool) { if (err->apr_err == SVN_ERR_FS_NOT_FOUND + || err->apr_err == SVN_ERR_FS_CONFLICT || err->apr_err == SVN_ERR_FS_ALREADY_EXISTS || err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE || err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND || err->apr_err == SVN_ERR_RA_DAV_ALREADY_EXISTS + || err->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED || svn_error_find_cause(err, SVN_ERR_RA_OUT_OF_DATE)) { if (ctx->notify_func2) @@ -102,6 +104,7 @@ fixup_commit_error(const char *local_abspath, } else if (svn_error_find_cause(err, SVN_ERR_FS_NO_LOCK_TOKEN) || err->apr_err == SVN_ERR_FS_LOCK_OWNER_MISMATCH + || err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN || err->apr_err == SVN_ERR_RA_NOT_LOCKED) { if (ctx->notify_func2) @@ -464,10 +467,12 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx, apr_pool_t *iterpool = svn_pool_create(scratch_pool); int i; + SVN_ERR_ASSERT(commit_relpath != NULL); + /* A function to retrieve not present children would be nice to have */ - SVN_ERR(svn_wc__node_get_children_of_working_node( - &children, wc_ctx, local_abspath, TRUE, - scratch_pool, iterpool)); + SVN_ERR(svn_wc__node_get_not_present_children(&children, wc_ctx, + local_abspath, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -483,13 +488,10 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx, this_abspath, FALSE, scratch_pool)); if (!not_present) - continue; + continue; /* Node is replaced */ - if (commit_relpath == NULL) - this_commit_relpath = NULL; - else - this_commit_relpath = svn_relpath_join(commit_relpath, name, - iterpool); + this_commit_relpath = svn_relpath_join(commit_relpath, name, + iterpool); /* We should check if we should really add a delete operation */ if (check_url_func) @@ -502,7 +504,7 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx, /* Determine from what parent we would be the deleted child */ SVN_ERR(svn_wc__node_get_origin( NULL, &parent_rev, &parent_repos_relpath, - &parent_repos_root_url, NULL, NULL, + &parent_repos_root_url, NULL, NULL, NULL, wc_ctx, svn_dirent_dirname(this_abspath, scratch_pool), @@ -768,13 +770,14 @@ harvest_status_callback(void *status_baton, && !(state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)) { svn_revnum_t dir_rev = SVN_INVALID_REVNUM; + const char *dir_repos_relpath = NULL; - if (!copy_mode_root && !status->switched && !is_added) - SVN_ERR(svn_wc__node_get_base(NULL, &dir_rev, NULL, NULL, NULL, NULL, + if (!copy_mode_root && !is_added) + SVN_ERR(svn_wc__node_get_base(NULL, &dir_rev, &dir_repos_relpath, NULL, + NULL, NULL, wc_ctx, svn_dirent_dirname(local_abspath, scratch_pool), FALSE /* ignore_enoent */, - FALSE /* show_hidden */, scratch_pool, scratch_pool)); if (copy_mode_root || status->switched || node_rev != dir_rev) @@ -794,6 +797,25 @@ harvest_status_callback(void *status_baton, cf_rev = status->revision; cf_relpath = status->repos_relpath; } + + if (!copy_mode_root && !is_added && baton->check_url_func + && dir_repos_relpath) + { + svn_node_kind_t me_kind; + /* Maybe we need to issue an delete (mixed rev/switched) */ + + SVN_ERR(baton->check_url_func( + baton->check_url_baton, &me_kind, + svn_path_url_add_component2(repos_root_url, + svn_relpath_join(dir_repos_relpath, + svn_dirent_basename(local_abspath, + NULL), + scratch_pool), + scratch_pool), + dir_rev, scratch_pool)); + if (me_kind != svn_node_none) + state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE; + } } } @@ -919,7 +941,7 @@ harvest_status_callback(void *status_baton, * directory. In either case, we require the op-root of the parent * to be part of the commit. See issue #4059. */ SVN_ERR(svn_wc__node_get_origin(&parent_is_copy, NULL, NULL, NULL, - NULL, ©_root_abspath, + NULL, NULL, ©_root_abspath, wc_ctx, parent_abspath, FALSE, scratch_pool, scratch_pool)); @@ -1233,13 +1255,13 @@ svn_client__harvest_committables(svn_client__committables_t **committables, /* Make sure that every path in danglers is part of the commit. */ for (hi = apr_hash_first(scratch_pool, danglers); hi; hi = apr_hash_next(hi)) { - const char *dangling_parent = svn__apr_hash_index_key(hi); + const char *dangling_parent = apr_hash_this_key(hi); svn_pool_clear(iterpool); if (! look_up_committable(*committables, dangling_parent, iterpool)) { - const char *dangling_child = svn__apr_hash_index_val(hi); + const char *dangling_child = apr_hash_this_val(hi); if (ctx->notify_func2 != NULL) { @@ -1357,7 +1379,10 @@ svn_client__get_copy_committables(svn_client__committables_t **committables, } -int svn_client__sort_commit_item_urls(const void *a, const void *b) +/* A svn_sort__array()/qsort()-compatible sort routine for sorting + an array of svn_client_commit_item_t *'s by their URL member. */ +static int +sort_commit_item_urls(const void *a, const void *b) { const svn_client_commit_item3_t *item1 = *((const svn_client_commit_item3_t * const *) a); @@ -1381,8 +1406,7 @@ svn_client__condense_commit_items(const char **base_url, SVN_ERR_ASSERT(ci && ci->nelts); /* Sort our commit items by their URLs. */ - qsort(ci->elts, ci->nelts, - ci->elt_size, svn_client__sort_commit_item_urls); + svn_sort__array(ci, sort_commit_item_urls); /* Loop through the URLs, finding the longest usable ancestor common to all of them, and making sure there are no duplicate URLs. */ @@ -1470,6 +1494,7 @@ struct file_mod_t { const svn_client_commit_item3_t *item; void *file_baton; + apr_pool_t *file_pool; }; @@ -1535,6 +1560,9 @@ do_item_commit(void **dir_baton, else file_pool = pool; + /* Subpools are cheap, but memory isn't */ + file_pool = svn_pool_create(file_pool); + /* Call the cancellation function. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); @@ -1618,11 +1646,12 @@ do_item_commit(void **dir_baton, else notify = NULL; + if (notify) { notify->kind = item->kind; notify->path_prefix = icb->notify_path_prefix; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } } @@ -1783,6 +1812,7 @@ do_item_commit(void **dir_baton, /* Add this file mod to the FILE_MODS hash. */ mod->item = item; mod->file_baton = file_baton; + mod->file_pool = file_pool; svn_hash_sets(file_mods, item->session_relpath, mod); } else if (file_baton) @@ -1790,7 +1820,7 @@ do_item_commit(void **dir_baton, /* Close any outstanding file batons that didn't get caught by the "has local mods" conditional above. */ err = editor->close_file(file_baton, NULL, file_pool); - + svn_pool_destroy(file_pool); if (err) goto fixup_error; } @@ -1858,7 +1888,7 @@ svn_client__do_commit(const char *base_url, hi; hi = apr_hash_next(hi)) { - struct file_mod_t *mod = svn__apr_hash_index_val(hi); + struct file_mod_t *mod = apr_hash_this_val(hi); const svn_client_commit_item3_t *item = mod->item; const svn_checksum_t *new_text_base_md5_checksum; const svn_checksum_t *new_text_base_sha1_checksum; @@ -1905,6 +1935,17 @@ svn_client__do_commit(const char *base_url, if (sha1_checksums) svn_hash_sets(*sha1_checksums, item->path, new_text_base_sha1_checksum); + + svn_pool_destroy(mod->file_pool); + } + + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(base_url, + svn_wc_notify_commit_finalizing, + iterpool); + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/libsvn_client/compat_providers.c b/contrib/subversion/subversion/libsvn_client/compat_providers.c index ae53a15cd..864d158e3 100644 --- a/contrib/subversion/subversion/libsvn_client/compat_providers.c +++ b/contrib/subversion/subversion/libsvn_client/compat_providers.c @@ -27,6 +27,10 @@ /*** Includes. ***/ +/* We define this here to remove any further warnings about the usage of + deprecated functions in this file. */ +#define SVN_DEPRECATED + #include "svn_auth.h" #include "svn_client.h" diff --git a/contrib/subversion/subversion/libsvn_client/copy.c b/contrib/subversion/subversion/libsvn_client/copy.c index 1a48b6715..12bbfe6cf 100644 --- a/contrib/subversion/subversion/libsvn_client/copy.c +++ b/contrib/subversion/subversion/libsvn_client/copy.c @@ -177,12 +177,513 @@ get_copy_pair_ancestors(const apr_array_header_t *copy_pairs, return SVN_NO_ERROR; } +/* Quote a string if it would be handled as multiple or different tokens + during externals parsing */ +static const char * +maybe_quote(const char *value, + apr_pool_t *result_pool) +{ + apr_status_t status; + char **argv; + + status = apr_tokenize_to_argv(value, &argv, result_pool); + + if (!status && argv[0] && !argv[1] && strcmp(argv[0], value) == 0) + return apr_pstrdup(result_pool, value); + + { + svn_stringbuf_t *sb = svn_stringbuf_create_empty(result_pool); + const char *c; + + svn_stringbuf_appendbyte(sb, '\"'); + + for (c = value; *c; c++) + { + if (*c == '\\' || *c == '\"' || *c == '\'') + svn_stringbuf_appendbyte(sb, '\\'); + + svn_stringbuf_appendbyte(sb, *c); + } + + svn_stringbuf_appendbyte(sb, '\"'); + +#ifdef SVN_DEBUG + status = apr_tokenize_to_argv(sb->data, &argv, result_pool); + + SVN_ERR_ASSERT_NO_RETURN(!status && argv[0] && !argv[1] + && !strcmp(argv[0], value)); +#endif + + return sb->data; + } +} + +/* In *NEW_EXTERNALS_DESCRIPTION, return a new external description for + * use as a line in an svn:externals property, based on the external item + * ITEM and the additional parser information in INFO. Pin the external + * to EXTERNAL_PEGREV. Use POOL for all allocations. */ +static svn_error_t * +make_external_description(const char **new_external_description, + const char *local_abspath_or_url, + svn_wc_external_item2_t *item, + svn_wc__externals_parser_info_t *info, + svn_opt_revision_t external_pegrev, + apr_pool_t *pool) +{ + const char *rev_str; + const char *peg_rev_str; + + switch (info->format) + { + case svn_wc__external_description_format_1: + if (external_pegrev.kind == svn_opt_revision_unspecified) + { + /* If info->rev_str is NULL, this yields an empty string. */ + rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL); + } + else if (info->rev_str && item->revision.kind != svn_opt_revision_head) + rev_str = apr_psprintf(pool, "%s ", info->rev_str); + else + { + /* ### can't handle svn_opt_revision_date without info->rev_str */ + SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number); + rev_str = apr_psprintf(pool, "-r%ld ", + external_pegrev.value.number); + } + + *new_external_description = + apr_psprintf(pool, "%s %s%s\n", maybe_quote(item->target_dir, pool), + rev_str, + maybe_quote(item->url, pool)); + break; + + case svn_wc__external_description_format_2: + if (external_pegrev.kind == svn_opt_revision_unspecified) + { + /* If info->rev_str is NULL, this yields an empty string. */ + rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL); + } + else if (info->rev_str && item->revision.kind != svn_opt_revision_head) + rev_str = apr_psprintf(pool, "%s ", info->rev_str); + else + rev_str = ""; + + if (external_pegrev.kind == svn_opt_revision_unspecified) + peg_rev_str = info->peg_rev_str ? info->peg_rev_str : ""; + else if (info->peg_rev_str && + item->peg_revision.kind != svn_opt_revision_head) + peg_rev_str = info->peg_rev_str; + else + { + /* ### can't handle svn_opt_revision_date without info->rev_str */ + SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number); + peg_rev_str = apr_psprintf(pool, "@%ld", + external_pegrev.value.number); + } + + *new_external_description = + apr_psprintf(pool, "%s%s %s\n", rev_str, + maybe_quote(apr_psprintf(pool, "%s%s", item->url, + peg_rev_str), + pool), + maybe_quote(item->target_dir, pool)); + break; + + default: + return svn_error_createf( + SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, + _("%s property defined at '%s' is using an unsupported " + "syntax"), SVN_PROP_EXTERNALS, + svn_dirent_local_style(local_abspath_or_url, pool)); + } + + return SVN_NO_ERROR; +} + +/* Pin all externals listed in EXTERNALS_PROP_VAL to their + * last-changed revision. Set *PINNED_EXTERNALS to a new property + * value allocated in RESULT_POOL, or to NULL if none of the externals + * in EXTERNALS_PROP_VAL were changed. LOCAL_ABSPATH_OR_URL is the + * path or URL defining the svn:externals property. Use SCRATCH_POOL + * for temporary allocations. + */ +static svn_error_t * +pin_externals_prop(svn_string_t **pinned_externals, + svn_string_t *externals_prop_val, + const apr_hash_t *externals_to_pin, + const char *repos_root_url, + const char *local_abspath_or_url, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *buf; + apr_array_header_t *external_items; + apr_array_header_t *parser_infos; + apr_array_header_t *items_to_pin; + int pinned_items; + int i; + apr_pool_t *iterpool; + + SVN_ERR(svn_wc__parse_externals_description(&external_items, + &parser_infos, + local_abspath_or_url, + externals_prop_val->data, + FALSE /* canonicalize_url */, + scratch_pool)); + + if (externals_to_pin) + { + items_to_pin = svn_hash_gets((apr_hash_t *)externals_to_pin, + local_abspath_or_url); + if (!items_to_pin) + { + /* No pinning at all for this path. */ + *pinned_externals = NULL; + return SVN_NO_ERROR; + } + } + else + items_to_pin = NULL; + + buf = svn_stringbuf_create_empty(scratch_pool); + iterpool = svn_pool_create(scratch_pool); + pinned_items = 0; + for (i = 0; i < external_items->nelts; i++) + { + svn_wc_external_item2_t *item; + svn_wc__externals_parser_info_t *info; + svn_opt_revision_t external_pegrev; + const char *pinned_desc; + + svn_pool_clear(iterpool); + + item = APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *); + info = APR_ARRAY_IDX(parser_infos, i, svn_wc__externals_parser_info_t *); + + if (items_to_pin) + { + int j; + svn_wc_external_item2_t *item_to_pin = NULL; + + for (j = 0; j < items_to_pin->nelts; j++) + { + svn_wc_external_item2_t *const current = + APR_ARRAY_IDX(items_to_pin, j, svn_wc_external_item2_t *); + + + if (current + && 0 == strcmp(item->url, current->url) + && 0 == strcmp(item->target_dir, current->target_dir)) + { + item_to_pin = current; + break; + } + } + + /* If this item is not in our list of external items to pin then + * simply keep the external at its original value. */ + if (!item_to_pin) + { + const char *desc; + + external_pegrev.kind = svn_opt_revision_unspecified; + SVN_ERR(make_external_description(&desc, local_abspath_or_url, + item, info, external_pegrev, + iterpool)); + svn_stringbuf_appendcstr(buf, desc); + continue; + } + } + + if (item->peg_revision.kind == svn_opt_revision_date) + { + /* Already pinned ... copy the peg date. */ + external_pegrev.kind = svn_opt_revision_date; + external_pegrev.value.date = item->peg_revision.value.date; + } + else if (item->peg_revision.kind == svn_opt_revision_number) + { + /* Already pinned ... copy the peg revision number. */ + external_pegrev.kind = svn_opt_revision_number; + external_pegrev.value.number = item->peg_revision.value.number; + } + else + { + SVN_ERR_ASSERT( + item->peg_revision.kind == svn_opt_revision_head || + item->peg_revision.kind == svn_opt_revision_unspecified); + + /* We're actually going to change the peg revision. */ + ++pinned_items; + + if (svn_path_is_url(local_abspath_or_url)) + { + const char *resolved_url; + svn_ra_session_t *external_ra_session; + svn_revnum_t latest_revnum; + + SVN_ERR(svn_wc__resolve_relative_external_url( + &resolved_url, item, repos_root_url, + local_abspath_or_url, iterpool, iterpool)); + SVN_ERR(svn_client__open_ra_session_internal(&external_ra_session, + NULL, resolved_url, + NULL, NULL, FALSE, + FALSE, ctx, + iterpool, + iterpool)); + SVN_ERR(svn_ra_get_latest_revnum(external_ra_session, + &latest_revnum, + iterpool)); + + external_pegrev.kind = svn_opt_revision_number; + external_pegrev.value.number = latest_revnum; + } + else + { + const char *external_abspath; + svn_node_kind_t external_kind; + svn_revnum_t external_checked_out_rev; + + external_abspath = svn_dirent_join(local_abspath_or_url, + item->target_dir, + iterpool); + SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, + NULL, NULL, ctx->wc_ctx, + local_abspath_or_url, + external_abspath, TRUE, + iterpool, + iterpool)); + if (external_kind == svn_node_none) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, + NULL, + _("Cannot pin external '%s' defined " + "in %s at '%s' because it is not " + "checked out in the working copy " + "at '%s'"), + item->url, SVN_PROP_EXTERNALS, + svn_dirent_local_style( + local_abspath_or_url, iterpool), + svn_dirent_local_style( + external_abspath, iterpool)); + else if (external_kind == svn_node_dir) + { + svn_boolean_t is_switched; + svn_boolean_t is_modified; + svn_revnum_t min_rev; + svn_revnum_t max_rev; + + /* Perform some sanity checks on the checked-out external. */ + + SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, + ctx->wc_ctx, + external_abspath, NULL, + iterpool)); + if (is_switched) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, + NULL, + _("Cannot pin external '%s' defined " + "in %s at '%s' because '%s' has " + "switched subtrees (switches " + "cannot be represented in %s)"), + item->url, SVN_PROP_EXTERNALS, + svn_dirent_local_style( + local_abspath_or_url, iterpool), + svn_dirent_local_style( + external_abspath, iterpool), + SVN_PROP_EXTERNALS); + + SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx, + external_abspath, TRUE, + ctx->cancel_func, + ctx->cancel_baton, + iterpool)); + if (is_modified) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, + NULL, + _("Cannot pin external '%s' defined " + "in %s at '%s' because '%s' has " + "local modifications (local " + "modifications cannot be " + "represented in %s)"), + item->url, SVN_PROP_EXTERNALS, + svn_dirent_local_style( + local_abspath_or_url, iterpool), + svn_dirent_local_style( + external_abspath, iterpool), + SVN_PROP_EXTERNALS); + + SVN_ERR(svn_wc__min_max_revisions(&min_rev, &max_rev, ctx->wc_ctx, + external_abspath, FALSE, + iterpool)); + if (min_rev != max_rev) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, + NULL, + _("Cannot pin external '%s' defined " + "in %s at '%s' because '%s' is a " + "mixed-revision working copy " + "(mixed-revisions cannot be " + "represented in %s)"), + item->url, SVN_PROP_EXTERNALS, + svn_dirent_local_style( + local_abspath_or_url, iterpool), + svn_dirent_local_style( + external_abspath, iterpool), + SVN_PROP_EXTERNALS); + external_checked_out_rev = min_rev; + } + else + { + SVN_ERR_ASSERT(external_kind == svn_node_file); + SVN_ERR(svn_wc__node_get_repos_info(&external_checked_out_rev, + NULL, NULL, NULL, + ctx->wc_ctx, external_abspath, + iterpool, iterpool)); + } + + external_pegrev.kind = svn_opt_revision_number; + external_pegrev.value.number = external_checked_out_rev; + } + } + + SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_date || + external_pegrev.kind == svn_opt_revision_number); + + SVN_ERR(make_external_description(&pinned_desc, local_abspath_or_url, + item, info, external_pegrev, iterpool)); + + svn_stringbuf_appendcstr(buf, pinned_desc); + } + svn_pool_destroy(iterpool); + + if (pinned_items > 0) + *pinned_externals = svn_string_create_from_buf(buf, result_pool); + else + *pinned_externals = NULL; + + return SVN_NO_ERROR; +} + +/* Return, in *PINNED_EXTERNALS, a new hash mapping URLs or local abspaths + * to svn:externals property values (as const char *), where some or all + * external references have been pinned. + * If EXTERNALS_TO_PIN is NULL, pin all externals, else pin the externals + * mentioned in EXTERNALS_TO_PIN. + * The pinning operation takes place as part of the copy operation for + * the source/destination pair PAIR. Use RA_SESSION and REPOS_ROOT_URL + * to contact the repository containing the externals definition, if neccesary. + * Use CX to fopen additional RA sessions to external repositories, if + * neccessary. Allocate *NEW_EXTERNALS in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +resolve_pinned_externals(apr_hash_t **pinned_externals, + const apr_hash_t *externals_to_pin, + svn_client__copy_pair_t *pair, + svn_ra_session_t *ra_session, + const char *repos_root_url, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *old_url = NULL; + apr_hash_t *externals_props; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + + *pinned_externals = apr_hash_make(result_pool); + + if (svn_path_is_url(pair->src_abspath_or_url)) + { + SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session, + pair->src_abspath_or_url, + scratch_pool)); + externals_props = apr_hash_make(scratch_pool); + SVN_ERR(svn_client__remote_propget(externals_props, NULL, + SVN_PROP_EXTERNALS, + pair->src_abspath_or_url, "", + svn_node_dir, + pair->src_revnum, + ra_session, + svn_depth_infinity, + scratch_pool, + scratch_pool)); + } + else + { + SVN_ERR(svn_wc__externals_gather_definitions(&externals_props, NULL, + ctx->wc_ctx, + pair->src_abspath_or_url, + svn_depth_infinity, + scratch_pool, scratch_pool)); + + /* ### gather_definitions returns propvals as const char * */ + for (hi = apr_hash_first(scratch_pool, externals_props); + hi; + hi = apr_hash_next(hi)) + { + const char *local_abspath_or_url = apr_hash_this_key(hi); + const char *propval = apr_hash_this_val(hi); + svn_string_t *new_propval = svn_string_create(propval, scratch_pool); + + svn_hash_sets(externals_props, local_abspath_or_url, new_propval); + } + } + + if (apr_hash_count(externals_props) == 0) + { + if (old_url) + SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool)); + return SVN_NO_ERROR; + } + + iterpool = svn_pool_create(scratch_pool); + for (hi = apr_hash_first(scratch_pool, externals_props); + hi; + hi = apr_hash_next(hi)) + { + const char *local_abspath_or_url = apr_hash_this_key(hi); + svn_string_t *externals_propval = apr_hash_this_val(hi); + const char *relpath; + svn_string_t *new_propval; + + svn_pool_clear(iterpool); + + SVN_ERR(pin_externals_prop(&new_propval, externals_propval, + externals_to_pin, + repos_root_url, local_abspath_or_url, ctx, + result_pool, iterpool)); + if (new_propval) + { + if (svn_path_is_url(pair->src_abspath_or_url)) + relpath = svn_uri_skip_ancestor(pair->src_abspath_or_url, + local_abspath_or_url, + result_pool); + else + relpath = svn_dirent_skip_ancestor(pair->src_abspath_or_url, + local_abspath_or_url); + SVN_ERR_ASSERT(relpath); + + svn_hash_sets(*pinned_externals, relpath, new_propval); + } + } + svn_pool_destroy(iterpool); + + if (old_url) + SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool)); + + 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_boolean_t metadata_only, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { @@ -195,22 +696,63 @@ do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep, const char *dst_abspath; svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *); + apr_hash_t *pinned_externals = NULL; + svn_pool_clear(iterpool); /* Check for cancellation */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); + if (pin_externals) + { + const char *repos_root_url; + + SVN_ERR(svn_wc__node_get_origin(NULL, NULL, NULL, &repos_root_url, + NULL, NULL, NULL, ctx->wc_ctx, + pair->src_abspath_or_url, FALSE, + scratch_pool, iterpool)); + SVN_ERR(resolve_pinned_externals(&pinned_externals, + externals_to_pin, pair, NULL, + repos_root_url, ctx, + iterpool, iterpool)); + } + /* 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 */, + metadata_only, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, iterpool); if (err) break; + + if (pinned_externals) + { + apr_hash_index_t *hi; + + for (hi = apr_hash_first(iterpool, pinned_externals); + hi; + hi = apr_hash_next(hi)) + { + const char *dst_relpath = apr_hash_this_key(hi); + svn_string_t *externals_propval = apr_hash_this_val(hi); + const char *local_abspath; + + local_abspath = svn_dirent_join(pair->dst_abspath_or_url, + dst_relpath, iterpool); + /* ### use a work queue? */ + SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, + SVN_PROP_EXTERNALS, externals_propval, + svn_depth_empty, TRUE /* skip_checks */, + NULL /* changelist_filter */, + ctx->cancel_func, ctx->cancel_baton, + NULL, NULL, /* no extra notification */ + iterpool)); + } + } } svn_pool_destroy(iterpool); @@ -223,6 +765,9 @@ do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep, static svn_error_t * do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep, const apr_array_header_t *copy_pairs, + svn_boolean_t metadata_only, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -236,7 +781,8 @@ do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep, SVN_WC__CALL_WITH_WRITE_LOCK( do_wc_to_wc_copies_with_write_lock(timestamp_sleep, copy_pairs, dst_parent, - ctx, pool), + metadata_only, pin_externals, + externals_to_pin, ctx, pool), ctx->wc_ctx, dst_parent_abspath, FALSE, pool); return SVN_NO_ERROR; @@ -594,6 +1140,8 @@ typedef struct path_driver_info_t svn_boolean_t resurrection; svn_boolean_t dir_add; svn_string_t *mergeinfo; /* the new mergeinfo for the target */ + svn_string_t *externals; /* new externals definitions for the target */ + svn_boolean_t only_pin_externals; } path_driver_info_t; @@ -631,7 +1179,7 @@ path_driver_cb_func(void **dir_baton, 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. */ + /* Check to see if we need to add the path as a parent directory. */ if (path_info->dir_add) { return cb_baton->editor->add_directory(path, parent_baton, NULL, @@ -662,7 +1210,7 @@ path_driver_cb_func(void **dir_baton, /* Not a move? This must just be the copy addition. */ else { - do_add = TRUE; + do_add = !path_info->only_pin_externals; } } @@ -702,6 +1250,18 @@ path_driver_cb_func(void **dir_baton, pool)); } } + + if (path_info->externals) + { + if (*dir_baton == NULL) + SVN_ERR(cb_baton->editor->open_directory(path, parent_baton, + SVN_INVALID_REVNUM, + pool, dir_baton)); + + SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS, + path_info->externals, pool)); + } + return SVN_NO_ERROR; } @@ -786,6 +1346,79 @@ find_absent_parents2(svn_ra_session_t *ra_session, return SVN_NO_ERROR; } +/* Queue property changes for pinning svn:externals properties set on + * descendants of the path corresponding to PARENT_INFO. PINNED_EXTERNALS + * is keyed by the relative path of each descendant which should have some + * or all of its externals pinned, with the corresponding pinned svn:externals + * properties as values. Property changes are queued in a new list of path + * infos *NEW_PATH_INFOS, or in an existing item of the PATH_INFOS list if an + * existing item is found for the descendant. Allocate results in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +queue_externals_change_path_infos(apr_array_header_t *new_path_infos, + apr_array_header_t *path_infos, + apr_hash_t *pinned_externals, + path_driver_info_t *parent_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, pinned_externals); + hi; + hi = apr_hash_next(hi)) + { + const char *dst_relpath = apr_hash_this_key(hi); + svn_string_t *externals_prop = apr_hash_this_val(hi); + const char *src_url; + path_driver_info_t *info; + int i; + + svn_pool_clear(iterpool); + + src_url = svn_path_url_add_component2(parent_info->src_url, + dst_relpath, iterpool); + + /* Try to find a path info the external change can be applied to. */ + info = NULL; + for (i = 0; i < path_infos->nelts; i++) + { + path_driver_info_t *existing_info; + + existing_info = APR_ARRAY_IDX(path_infos, i, path_driver_info_t *); + if (strcmp(src_url, existing_info->src_url) == 0) + { + info = existing_info; + break; + } + } + + if (info == NULL) + { + /* A copied-along child needs its externals pinned. + Create a new path info for this property change. */ + info = apr_pcalloc(result_pool, sizeof(*info)); + info->src_url = svn_path_url_add_component2( + parent_info->src_url, dst_relpath, + result_pool); + info->src_path = NULL; /* Only needed on copied dirs */ + info->dst_path = svn_relpath_join(parent_info->dst_path, + dst_relpath, + result_pool); + info->src_kind = svn_node_dir; + info->only_pin_externals = TRUE; + APR_ARRAY_PUSH(new_path_infos, path_driver_info_t *) = info; + } + + info->externals = externals_prop; + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + static svn_error_t * repos_to_repos_copy(const apr_array_header_t *copy_pairs, svn_boolean_t make_parents, @@ -794,6 +1427,8 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, void *commit_baton, svn_client_ctx_t *ctx, svn_boolean_t is_move, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, apr_pool_t *pool) { svn_error_t *err; @@ -809,6 +1444,7 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, struct path_driver_cb_baton cb_baton; apr_array_header_t *new_dirs = NULL; apr_hash_t *commit_revprops; + apr_array_header_t *pin_externals_only_infos = NULL; int i; svn_client__copy_pair_t *first_pair = APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *); @@ -1061,16 +1697,40 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, 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); + { + const char *path = svn_uri_skip_ancestor(repos_root, + pair->dst_abspath_or_url, + pool); + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '/%s' already exists"), path); + } /* More info for our INFO structure. */ - info->src_path = src_rel; + info->src_path = src_rel; /* May be NULL, if outside RA session scope */ 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 (pin_externals) + { + apr_hash_t *pinned_externals; + + SVN_ERR(resolve_pinned_externals(&pinned_externals, + externals_to_pin, pair, + ra_session, repos_root, + ctx, pool, pool)); + if (pin_externals_only_infos == NULL) + { + pin_externals_only_infos = + apr_array_make(pool, 0, sizeof(path_driver_info_t *)); + } + SVN_ERR(queue_externals_change_path_infos(pin_externals_only_infos, + path_infos, + pinned_externals, + info, pool, pool)); + } } if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) @@ -1091,6 +1751,7 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, item = svn_client_commit_item3_create(pool); item->url = svn_path_url_add_component2(top_url, relpath, pool); + item->kind = svn_node_dir; item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; } @@ -1104,14 +1765,19 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, 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; + item->kind = info->src_kind; + item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD + | SVN_CLIENT_COMMIT_ITEM_IS_COPY; + item->copyfrom_url = info->src_url; + item->copyfrom_rev = info->src_revnum; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; if (is_move && (! info->resurrection)) { - item = apr_pcalloc(pool, sizeof(*item)); + item = svn_client_commit_item3_create(pool); item->url = svn_path_url_add_component2(top_url, info->src_path, pool); + item->kind = info->src_kind; item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; } @@ -1153,6 +1819,19 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, APR_ARRAY_PUSH(paths, const char *) = info->src_path; } + /* Add any items which only need their externals pinned. */ + if (pin_externals_only_infos) + { + for (i = 0; i < pin_externals_only_infos->nelts; i++) + { + path_driver_info_t *info; + + info = APR_ARRAY_IDX(pin_externals_only_infos, i, path_driver_info_t *); + APR_ARRAY_PUSH(paths, const char *) = info->dst_path; + svn_hash_sets(action_hash, info->dst_path, info); + } + } + SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, message, ctx, pool)); @@ -1184,6 +1863,15 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs, editor->abort_edit(edit_baton, pool)); } + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(top_url, + svn_wc_notify_commit_finalizing, + pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } + /* Close the edit. */ return svn_error_trace(editor->close_edit(edit_baton, pool)); } @@ -1222,6 +1910,65 @@ check_url_kind(void *baton, return SVN_NO_ERROR; } +/* Queue a property change on a copy of LOCAL_ABSPATH to COMMIT_URL + * in the COMMIT_ITEMS list. + * If the list does not already have a commit item for COMMIT_URL + * add a new commit item for the property change. + * Allocate results in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +queue_prop_change_commit_items(const char *local_abspath, + const char *commit_url, + apr_array_header_t *commit_items, + const char *propname, + svn_string_t *propval, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_client_commit_item3_t *item = NULL; + svn_prop_t *prop; + int i; + + for (i = 0; i < commit_items->nelts; i++) + { + svn_client_commit_item3_t *existing_item; + + existing_item = APR_ARRAY_IDX(commit_items, i, + svn_client_commit_item3_t *); + if (strcmp(existing_item->url, commit_url) == 0) + { + item = existing_item; + break; + } + } + + if (item == NULL) + { + item = svn_client_commit_item3_create(result_pool); + item->path = local_abspath; + item->url = commit_url; + item->kind = svn_node_dir; + item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS; + + item->incoming_prop_changes = apr_array_make(result_pool, 1, + sizeof(svn_prop_t *)); + APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; + } + else + item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS; + + if (item->outgoing_prop_changes == NULL) + item->outgoing_prop_changes = apr_array_make(result_pool, 1, + sizeof(svn_prop_t *)); + + prop = apr_palloc(result_pool, sizeof(*prop)); + prop->name = propname; + prop->value = propval; + APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *) = prop; + + return SVN_NO_ERROR; +} + /* ### Copy ... * COMMIT_INFO_P is ... * COPY_PAIRS is ... such that each 'src_abspath_or_url' is a local abspath @@ -1235,6 +1982,8 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, void *commit_baton, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { @@ -1244,7 +1993,9 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, const char *top_src_abspath; svn_ra_session_t *ra_session; const svn_delta_editor_t *editor; +#ifdef ENABLE_EV2_SHIMS apr_hash_t *relpath_map = NULL; +#endif void *edit_baton; svn_client__committables_t *committables; apr_array_header_t *commit_items; @@ -1253,6 +2004,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, apr_hash_t *commit_revprops; svn_client__copy_pair_t *first_pair; apr_pool_t *session_pool = svn_pool_create(scratch_pool); + apr_array_header_t *commit_items_for_dav; int i; /* Find the common root of all the source paths */ @@ -1287,9 +2039,13 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, SVN_ERR(svn_dirent_get_absolute(&top_src_abspath, top_src_path, scratch_pool)); + commit_items_for_dav = apr_array_make(session_pool, 0, + sizeof(svn_client_commit_item3_t*)); + /* 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, + top_src_abspath, + commit_items_for_dav, FALSE /* write_dav_props */, TRUE /* read_dav_props */, ctx, @@ -1326,52 +2082,6 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, } } - 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; @@ -1402,6 +2112,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, item = svn_client_commit_item3_create(scratch_pool); item->url = url; + item->kind = svn_node_dir; item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; item->incoming_prop_changes = apr_array_make(scratch_pool, 1, sizeof(svn_prop_t *)); @@ -1429,8 +2140,6 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, /* 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( @@ -1447,34 +2156,97 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, 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)); + = apr_palloc(scratch_pool, sizeof(*mergeinfo_prop)); svn_string_t *prop_value; SVN_ERR(svn_mergeinfo_to_string(&prop_value, mergeinfo, - item->outgoing_prop_changes->pool)); + scratch_pool)); + + if (!item->outgoing_prop_changes) + { + item->outgoing_prop_changes = apr_array_make(scratch_pool, 1, + sizeof(svn_prop_t *)); + } mergeinfo_prop->name = SVN_PROP_MERGEINFO; mergeinfo_prop->value = prop_value; APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *) = mergeinfo_prop; } + + if (pin_externals) + { + apr_hash_t *pinned_externals; + apr_hash_index_t *hi; + + SVN_ERR(resolve_pinned_externals(&pinned_externals, + externals_to_pin, pair, + ra_session, cukb.repos_root_url, + ctx, scratch_pool, iterpool)); + for (hi = apr_hash_first(scratch_pool, pinned_externals); + hi; + hi = apr_hash_next(hi)) + { + const char *dst_relpath = apr_hash_this_key(hi); + svn_string_t *externals_propval = apr_hash_this_val(hi); + const char *dst_url; + const char *commit_url; + const char *src_abspath; + + if (svn_path_is_url(pair->dst_abspath_or_url)) + dst_url = pair->dst_abspath_or_url; + else + SVN_ERR(svn_wc__node_get_url(&dst_url, ctx->wc_ctx, + pair->dst_abspath_or_url, + scratch_pool, iterpool)); + commit_url = svn_path_url_add_component2(dst_url, dst_relpath, + scratch_pool); + src_abspath = svn_dirent_join(pair->src_abspath_or_url, + dst_relpath, iterpool); + SVN_ERR(queue_prop_change_commit_items(src_abspath, + commit_url, commit_items, + SVN_PROP_EXTERNALS, + externals_propval, + scratch_pool, iterpool)); + } + } + } + + if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) + { + const char *tmp_file; + + 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 = ""; /* Sort and condense our COMMIT_ITEMS. */ SVN_ERR(svn_client__condense_commit_items(&top_dst_url, commit_items, scratch_pool)); + /* Add the commit items to the DAV commit item list to provide access + to dav properties (for pre http-v2 DAV) */ + apr_array_cat(commit_items_for_dav, commit_items); + #ifdef ENABLE_EV2_SHIMS if (commit_items) { - relpath_map = apr_hash_make(pool); + relpath_map = apr_hash_make(scratch_pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, @@ -1485,7 +2257,8 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, continue; svn_pool_clear(iterpool); - SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL, + SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, + NULL, NULL, ctx->wc_ctx, item->path, FALSE, scratch_pool, iterpool)); if (relpath) @@ -1494,22 +2267,17 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, } #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_ra_reparent(ra_session, top_dst_url, session_pool)); SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, message, ctx, session_pool)); /* Fetch RA commit editor. */ +#ifdef ENABLE_EV2_SHIMS SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map, session_pool))); +#endif SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, commit_revprops, commit_callback, @@ -1520,7 +2288,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs, /* 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 /* notify_path_prefix */, NULL, ctx, session_pool, session_pool), _("Commit failed (details follow):")); @@ -1568,6 +2336,8 @@ 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_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool) @@ -1596,7 +2366,6 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep, { 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. */ @@ -1625,14 +2394,22 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep, ctx->notify_func2 = notification_adjust_func; ctx->notify_baton2 = &nb; - err = svn_client__checkout_internal(&pair->src_revnum, + /* Avoid a chicken-and-egg problem: + * If pinning externals we'll need to adjust externals + * properties before checking out any externals. + * But copy needs to happen before pinning because else there + * are no svn:externals properties to pin. */ + if (pin_externals) + ignore_externals = TRUE; + + err = svn_client__checkout_internal(&pair->src_revnum, timestamp_sleep, pair->src_original, tmp_abspath, &pair->src_peg_revision, &pair->src_op_revision, svn_depth_infinity, ignore_externals, FALSE, - &sleep_needed, ctx, pool); + ra_session, ctx, pool); ctx->notify_func2 = old_notify_func2; ctx->notify_baton2 = old_notify_baton2; @@ -1677,6 +2454,61 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep, return SVN_NO_ERROR; } + + if (pin_externals) + { + apr_hash_t *pinned_externals; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + const char *repos_root_url; + apr_hash_t *new_externals; + apr_hash_t *new_depths; + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); + SVN_ERR(resolve_pinned_externals(&pinned_externals, + externals_to_pin, pair, + ra_session, repos_root_url, + ctx, pool, pool)); + + iterpool = svn_pool_create(pool); + for (hi = apr_hash_first(pool, pinned_externals); + hi; + hi = apr_hash_next(hi)) + { + const char *dst_relpath = apr_hash_this_key(hi); + svn_string_t *externals_propval = apr_hash_this_val(hi); + const char *local_abspath; + + svn_pool_clear(iterpool); + + local_abspath = svn_dirent_join(pair->dst_abspath_or_url, + dst_relpath, iterpool); + /* ### use a work queue? */ + SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, + SVN_PROP_EXTERNALS, externals_propval, + svn_depth_empty, TRUE /* skip_checks */, + NULL /* changelist_filter */, + ctx->cancel_func, ctx->cancel_baton, + NULL, NULL, /* no extra notification */ + iterpool)); + } + + /* Now update all externals in the newly created copy. */ + SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, + &new_depths, + ctx->wc_ctx, + dst_abspath, + svn_depth_infinity, + iterpool, iterpool)); + SVN_ERR(svn_client__handle_externals(new_externals, + new_depths, + repos_root_url, dst_abspath, + svn_depth_infinity, + timestamp_sleep, + ra_session, + ctx, iterpool)); + svn_pool_destroy(iterpool); + } } /* end directory case */ else if (pair->src_kind == svn_node_file) @@ -1725,7 +2557,7 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep, 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); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } return SVN_NO_ERROR; @@ -1736,6 +2568,8 @@ 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_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) @@ -1801,6 +2635,7 @@ repos_to_wc_copy_locked(svn_boolean_t *timestamp_sleep, svn_client__copy_pair_t *), same_repositories, ignore_externals, + pin_externals, externals_to_pin, ra_session, ctx, iterpool)); } svn_pool_destroy(iterpool); @@ -1813,6 +2648,8 @@ 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_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -1921,6 +2758,7 @@ repos_to_wc_copy(svn_boolean_t *timestamp_sleep, SVN_WC__CALL_WITH_WRITE_LOCK( repos_to_wc_copy_locked(timestamp_sleep, copy_pairs, top_dst_path, ignore_externals, + pin_externals, externals_to_pin, ra_session, ctx, pool), ctx->wc_ctx, lock_abspath, FALSE, pool); return SVN_NO_ERROR; @@ -1947,6 +2785,8 @@ try_copy(svn_boolean_t *timestamp_sleep, svn_boolean_t metadata_only, svn_boolean_t make_parents, svn_boolean_t ignore_externals, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, void *commit_baton, @@ -1959,6 +2799,9 @@ try_copy(svn_boolean_t *timestamp_sleep, svn_boolean_t srcs_are_urls, dst_is_url; int i; + /* Assert instead of crashing if the sources list is empty. */ + SVN_ERR_ASSERT(sources->nelts > 0); + /* 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. */ @@ -1979,7 +2822,7 @@ try_copy(svn_boolean_t *timestamp_sleep, { 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)); + svn_client__copy_pair_t *pair = apr_pcalloc(pool, sizeof(*pair)); const char *src_basename; svn_boolean_t src_is_url = svn_path_is_url(source->path); @@ -2001,6 +2844,7 @@ try_copy(svn_boolean_t *timestamp_sleep, pair->src_op_revision = *source->revision; pair->src_peg_revision = *source->peg_revision; + pair->src_kind = svn_node_unknown; SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision, &pair->src_op_revision, @@ -2029,7 +2873,7 @@ try_copy(svn_boolean_t *timestamp_sleep, else { /* Only one source path. */ - svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair)); + svn_client__copy_pair_t *pair = apr_pcalloc(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); @@ -2041,6 +2885,7 @@ try_copy(svn_boolean_t *timestamp_sleep, source->path, pool)); pair->src_op_revision = *source->revision; pair->src_peg_revision = *source->peg_revision; + pair->src_kind = svn_node_unknown; SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision, &pair->src_op_revision, @@ -2187,7 +3032,7 @@ try_copy(svn_boolean_t *timestamp_sleep, SVN_ERR(svn_wc__node_get_origin(NULL, ©from_rev, ©from_repos_relpath, ©from_repos_root_url, - NULL, NULL, + NULL, NULL, NULL, ctx->wc_ctx, pair->src_abspath_or_url, TRUE, iterpool, iterpool)); @@ -2242,30 +3087,35 @@ try_copy(svn_boolean_t *timestamp_sleep, else { /* We ignore these values, so assert the default value */ - SVN_ERR_ASSERT(allow_mixed_revisions && !metadata_only); + SVN_ERR_ASSERT(allow_mixed_revisions); return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep, - copy_pairs, ctx, pool)); + copy_pairs, + metadata_only, + pin_externals, + externals_to_pin, + 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)); + commit_callback, commit_baton, + pin_externals, externals_to_pin, 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)); + pin_externals, externals_to_pin, 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)); + pin_externals, externals_to_pin, pool)); } } @@ -2273,11 +3123,14 @@ try_copy(svn_boolean_t *timestamp_sleep, /* Public Interfaces */ svn_error_t * -svn_client_copy6(const apr_array_header_t *sources, +svn_client_copy7(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, + svn_boolean_t metadata_only, + svn_boolean_t pin_externals, + const apr_hash_t *externals_to_pin, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, void *commit_baton, @@ -2296,9 +3149,11 @@ svn_client_copy6(const apr_array_header_t *sources, sources, dst_path, FALSE /* is_move */, TRUE /* allow_mixed_revisions */, - FALSE /* metadata_only */, + metadata_only, make_parents, ignore_externals, + pin_externals, + externals_to_pin, revprop_table, commit_callback, commit_baton, ctx, @@ -2330,9 +3185,11 @@ svn_client_copy6(const apr_array_header_t *sources, sources, dst_path, FALSE /* is_move */, TRUE /* allow_mixed_revisions */, - FALSE /* metadata_only */, + metadata_only, make_parents, ignore_externals, + pin_externals, + externals_to_pin, revprop_table, commit_callback, commit_baton, ctx, @@ -2394,6 +3251,8 @@ svn_client_move7(const apr_array_header_t *src_paths, metadata_only, make_parents, FALSE /* ignore_externals */, + FALSE /* pin_externals */, + NULL /* externals_to_pin */, revprop_table, commit_callback, commit_baton, ctx, @@ -2427,6 +3286,8 @@ svn_client_move7(const apr_array_header_t *src_paths, metadata_only, make_parents, FALSE /* ignore_externals */, + FALSE /* pin_externals */, + NULL /* externals_to_pin */, revprop_table, commit_callback, commit_baton, ctx, diff --git a/contrib/subversion/subversion/libsvn_client/copy_foreign.c b/contrib/subversion/subversion/libsvn_client/copy_foreign.c index 8de8a5d6a..cfe6aea05 100644 --- a/contrib/subversion/subversion/libsvn_client/copy_foreign.c +++ b/contrib/subversion/subversion/libsvn_client/copy_foreign.c @@ -160,7 +160,7 @@ dir_change_prop(void *dir_baton, if (! db->created) { /* We can still store them in the hash for immediate addition - with the svn_wc_add_from_disk2() call */ + with the svn_wc_add_from_disk3() call */ if (! db->properties) db->properties = apr_hash_make(db->pool); @@ -173,7 +173,7 @@ dir_change_prop(void *dir_baton, /* We have already notified for this directory, so don't do that again */ SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value, svn_depth_empty, FALSE, NULL, - NULL, NULL, /* Cancelation */ + NULL, NULL, /* Cancellation */ NULL, NULL, /* Notification */ scratch_pool)); } @@ -213,9 +213,10 @@ ensure_added(struct dir_baton_t *db, db->created = TRUE; /* Add the directory with all the already collected properties */ - SVN_ERR(svn_wc_add_from_disk2(db->eb->wc_ctx, + SVN_ERR(svn_wc_add_from_disk3(db->eb->wc_ctx, db->local_abspath, db->properties, + TRUE /* skip checks */, db->eb->notify_func, db->eb->notify_baton, scratch_pool)); @@ -306,7 +307,7 @@ file_change_prop(void *file_baton, } /* We store all properties in the hash for immediate addition - with the svn_wc_add_from_disk2() call */ + with the svn_wc_add_from_disk3() call */ if (! fb->properties) fb->properties = apr_hash_make(fb->pool); @@ -375,7 +376,8 @@ file_close(void *file_baton, fb->pool))); } - SVN_ERR(svn_wc_add_from_disk2(eb->wc_ctx, fb->local_abspath, fb->properties, + SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties, + TRUE /* skip checks */, eb->notify_func, eb->notify_baton, fb->pool)); @@ -526,7 +528,7 @@ svn_client__copy_foreign(const char *url, for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); if (svn_property_kind2(name) != svn_prop_regular_kind || ! strcmp(name, SVN_PROP_MERGEINFO)) @@ -538,12 +540,14 @@ svn_client__copy_foreign(const char *url, if (!already_locked) SVN_WC__CALL_WITH_WRITE_LOCK( - svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props, + svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props, + TRUE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, scratch_pool), ctx->wc_ctx, dir_abspath, FALSE, scratch_pool); else - SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props, + TRUE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } diff --git a/contrib/subversion/subversion/libsvn_client/ctx.c b/contrib/subversion/subversion/libsvn_client/ctx.c index 185b91e8c..05a5eec2d 100644 --- a/contrib/subversion/subversion/libsvn_client/ctx.c +++ b/contrib/subversion/subversion/libsvn_client/ctx.c @@ -27,6 +27,7 @@ /*** Includes. ***/ +#include #include #include "svn_hash.h" #include "svn_client.h" @@ -34,6 +35,8 @@ #include "private/svn_wc_private.h" +#include "client.h" + /*** Code. ***/ @@ -76,6 +79,20 @@ call_conflict_func(svn_wc_conflict_result_t **result, return SVN_NO_ERROR; } +/* The magic number in client_ctx_t.magic_id. */ +#define CLIENT_CTX_MAGIC APR_UINT64_C(0xDEADBEEF600DF00D) + +svn_client__private_ctx_t * +svn_client__get_private_ctx(svn_client_ctx_t *ctx) +{ + svn_client__private_ctx_t *const private_ctx = + (void*)((char *)ctx - offsetof(svn_client__private_ctx_t, public_ctx)); + SVN_ERR_ASSERT_NO_RETURN(&private_ctx->public_ctx == ctx); + SVN_ERR_ASSERT_NO_RETURN(0 == private_ctx->magic_null); + SVN_ERR_ASSERT_NO_RETURN(CLIENT_CTX_MAGIC == private_ctx->magic_id); + return private_ctx; +} + svn_error_t * svn_client_create_context2(svn_client_ctx_t **ctx, apr_hash_t *cfg_hash, @@ -83,23 +100,29 @@ svn_client_create_context2(svn_client_ctx_t **ctx, { svn_config_t *cfg_config; - *ctx = apr_pcalloc(pool, sizeof(svn_client_ctx_t)); + svn_client__private_ctx_t *const private_ctx = + apr_pcalloc(pool, sizeof(*private_ctx)); + svn_client_ctx_t *const public_ctx = &private_ctx->public_ctx; + + private_ctx->magic_null = 0; + private_ctx->magic_id = CLIENT_CTX_MAGIC; - (*ctx)->notify_func2 = call_notify_func; - (*ctx)->notify_baton2 = *ctx; + public_ctx->notify_func2 = call_notify_func; + public_ctx->notify_baton2 = public_ctx; - (*ctx)->conflict_func2 = call_conflict_func; - (*ctx)->conflict_baton2 = *ctx; + public_ctx->conflict_func2 = call_conflict_func; + public_ctx->conflict_baton2 = public_ctx; - (*ctx)->config = cfg_hash; + public_ctx->config = cfg_hash; if (cfg_hash) cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG); else cfg_config = NULL; - SVN_ERR(svn_wc_context_create(&(*ctx)->wc_ctx, cfg_config, pool, - pool)); + SVN_ERR(svn_wc_context_create(&public_ctx->wc_ctx, cfg_config, + pool, pool)); + *ctx = public_ctx; return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/delete.c b/contrib/subversion/subversion/libsvn_client/delete.c index 803b70c1f..943cdd9a4 100644 --- a/contrib/subversion/subversion/libsvn_client/delete.c +++ b/contrib/subversion/subversion/libsvn_client/delete.c @@ -258,6 +258,15 @@ single_repos_delete(svn_ra_session_t *ra_session, editor->abort_edit(edit_baton, pool))); } + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(base_uri, + svn_wc_notify_commit_finalizing, + pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } + /* Close the edit. */ return svn_error_trace(editor->close_edit(edit_baton, pool)); } @@ -296,7 +305,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris, for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi)) { - const char *repos_root = svn__apr_hash_index_key(hi); + const char *repos_root = apr_hash_this_key(hi); repos_relpath = svn_uri_skip_ancestor(repos_root, uri, pool); if (repos_relpath) @@ -304,7 +313,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris, /* Great! We've found another URI underneath this session. We'll pick out the related RA session for use later, store the new target, and move on. */ - repos_deletables = svn__apr_hash_index_val(hi); + repos_deletables = apr_hash_this_val(hi); APR_ARRAY_PUSH(repos_deletables->target_uris, const char *) = apr_pstrdup(pool, uri); break; @@ -346,14 +355,14 @@ delete_urls_multi_repos(const apr_array_header_t *uris, RA error code otherwise for 1.6 compatibility.) */ if (!repos_relpath || !*repos_relpath) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - "URL '%s' not within a repository", uri); + _("URL '%s' not within a repository"), uri); /* Now, test to see if the thing actually exists in HEAD. */ SVN_ERR(svn_ra_check_path(repos_deletables->ra_session, repos_relpath, SVN_INVALID_REVNUM, &kind, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, - "URL '%s' does not exist", uri); + _("URL '%s' does not exist"), uri); } /* Now we iterate over the DELETABLES hash, issuing a commit for @@ -361,7 +370,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris, iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi)) { - struct repos_deletables_t *repos_deletables = svn__apr_hash_index_val(hi); + struct repos_deletables_t *repos_deletables = apr_hash_this_val(hi); const char *base_uri; apr_array_header_t *target_relpaths; @@ -573,7 +582,7 @@ svn_client_delete4(const apr_array_header_t *paths, for (hi = apr_hash_first(pool, wcroots); hi; hi = apr_hash_next(hi)) { const char *root_abspath; - const apr_array_header_t *targets = svn__apr_hash_index_val(hi); + const apr_array_header_t *targets = apr_hash_this_val(hi); svn_pool_clear(iterpool); diff --git a/contrib/subversion/subversion/libsvn_client/deprecated.c b/contrib/subversion/subversion/libsvn_client/deprecated.c index a67a69bb9..b1760a4e3 100644 --- a/contrib/subversion/subversion/libsvn_client/deprecated.c +++ b/contrib/subversion/subversion/libsvn_client/deprecated.c @@ -626,6 +626,28 @@ svn_client_commit(svn_client_commit_info_t **commit_info_p, } /*** From copy.c ***/ +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) +{ + return svn_error_trace(svn_client_copy7(sources, dst_path, copy_as_child, + make_parents, ignore_externals, + FALSE /* metadata_only */, + FALSE /* pin_externals */, + NULL /* externals_to_pin */, + revprop_table, + commit_callback, commit_baton, + ctx, pool)); +} + svn_error_t * svn_client_copy5(svn_commit_info_t **commit_info_p, const apr_array_header_t *sources, @@ -1498,6 +1520,7 @@ svn_client_ls(apr_hash_t **dirents, } /*** From log.c ***/ + svn_error_t * svn_client_log4(const apr_array_header_t *targets, const svn_opt_revision_t *peg_revision, @@ -1970,8 +1993,8 @@ svn_client_propget3(apr_hash_t **props, for (hi = apr_hash_first(pool, temp_props); hi; hi = apr_hash_next(hi)) { - const char *abspath = svn__apr_hash_index_key(hi); - svn_string_t *value = svn__apr_hash_index_val(hi); + const char *abspath = apr_hash_this_key(hi); + svn_string_t *value = apr_hash_this_val(hi); const char *relpath = svn_dirent_join(path_or_url, svn_dirent_skip_ancestor(target, abspath), pool); @@ -2031,9 +2054,9 @@ string_hash_dup(apr_hash_t *hash, apr_pool_t *pool) for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi)) { - const char *key = apr_pstrdup(pool, svn__apr_hash_index_key(hi)); - apr_ssize_t klen = svn__apr_hash_index_klen(hi); - svn_string_t *val = svn_string_dup(svn__apr_hash_index_val(hi), pool); + const char *key = apr_pstrdup(pool, apr_hash_this_key(hi)); + apr_ssize_t klen = apr_hash_this_key_len(hi); + svn_string_t *val = svn_string_dup(apr_hash_this_val(hi), pool); apr_hash_set(new_hash, key, klen, val); } @@ -2185,6 +2208,28 @@ svn_client_proplist(apr_array_header_t **props, /*** From status.c ***/ +svn_error_t * +svn_client_status5(svn_revnum_t *result_rev, + svn_client_ctx_t *ctx, + const char *path, + const svn_opt_revision_t *revision, + svn_depth_t depth, + svn_boolean_t get_all, + svn_boolean_t update, + svn_boolean_t no_ignore, + svn_boolean_t ignore_externals, + svn_boolean_t depth_as_sticky, + const apr_array_header_t *changelists, + svn_client_status_func_t status_func, + void *status_baton, + apr_pool_t *scratch_pool) +{ + return svn_client_status6(result_rev, ctx, path, revision, depth, + get_all, update, TRUE, no_ignore, + ignore_externals, depth_as_sticky, changelists, + status_func, status_baton, scratch_pool); +} + struct status4_wrapper_baton { svn_wc_context_t *wc_ctx; @@ -2438,6 +2483,21 @@ svn_client_switch(svn_revnum_t *result_rev, } /*** From cat.c ***/ +svn_error_t * +svn_client_cat2(svn_stream_t *out, + const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_client_cat3(NULL /* props */, + out, path_or_url, peg_revision, revision, + TRUE /* expand_keywords */, + ctx, pool, pool); +} + + svn_error_t * svn_client_cat(svn_stream_t *out, const char *path_or_url, @@ -2487,6 +2547,32 @@ svn_client_checkout(svn_revnum_t *result_rev, /*** From info.c ***/ +svn_error_t * +svn_client_info3(const char *abspath_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + svn_boolean_t fetch_excluded, + svn_boolean_t fetch_actual_only, + const apr_array_header_t *changelists, + svn_client_info_receiver2_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_error_trace( + svn_client_info4(abspath_or_url, + peg_revision, + revision, + depth, + fetch_excluded, + fetch_actual_only, + FALSE /* include_externals */, + changelists, + receiver, receiver_baton, + ctx, pool)); +} + svn_info_t * svn_info_dup(const svn_info_t *info, apr_pool_t *pool) { @@ -2746,6 +2832,22 @@ svn_client_resolved(const char *path, svn_wc_conflict_choose_merged, ctx, pool); } /*** From revert.c ***/ +svn_error_t * +svn_client_revert2(const apr_array_header_t *paths, + svn_depth_t depth, + const apr_array_header_t *changelists, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_error_trace(svn_client_revert3(paths, + depth, + changelists, + FALSE /* clear_changelists */, + FALSE /* metadata_only */, + ctx, + pool)); +} + svn_error_t * svn_client_revert(const apr_array_header_t *paths, svn_boolean_t recursive, @@ -2783,7 +2885,7 @@ svn_client_uuid_from_url(const char **uuid, /* destroy the RA session */ svn_pool_destroy(subpool); - return svn_error_trace(err);; + return svn_error_trace(err); } svn_error_t * @@ -2964,3 +3066,24 @@ svn_client_commit_item2_dup(const svn_client_commit_item2_t *item, return new_item; } +svn_error_t * +svn_client_cleanup(const char *path, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *local_abspath; + + 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)); + + return svn_error_trace(svn_client_cleanup2(local_abspath, + TRUE /* break_locks */, + TRUE /* fix_recorded_timestamps */, + TRUE /* clear_dav_cache */, + TRUE /* vacuum_pristines */, + FALSE /* include_externals */, + ctx, scratch_pool)); +} diff --git a/contrib/subversion/subversion/libsvn_client/diff.c b/contrib/subversion/subversion/libsvn_client/diff.c index 26890aec6..4817ffdf1 100644 --- a/contrib/subversion/subversion/libsvn_client/diff.c +++ b/contrib/subversion/subversion/libsvn_client/diff.c @@ -52,20 +52,22 @@ #include "private/svn_diff_private.h" #include "private/svn_subr_private.h" #include "private/svn_io_private.h" +#include "private/svn_ra_private.h" #include "svn_private_config.h" /* Utilities */ +#define DIFF_REVNUM_NONEXISTENT ((svn_revnum_t) -100) #define MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir) \ svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL, \ _("Path '%s' must be an immediate child of " \ "the directory '%s'"), path, relative_to_dir) -/* Calculate the repository relative path of DIFF_RELPATH, using RA_SESSION - * and WC_CTX, and return the result in *REPOS_RELPATH. +/* Calculate the repository relative path of DIFF_RELPATH, using + * SESSION_RELPATH and WC_CTX, and return the result in *REPOS_RELPATH. * ORIG_TARGET is the related original target passed to the diff command, * and may be used to derive leading path components missing from PATH. * ANCHOR is the local path where the diff editor is anchored. @@ -74,16 +76,15 @@ static svn_error_t * make_repos_relpath(const char **repos_relpath, const char *diff_relpath, const char *orig_target, - svn_ra_session_t *ra_session, + const char *session_relpath, svn_wc_context_t *wc_ctx, const char *anchor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *local_abspath; - const char *orig_repos_relpath = NULL; - if (! ra_session + if (! session_relpath || (anchor && !svn_path_is_url(orig_target))) { svn_error_t *err; @@ -98,7 +99,7 @@ make_repos_relpath(const char **repos_relpath, wc_ctx, local_abspath, result_pool, scratch_pool); - if (!ra_session + if (!session_relpath || ! err || (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)) { @@ -113,23 +114,8 @@ make_repos_relpath(const char **repos_relpath, svn_error_clear(err); } - { - const char *url; - const char *repos_root_url; - - /* Would be nice if the RA layer could just provide the parent - repos_relpath of the ra session */ - SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool)); - - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, - scratch_pool)); - - orig_repos_relpath = svn_uri_skip_ancestor(repos_root_url, url, - scratch_pool); - - *repos_relpath = svn_relpath_join(orig_repos_relpath, diff_relpath, - result_pool); - } + *repos_relpath = svn_relpath_join(session_relpath, diff_relpath, + result_pool); return SVN_NO_ERROR; } @@ -169,9 +155,6 @@ adjust_paths_for_diff_labels(const char **index_path, new_path = "."; else return MAKE_ERR_BAD_RELATIVE_PATH(new_path, relative_to_dir); - - child_path = svn_dirent_is_child(relative_to_dir, new_path1, - result_pool); } { @@ -179,7 +162,7 @@ adjust_paths_for_diff_labels(const char **index_path, svn_boolean_t is_url1; svn_boolean_t is_url2; /* ### Holy cow. Due to anchor/target weirdness, we can't - simply join diff_cmd_baton->orig_path_1 with path, ditto for + simply join dwi->orig_path_1 with path, ditto for orig_path_2. That will work when they're directory URLs, but not for file URLs. Nor can we just use anchor1 and anchor2 from do_diff(), at least not without some more logic here. @@ -230,7 +213,7 @@ adjust_paths_for_diff_labels(const char **index_path, if (new_path2[0] == '\0') new_path2 = new_path; else if (svn_path_is_url(new_path2)) - new_path1 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2); + new_path2 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2); else if (new_path2[0] == '/') new_path2 = apr_psprintf(result_pool, "%s\t(...%s)", new_path, new_path2); else @@ -254,9 +237,11 @@ diff_label(const char *path, apr_pool_t *pool) { const char *label; - if (revnum != SVN_INVALID_REVNUM) + if (revnum >= 0) label = apr_psprintf(pool, _("%s\t(revision %ld)"), path, revnum); - else + else if (revnum == DIFF_REVNUM_NONEXISTENT) + label = apr_psprintf(pool, _("%s\t(nonexistent)"), path); + else /* SVN_INVALID_REVNUM */ label = apr_psprintf(pool, _("%s\t(working copy)"), path); return label; @@ -452,7 +437,9 @@ display_prop_diffs(const apr_array_header_t *propchanges, const char *relative_to_dir, svn_boolean_t show_diff_header, svn_boolean_t use_git_diff_format, - svn_ra_session_t *ra_session, + const char *ra_session_relpath, + svn_cancel_func_t cancel_func, + void *cancel_baton, svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { @@ -465,10 +452,10 @@ display_prop_diffs(const apr_array_header_t *propchanges, if (use_git_diff_format) { SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, orig_path1, - ra_session, wc_ctx, anchor, + ra_session_relpath, wc_ctx, anchor, scratch_pool, scratch_pool)); SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, orig_path2, - ra_session, wc_ctx, anchor, + ra_session_relpath, wc_ctx, anchor, scratch_pool, scratch_pool)); } @@ -521,7 +508,9 @@ display_prop_diffs(const apr_array_header_t *propchanges, SVN_ERR(svn_diff__display_prop_diffs( outstream, encoding, propchanges, original_props, - TRUE /* pretty_print_mergeinfo */, scratch_pool)); + TRUE /* pretty_print_mergeinfo */, + -1 /* context_size */, + cancel_func, cancel_baton, scratch_pool)); return SVN_NO_ERROR; } @@ -530,9 +519,27 @@ display_prop_diffs(const apr_array_header_t *propchanges, /*** Callbacks for 'svn diff', invoked by the repos-diff editor. ***/ +/* State provided by the diff drivers; used by the diff writer */ +typedef struct diff_driver_info_t +{ + /* The anchor to prefix before wc paths */ + const char *anchor; -struct diff_cmd_baton { + /* Relative path of ra session from repos_root_url */ + const char *session_relpath; + /* The original targets passed to the diff command. We may need + these to construct distinctive diff labels when comparing the + same relative path in the same revision, under different anchors + (for example, when comparing a trunk against a branch). */ + const char *orig_path_1; + const char *orig_path_2; +} diff_driver_info_t; + + +/* Diff writer state */ +typedef struct diff_writer_info_t +{ /* If non-null, the external diff command to invoke. */ const char *diff_cmd; @@ -556,24 +563,6 @@ struct diff_cmd_baton { const char *header_encoding; - /* The original targets passed to the diff command. We may need - these to construct distinctive diff labels when comparing the - same relative path in the same revision, under different anchors - (for example, when comparing a trunk against a branch). */ - const char *orig_path_1; - const char *orig_path_2; - - /* These are the numeric representations of the revisions passed to - svn_client_diff6(), either may be SVN_INVALID_REVNUM. We need these - because some of the svn_wc_diff_callbacks4_t don't get revision - arguments. - - ### Perhaps we should change the callback signatures and eliminate - ### these? - */ - svn_revnum_t revnum1; - svn_revnum_t revnum2; - /* Set this if you want diff output even for binary files. */ svn_boolean_t force_binary; @@ -597,22 +586,18 @@ struct diff_cmd_baton { svn_boolean_t no_diff_deleted; /* Whether to ignore copyfrom information when showing adds */ - svn_boolean_t no_copyfrom_on_add; + svn_boolean_t show_copies_as_adds; /* Empty files for creating diffs or NULL if not used yet */ const char *empty_file; svn_wc_context_t *wc_ctx; - /* The RA session used during diffs involving the repository. */ - svn_ra_session_t *ra_session; - - /* The anchor to prefix before wc paths */ - const char *anchor; + svn_cancel_func_t cancel_func; + void *cancel_baton; - /* Whether the local diff target of a repos->wc diff is a copy. */ - svn_boolean_t repos_wc_diff_target_is_copy; -}; + struct diff_driver_info_t ddi; +} diff_writer_info_t; /* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added */ @@ -620,17 +605,16 @@ static svn_error_t * diff_props_changed(const char *diff_relpath, svn_revnum_t rev1, svn_revnum_t rev2, - svn_boolean_t dir_was_added, const apr_array_header_t *propchanges, apr_hash_t *original_props, svn_boolean_t show_diff_header, - struct diff_cmd_baton *diff_cmd_baton, + diff_writer_info_t *dwi, apr_pool_t *scratch_pool) { apr_array_header_t *props; /* If property differences are ignored, there's nothing to do. */ - if (diff_cmd_baton->ignore_properties) + if (dwi->ignore_properties) return SVN_NO_ERROR; SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, @@ -638,58 +622,31 @@ diff_props_changed(const char *diff_relpath, if (props->nelts > 0) { - /* We're using the revnums from the diff_cmd_baton since there's + /* We're using the revnums from the dwi since there's * no revision argument to the svn_wc_diff_callback_t * dir_props_changed(). */ SVN_ERR(display_prop_diffs(props, original_props, diff_relpath, - diff_cmd_baton->anchor, - diff_cmd_baton->orig_path_1, - diff_cmd_baton->orig_path_2, + dwi->ddi.anchor, + dwi->ddi.orig_path_1, + dwi->ddi.orig_path_2, rev1, rev2, - diff_cmd_baton->header_encoding, - diff_cmd_baton->outstream, - diff_cmd_baton->relative_to_dir, + dwi->header_encoding, + dwi->outstream, + dwi->relative_to_dir, show_diff_header, - diff_cmd_baton->use_git_diff_format, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, + dwi->use_git_diff_format, + dwi->ddi.session_relpath, + dwi->cancel_func, + dwi->cancel_baton, + dwi->wc_ctx, scratch_pool)); } return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_dir_props_changed(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - svn_boolean_t dir_was_added, - const apr_array_header_t *propchanges, - apr_hash_t *original_props, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - struct diff_cmd_baton *diff_cmd_baton = diff_baton; - - return svn_error_trace(diff_props_changed(diff_relpath, - /* ### These revs be filled - * ### with per node info */ - dir_was_added - ? 0 /* Magic legacy value */ - : diff_cmd_baton->revnum1, - diff_cmd_baton->revnum2, - dir_was_added, - propchanges, - original_props, - TRUE /* show_diff_header */, - diff_cmd_baton, - scratch_pool)); -} - - /* Show differences between TMPFILE1 and TMPFILE2. DIFF_RELPATH, REV1, and REV2 are used in the headers to indicate the file and revisions. If either MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff, @@ -711,26 +668,24 @@ diff_content_changed(svn_boolean_t *wrote_header, svn_boolean_t force_diff, const char *copyfrom_path, svn_revnum_t copyfrom_rev, - struct diff_cmd_baton *diff_cmd_baton, + diff_writer_info_t *dwi, apr_pool_t *scratch_pool) { - int exitcode; - const char *rel_to_dir = diff_cmd_baton->relative_to_dir; - svn_stream_t *errstream = diff_cmd_baton->errstream; - svn_stream_t *outstream = diff_cmd_baton->outstream; + const char *rel_to_dir = dwi->relative_to_dir; + svn_stream_t *outstream = dwi->outstream; const char *label1, *label2; svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE; const char *index_path = diff_relpath; - const char *path1 = diff_cmd_baton->orig_path_1; - const char *path2 = diff_cmd_baton->orig_path_2; + const char *path1 = dwi->ddi.orig_path_1; + const char *path2 = dwi->ddi.orig_path_2; /* If only property differences are shown, there's nothing to do. */ - if (diff_cmd_baton->properties_only) + if (dwi->properties_only) return SVN_NO_ERROR; /* Generate the diff headers. */ SVN_ERR(adjust_paths_for_diff_labels(&index_path, &path1, &path2, - rel_to_dir, diff_cmd_baton->anchor, + rel_to_dir, dwi->ddi.anchor, scratch_pool, scratch_pool)); label1 = diff_label(path1, rev1, scratch_pool); @@ -744,42 +699,82 @@ diff_content_changed(svn_boolean_t *wrote_header, if (mimetype2) mt2_binary = svn_mime_type_is_binary(mimetype2); - if (! diff_cmd_baton->force_binary && (mt1_binary || mt2_binary)) + if (! dwi->force_binary && (mt1_binary || mt2_binary)) { /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); /* ### Print git diff headers. */ - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - _("Cannot display: file marked as a binary type.%s"), - APR_EOL_STR)); - - if (mt1_binary && !mt2_binary) - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, mimetype1)); - else if (mt2_binary && !mt1_binary) - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, mimetype2)); - else if (mt1_binary && mt2_binary) + if (dwi->use_git_diff_format) + { + svn_stream_t *left_stream; + svn_stream_t *right_stream; + const char *repos_relpath1; + const char *repos_relpath2; + + SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, + dwi->ddi.orig_path_1, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, + scratch_pool, scratch_pool)); + SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, + dwi->ddi.orig_path_2, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, + scratch_pool, scratch_pool)); + SVN_ERR(print_git_diff_header(outstream, &label1, &label2, + operation, + repos_relpath1, repos_relpath2, + rev1, rev2, + copyfrom_path, + copyfrom_rev, + dwi->header_encoding, + scratch_pool)); + + SVN_ERR(svn_stream_open_readonly(&left_stream, tmpfile1, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&right_stream, tmpfile2, + scratch_pool, scratch_pool)); + SVN_ERR(svn_diff_output_binary(outstream, + left_stream, right_stream, + dwi->cancel_func, dwi->cancel_baton, + scratch_pool)); + } + else { - if (strcmp(mimetype1, mimetype2) == 0) + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + _("Cannot display: file marked as a binary type.%s"), + APR_EOL_STR)); + + if (mt1_binary && !mt2_binary) SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, - mimetype1)); - else + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, mimetype1)); + else if (mt2_binary && !mt1_binary) SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = (%s, %s)" APR_EOL_STR, - mimetype1, mimetype2)); + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, mimetype2)); + else if (mt1_binary && mt2_binary) + { + if (strcmp(mimetype1, mimetype2) == 0) + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, + mimetype1)); + else + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + "svn:mime-type = (%s, %s)" APR_EOL_STR, + mimetype1, mimetype2)); + } } /* Exit early. */ @@ -787,17 +782,19 @@ diff_content_changed(svn_boolean_t *wrote_header, } - if (diff_cmd_baton->diff_cmd) + if (dwi->diff_cmd) { + svn_stream_t *errstream = dwi->errstream; apr_file_t *outfile; apr_file_t *errfile; const char *outfilename; const char *errfilename; svn_stream_t *stream; + int exitcode; /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); @@ -827,12 +824,12 @@ diff_content_changed(svn_boolean_t *wrote_header, scratch_pool, scratch_pool)); SVN_ERR(svn_io_run_diff2(".", - diff_cmd_baton->options.for_external.argv, - diff_cmd_baton->options.for_external.argc, + dwi->options.for_external.argv, + dwi->options.for_external.argc, label1, label2, tmpfile1, tmpfile2, &exitcode, outfile, errfile, - diff_cmd_baton->diff_cmd, scratch_pool)); + dwi->diff_cmd, scratch_pool)); /* Now, open and copy our files to our output streams. */ if (outfilename) @@ -854,43 +851,44 @@ diff_content_changed(svn_boolean_t *wrote_header, NULL, NULL, scratch_pool)); } - /* We have a printed a diff for this path, mark it as visited. */ - *wrote_header = TRUE; + /* If we have printed a diff for this path, mark it as visited. */ + if (exitcode == 1) + *wrote_header = TRUE; } else /* use libsvn_diff to generate the diff */ { svn_diff_t *diff; SVN_ERR(svn_diff_file_diff_2(&diff, tmpfile1, tmpfile2, - diff_cmd_baton->options.for_internal, + dwi->options.for_internal, scratch_pool)); if (force_diff - || diff_cmd_baton->use_git_diff_format + || dwi->use_git_diff_format || svn_diff_contains_diffs(diff)) { /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); - if (diff_cmd_baton->use_git_diff_format) + if (dwi->use_git_diff_format) { const char *repos_relpath1; const char *repos_relpath2; SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, - diff_cmd_baton->orig_path_1, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, - diff_cmd_baton->anchor, + dwi->ddi.orig_path_1, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, scratch_pool, scratch_pool)); SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, - diff_cmd_baton->orig_path_2, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, - diff_cmd_baton->anchor, + dwi->ddi.orig_path_2, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, scratch_pool, scratch_pool)); SVN_ERR(print_git_diff_header(outstream, &label1, &label2, operation, @@ -898,20 +896,23 @@ diff_content_changed(svn_boolean_t *wrote_header, rev1, rev2, copyfrom_path, copyfrom_rev, - diff_cmd_baton->header_encoding, + dwi->header_encoding, scratch_pool)); } /* Output the actual diff */ if (force_diff || svn_diff_contains_diffs(diff)) - SVN_ERR(svn_diff_file_output_unified3(outstream, diff, + SVN_ERR(svn_diff_file_output_unified4(outstream, diff, tmpfile1, tmpfile2, label1, label2, - diff_cmd_baton->header_encoding, rel_to_dir, - diff_cmd_baton->options.for_internal->show_c_function, + dwi->header_encoding, rel_to_dir, + dwi->options.for_internal->show_c_function, + dwi->options.for_internal->context_size, + dwi->cancel_func, dwi->cancel_baton, scratch_pool)); - /* We have a printed a diff for this path, mark it as visited. */ - *wrote_header = TRUE; + /* If we have printed a diff for this path, mark it as visited. */ + if (dwi->use_git_diff_format || svn_diff_contains_diffs(diff)) + *wrote_header = TRUE; } } @@ -922,62 +923,43 @@ diff_content_changed(svn_boolean_t *wrote_header, return SVN_NO_ERROR; } +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - const char *diff_relpath, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - return SVN_NO_ERROR; -} - -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_file_changed(svn_wc_notify_state_t *content_state, - svn_wc_notify_state_t *prop_state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, +diff_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, - apr_hash_t *original_props, - void *diff_baton, + void *file_baton, + const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; svn_boolean_t wrote_header = FALSE; - /* During repos->wc diff of a copy revision numbers obtained - * from the working copy are always SVN_INVALID_REVNUM. */ - if (diff_cmd_baton->repos_wc_diff_target_is_copy) - { - if (rev1 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM) - rev1 = diff_cmd_baton->revnum1; - - if (rev2 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM) - rev2 = diff_cmd_baton->revnum2; - } - - if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + if (file_modified) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + left_source->revision, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_modified, FALSE, NULL, - SVN_INVALID_REVNUM, diff_cmd_baton, + SVN_INVALID_REVNUM, dwi, scratch_pool)); if (prop_changes->nelts > 0) - SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, FALSE, prop_changes, - original_props, !wrote_header, - diff_cmd_baton, scratch_pool)); + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + right_source->revision, prop_changes, + left_props, !wrote_header, + dwi, scratch_pool)); return SVN_NO_ERROR; } @@ -985,132 +967,125 @@ diff_file_changed(svn_wc_notify_state_t *content_state, each of these next two functions, they can be dumb wrappers around the main workhorse routine. */ -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_added(svn_wc_notify_state_t *content_state, - svn_wc_notify_state_t *prop_state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - const apr_array_header_t *prop_changes, - apr_hash_t *original_props, - void *diff_baton, +diff_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) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; svn_boolean_t wrote_header = FALSE; + const char *left_file; + apr_hash_t *left_props; + apr_array_header_t *prop_changes; /* During repos->wc diff of a copy revision numbers obtained * from the working copy are always SVN_INVALID_REVNUM. */ - if (diff_cmd_baton->repos_wc_diff_target_is_copy) + if (copyfrom_source && !dwi->show_copies_as_adds) { - if (rev1 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM) - rev1 = diff_cmd_baton->revnum1; - - if (rev2 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM) - rev2 = diff_cmd_baton->revnum2; + left_file = copyfrom_file; + left_props = copyfrom_props ? copyfrom_props : apr_hash_make(scratch_pool); } - - if (diff_cmd_baton->no_copyfrom_on_add - && (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision))) + else { - apr_hash_t *empty_hash = apr_hash_make(scratch_pool); - apr_array_header_t *new_changes; - - /* Rebase changes on having no left source. */ - if (!diff_cmd_baton->empty_file) - SVN_ERR(svn_io_open_unique_file3(NULL, &diff_cmd_baton->empty_file, + if (!dwi->empty_file) + SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file, NULL, svn_io_file_del_on_pool_cleanup, - diff_cmd_baton->pool, scratch_pool)); + dwi->pool, scratch_pool)); - SVN_ERR(svn_prop_diffs(&new_changes, - svn_prop__patch(original_props, prop_changes, - scratch_pool), - empty_hash, - scratch_pool)); + left_file = dwi->empty_file; + left_props = apr_hash_make(scratch_pool); - tmpfile1 = diff_cmd_baton->empty_file; - prop_changes = new_changes; - original_props = empty_hash; - copyfrom_revision = SVN_INVALID_REVNUM; + copyfrom_source = NULL; + copyfrom_file = NULL; } - if (diff_cmd_baton->no_diff_added) + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, scratch_pool)); + + if (dwi->no_diff_added) { - const char *index_path = diff_relpath; + const char *index_path = relpath; - if (diff_cmd_baton->anchor) - index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath, + if (dwi->ddi.anchor) + index_path = svn_dirent_join(dwi->ddi.anchor, relpath, scratch_pool); - SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream, - diff_cmd_baton->header_encoding, scratch_pool, + SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream, + dwi->header_encoding, scratch_pool, "Index: %s (added)" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); wrote_header = TRUE; } - else if (tmpfile1 && copyfrom_path) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + else if (copyfrom_source && right_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + copyfrom_source->revision, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_copied, TRUE /* force diff output */, - copyfrom_path, - copyfrom_revision, diff_cmd_baton, - scratch_pool)); - else if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + copyfrom_source->repos_relpath, + copyfrom_source->revision, + dwi, scratch_pool)); + else if (right_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + DIFF_REVNUM_NONEXISTENT, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_added, TRUE /* force diff output */, NULL, SVN_INVALID_REVNUM, - diff_cmd_baton, scratch_pool)); + dwi, scratch_pool)); if (prop_changes->nelts > 0) - SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, - FALSE, prop_changes, - original_props, ! wrote_header, - diff_cmd_baton, scratch_pool)); + SVN_ERR(diff_props_changed(relpath, + copyfrom_source ? copyfrom_source->revision + : DIFF_REVNUM_NONEXISTENT, + right_source->revision, + prop_changes, + left_props, ! wrote_header, + dwi, scratch_pool)); return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - const char *mimetype1, - const char *mimetype2, - apr_hash_t *original_props, - void *diff_baton, +diff_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) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; - if (diff_cmd_baton->no_diff_deleted) + if (dwi->no_diff_deleted) { - const char *index_path = diff_relpath; + const char *index_path = relpath; - if (diff_cmd_baton->anchor) - index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath, + if (dwi->ddi.anchor) + index_path = svn_dirent_join(dwi->ddi.anchor, relpath, scratch_pool); - SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream, - diff_cmd_baton->header_encoding, scratch_pool, + SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream, + dwi->header_encoding, scratch_pool, "Index: %s (deleted)" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); @@ -1118,99 +1093,144 @@ diff_file_deleted(svn_wc_notify_state_t *state, else { svn_boolean_t wrote_header = FALSE; - if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, - diff_cmd_baton->revnum1, - diff_cmd_baton->revnum2, - mimetype1, mimetype2, + + if (!dwi->empty_file) + SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file, + NULL, svn_io_file_del_on_pool_cleanup, + dwi->pool, scratch_pool)); + + if (left_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, dwi->empty_file, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + NULL, svn_diff_op_deleted, FALSE, NULL, SVN_INVALID_REVNUM, - diff_cmd_baton, + dwi, scratch_pool)); - /* Should we also report the properties as deleted? */ - } + if (left_props && apr_hash_count(left_props)) + { + apr_array_header_t *prop_changes; - /* We don't list all the deleted properties. */ + SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool), + left_props, scratch_pool)); - return SVN_NO_ERROR; -} - -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_dir_added(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *diff_relpath, - svn_revnum_t rev, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - /* Do nothing. */ + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + prop_changes, + left_props, ! wrote_header, + dwi, scratch_pool)); + } + } return SVN_NO_ERROR; } /* An svn_wc_diff_callbacks4_t function. */ static svn_error_t * -diff_dir_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - void *diff_baton, +diff_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) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + right_source->revision, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_dir_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *diff_relpath, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + apr_hash_t *left_props; + apr_array_header_t *prop_changes; - return SVN_NO_ERROR; + if (dwi->no_diff_added) + return SVN_NO_ERROR; + + if (copyfrom_source && !dwi->show_copies_as_adds) + { + left_props = copyfrom_props ? copyfrom_props + : apr_hash_make(scratch_pool); + } + else + { + left_props = apr_hash_make(scratch_pool); + copyfrom_source = NULL; + } + + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, + scratch_pool)); + + return svn_error_trace(diff_props_changed(relpath, + copyfrom_source ? copyfrom_source->revision + : DIFF_REVNUM_NONEXISTENT, + right_source->revision, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_dir_closed(svn_wc_notify_state_t *contentstate, - svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - svn_boolean_t dir_was_added, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + apr_array_header_t *prop_changes; + + if (dwi->no_diff_deleted) + return SVN_NO_ERROR; + + + SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool), + left_props, scratch_pool)); + + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); return SVN_NO_ERROR; } -static const svn_wc_diff_callbacks4_t diff_callbacks = -{ - diff_file_opened, - diff_file_changed, - diff_file_added, - diff_file_deleted, - diff_dir_deleted, - diff_dir_opened, - diff_dir_added, - diff_dir_props_changed, - diff_dir_closed -}; - /*-----------------------------------------------------------------*/ /** The logic behind 'svn diff' and 'svn merge'. */ @@ -1329,45 +1349,6 @@ check_diff_target_exists(const char *url, return SVN_NO_ERROR; } - -/* Return in *RESOLVED_URL the URL which PATH_OR_URL@PEG_REVISION has in - * REVISION. If the object has no location in REVISION, set *RESOLVED_URL - * to NULL. */ -static svn_error_t * -resolve_pegged_diff_target_url(const char **resolved_url, - svn_ra_session_t *ra_session, - const char *path_or_url, - const svn_opt_revision_t *peg_revision, - const svn_opt_revision_t *revision, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - svn_error_t *err; - - /* Check if the PATH_OR_URL exists at REVISION. */ - err = svn_client__repos_locations(resolved_url, NULL, - NULL, NULL, - ra_session, - path_or_url, - peg_revision, - revision, - NULL, - ctx, scratch_pool); - if (err) - { - if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES || - err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *resolved_url = NULL; - } - else - return svn_error_trace(err); - } - - return SVN_NO_ERROR; -} - /** Prepare a repos repos diff between PATH_OR_URL1 and * PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2. * Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2. @@ -1375,13 +1356,10 @@ resolve_pegged_diff_target_url(const char **resolved_url, * *TARGET1 and *TARGET2, based on *URL1 and *URL2. * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify * that at least one of the diff targets exists. - * Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION - * which is pointing at *ANCHOR1. * Use client context CTX. Do all allocations in POOL. */ static svn_error_t * diff_prepare_repos_repos(const char **url1, const char **url2, - const char **base_path, svn_revnum_t *rev1, svn_revnum_t *rev2, const char **anchor1, @@ -1399,92 +1377,135 @@ diff_prepare_repos_repos(const char **url1, const svn_opt_revision_t *peg_revision, apr_pool_t *pool) { - const char *abspath_or_url2; - const char *abspath_or_url1; + const char *local_abspath1 = NULL; + const char *local_abspath2 = NULL; const char *repos_root_url; const char *wri_abspath = NULL; + svn_client__pathrev_t *resolved1; + svn_client__pathrev_t *resolved2 = NULL; + enum svn_opt_revision_kind peg_kind = peg_revision->kind; if (!svn_path_is_url(path_or_url2)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2, pool)); - SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, abspath_or_url2, + SVN_ERR(svn_dirent_get_absolute(&local_abspath2, path_or_url2, pool)); + SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, local_abspath2, pool, pool)); - wri_abspath = abspath_or_url2; + wri_abspath = local_abspath2; } else - *url2 = abspath_or_url2 = apr_pstrdup(pool, path_or_url2); + *url2 = apr_pstrdup(pool, path_or_url2); if (!svn_path_is_url(path_or_url1)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool)); - SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, abspath_or_url1, - pool, pool)); - wri_abspath = abspath_or_url1; + SVN_ERR(svn_dirent_get_absolute(&local_abspath1, path_or_url1, pool)); + wri_abspath = local_abspath1; } - else - *url1 = abspath_or_url1 = apr_pstrdup(pool, path_or_url1); - - /* We need exactly one BASE_PATH, so we'll let the BASE_PATH - calculated for PATH_OR_URL2 override the one for PATH_OR_URL1 - (since the diff will be "applied" to URL2 anyway). */ - *base_path = NULL; - if (strcmp(*url1, path_or_url1) != 0) - *base_path = path_or_url1; - if (strcmp(*url2, path_or_url2) != 0) - *base_path = path_or_url2; SVN_ERR(svn_client_open_ra_session2(ra_session, *url2, wri_abspath, ctx, pool, pool)); /* If we are performing a pegged diff, we need to find out what our actual URLs will be. */ - if (peg_revision->kind != svn_opt_revision_unspecified) + if (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2 + || local_abspath2) { - const char *resolved_url1; - const char *resolved_url2; - - SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session, - path_or_url2, peg_revision, - revision2, ctx, pool)); - - SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); - SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session, - path_or_url1, peg_revision, - revision1, ctx, pool)); - - /* Either or both URLs might have changed as a result of resolving - * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs - * could be resolved, use the same URL for URL1 and URL2, so we can - * show a diff that adds or removes the object (see issue #4153). */ - if (resolved_url2) + svn_error_t *err; + + err = svn_client__resolve_rev_and_url(&resolved2, + *ra_session, path_or_url2, + peg_revision, revision2, + ctx, pool); + if (err) { - *url2 = resolved_url2; - if (!resolved_url1) - *url1 = resolved_url2; + if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES + && err->apr_err != SVN_ERR_FS_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + resolved2 = NULL; } - if (resolved_url1) + } + else + resolved2 = NULL; + + if (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2 + || local_abspath1) + { + svn_error_t *err; + + err = svn_client__resolve_rev_and_url(&resolved1, + *ra_session, path_or_url1, + peg_revision, revision1, + ctx, pool); + if (err) { - *url1 = resolved_url1; - if (!resolved_url2) - *url2 = resolved_url1; + if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES + && err->apr_err != SVN_ERR_FS_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + resolved1 = NULL; } + } + else + resolved1 = NULL; + + if (resolved1) + { + *url1 = resolved1->url; + *rev1 = resolved1->rev; + } + else + { + /* It would be nice if we could just return an error when resolving a + location fails... But in many such cases we prefer diffing against + an not existing location to show adds od removes (see issue #4153) */ + + if (resolved2 + && (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2)) + *url1 = resolved2->url; + else if (! local_abspath1) + *url1 = path_or_url1; + else + SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, local_abspath1, + pool, pool)); + + SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx, + local_abspath1 /* may be NULL */, + *ra_session, revision1, pool)); + } - /* Reparent the session, since *URL2 might have changed as a result - the above call. */ - SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); + if (resolved2) + { + *url2 = resolved2->url; + *rev2 = resolved2->rev; + } + else + { + /* It would be nice if we could just return an error when resolving a + location fails... But in many such cases we prefer diffing against + an not existing location to show adds od removes (see issue #4153) */ + + if (resolved1 + && (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2)) + *url2 = resolved1->url; + /* else keep url2 */ + + SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx, + local_abspath2 /* may be NULL */, + *ra_session, revision2, pool)); } /* Resolve revision and get path kind for the second target. */ - SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx, - (path_or_url2 == *url2) ? NULL : abspath_or_url2, - *ra_session, revision2, pool)); + SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool)); /* Do the same for the first target. */ SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); - SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx, - (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1, - *ra_session, revision1, pool)); SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool)); /* Either both URLs must exist at their respective revisions, @@ -1521,12 +1542,37 @@ diff_prepare_repos_repos(const char **url1, if (strcmp(*url1, repos_root_url) != 0 && strcmp(*url2, repos_root_url) != 0) { + svn_node_kind_t ignored_kind; + svn_error_t *err; + svn_uri_split(anchor1, target1, *url1, pool); svn_uri_split(anchor2, target2, *url2, pool); - if (*base_path - && (*kind1 == svn_node_file || *kind2 == svn_node_file)) - *base_path = svn_dirent_dirname(*base_path, pool); + SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool)); + + /* We might not have the necessary rights to read the root now. + (It is ok to pass a revision here where the node doesn't exist) */ + err = svn_ra_check_path(*ra_session, "", *rev1, &ignored_kind, pool); + + if (err && (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN + || err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED)) + { + svn_error_clear(err); + + /* Ok, lets undo the reparent... + + We can't report replacements this way, but at least we can + report changes on the descendants */ + + *anchor1 = svn_path_url_add_component2(*anchor1, *target1, pool); + *anchor2 = svn_path_url_add_component2(*anchor2, *target2, pool); + *target1 = ""; + *target2 = ""; + + SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool)); + } + else + SVN_ERR(err); } return SVN_NO_ERROR; @@ -1553,7 +1599,7 @@ diff_prepare_repos_repos(const char **url1, Since Subversion 1.8 we also have a variant of svn_wc_diff called svn_client__arbitrary_nodes_diff, that allows handling WORKING-WORKING - comparisions between nodes in the working copy. + comparisons between nodes in the working copy. So the truth of the matter is, if the caller's arguments can't be pigeonholed into one of these use-cases, we currently bail with a @@ -1583,28 +1629,27 @@ unsupported_diff_error(svn_error_t *child_err) All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_wc_wc(const char *path1, +diff_wc_wc(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, + const char *path1, const svn_opt_revision_t *revision1, const char *path2, const svn_opt_revision_t *revision2, 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 *changelists, - const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *abspath1; - svn_error_t *err; - svn_node_kind_t kind; SVN_ERR_ASSERT(! svn_path_is_url(path1)); SVN_ERR_ASSERT(! svn_path_is_url(path2)); - SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, scratch_pool)); /* Currently we support only the case where path1 and path2 are the same path. */ @@ -1617,42 +1662,25 @@ diff_wc_wc(const char *path1, "and its working files are supported at this time" ))); - - /* Resolve named revisions to real numbers. */ - err = svn_client__get_revision_number(&callback_baton->revnum1, NULL, - ctx->wc_ctx, abspath1, NULL, - revision1, pool); - - /* In case of an added node, we have no base rev, and we show a revision - * number of 0. Note that this code is currently always asking for - * svn_opt_revision_base. - * ### TODO: get rid of this 0 for added nodes. */ - if (err && (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)) + if (ddi) { - svn_error_clear(err); - callback_baton->revnum1 = 0; - } - else - SVN_ERR(err); + svn_node_kind_t kind; - callback_baton->revnum2 = SVN_INVALID_REVNUM; /* WC */ + SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, + TRUE, FALSE, scratch_pool)); - SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, - TRUE, FALSE, pool)); + if (kind != svn_node_dir) + ddi->anchor = svn_dirent_dirname(path1, scratch_pool); + else + ddi->anchor = path1; + } - if (kind != svn_node_dir) - callback_baton->anchor = svn_dirent_dirname(path1, pool); - else - callback_baton->anchor = path1; - - SVN_ERR(svn_wc_diff6(ctx->wc_ctx, - abspath1, - callbacks, callback_baton, - depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - ctx->cancel_func, ctx->cancel_baton, - pool)); + SVN_ERR(svn_wc__diff7(root_relpath, root_is_dir, + ctx->wc_ctx, abspath1, depth, + ignore_ancestry, changelists, + diff_processor, + ctx->cancel_func, ctx->cancel_baton, + result_pool, scratch_pool)); return SVN_NO_ERROR; } @@ -1666,9 +1694,9 @@ diff_wc_wc(const char *path1, All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, - svn_client_ctx_t *ctx, +diff_repos_repos(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, const char *path_or_url1, const char *path_or_url2, const svn_opt_revision_t *revision1, @@ -1676,7 +1704,11 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const svn_opt_revision_t *peg_revision, svn_depth_t depth, svn_boolean_t ignore_ancestry, - apr_pool_t *pool) + svn_boolean_t text_deltas, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_session_t *extra_ra_session; @@ -1686,11 +1718,8 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const svn_delta_editor_t *diff_editor; void *diff_edit_baton; - const svn_diff_tree_processor_t *diff_processor; - const char *url1; const char *url2; - const char *base_path; svn_revnum_t rev1; svn_revnum_t rev2; svn_node_kind_t kind1; @@ -1700,40 +1729,40 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const char *target1; const char *target2; svn_ra_session_t *ra_session; - const char *wri_abspath = NULL; /* Prepare info for the repos repos diff. */ - SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, + SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &rev1, &rev2, &anchor1, &anchor2, &target1, &target2, &kind1, &kind2, &ra_session, ctx, path_or_url1, path_or_url2, revision1, revision2, peg_revision, - pool)); - - /* Find a WC path for the ra session */ - if (!svn_path_is_url(path_or_url1)) - SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url1, pool)); - else if (!svn_path_is_url(path_or_url2)) - SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url2, pool)); + scratch_pool)); /* Set up the repos_diff editor on BASE_PATH, if available. Otherwise, we just use "". */ - SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, - callbacks, callback_baton, - TRUE /* walk_deleted_dirs */, - pool, pool)); - - /* Get actual URLs. */ - callback_baton->orig_path_1 = url1; - callback_baton->orig_path_2 = url2; - - /* Get numeric revisions. */ - callback_baton->revnum1 = rev1; - callback_baton->revnum2 = rev2; + if (ddi) + { + /* Get actual URLs. */ + ddi->orig_path_1 = url1; + ddi->orig_path_2 = url2; + + /* This should be moved to the diff writer + - path_or_url are provided by the caller + - target1 is available as *root_relpath + - (kind1 != svn_node_dir || kind2 != svn_node_dir) = !*root_is_dir */ + + if (!svn_path_is_url(path_or_url2)) + ddi->anchor = path_or_url2; + else if (!svn_path_is_url(path_or_url1)) + ddi->anchor = path_or_url1; + else + ddi->anchor = NULL; - callback_baton->ra_session = ra_session; - callback_baton->anchor = base_path; + if (*target1 && ddi->anchor + && (kind1 != svn_node_dir || kind2 != svn_node_dir)) + ddi->anchor = svn_dirent_dirname(ddi->anchor, result_pool); + } /* The repository can bring in a new working copy, but not delete everything. Luckily our new diff handler can just be reversed. */ @@ -1759,45 +1788,65 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, target1 = str_tmp; diff_processor = svn_diff__tree_processor_reverse_create(diff_processor, - NULL, pool); + NULL, + scratch_pool); } /* Filter the first path component using a filter processor, until we fixed the diff processing to handle this directly */ - if ((kind1 != svn_node_file && kind2 != svn_node_file) && target1[0] != '\0') - { - diff_processor = svn_diff__tree_processor_filter_create(diff_processor, - target1, pool); - } + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, target1); + else if ((kind1 != svn_node_file && kind2 != svn_node_file) + && target1[0] != '\0') + { + diff_processor = svn_diff__tree_processor_filter_create( + diff_processor, target1, scratch_pool); + } /* Now, we open an extra RA session to the correct anchor location for URL1. This is used during the editor calls to fetch file contents. */ - SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, wri_abspath, - ctx, pool, pool)); + SVN_ERR(svn_ra__dup_session(&extra_ra_session, ra_session, anchor1, + scratch_pool, scratch_pool)); + + if (ddi) + { + const char *repos_root_url; + const char *session_url; + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, + scratch_pool)); + SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, + scratch_pool)); + + ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url, + session_url, + result_pool); + } SVN_ERR(svn_client__get_diff_editor2( &diff_editor, &diff_edit_baton, extra_ra_session, depth, rev1, - TRUE /* text_deltas */, + text_deltas, diff_processor, ctx->cancel_func, ctx->cancel_baton, - pool)); + scratch_pool)); /* We want to switch our txn into URL2 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, rev2, target1, - depth, ignore_ancestry, TRUE /* text_deltas */, - url2, diff_editor, diff_edit_baton, pool)); + depth, ignore_ancestry, text_deltas, + url2, diff_editor, diff_edit_baton, scratch_pool)); /* Drive the reporter; do the diff. */ SVN_ERR(reporter->set_path(reporter_baton, "", rev1, svn_depth_infinity, FALSE, NULL, - pool)); + scratch_pool)); - return svn_error_trace(reporter->finish_report(reporter_baton, pool)); + return svn_error_trace( + reporter->finish_report(reporter_baton, scratch_pool)); } /* Perform a diff between a repository path and a working-copy path. @@ -1811,7 +1860,10 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_repos_wc(const char *path_or_url1, +diff_repos_wc(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, + const char *path_or_url1, const svn_opt_revision_t *revision1, const svn_opt_revision_t *peg_revision, const char *path2, @@ -1819,18 +1871,13 @@ diff_repos_wc(const char *path_or_url1, svn_boolean_t reverse, 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 *changelists, - const svn_wc_diff_callbacks4_t *callbacks, - void *callback_baton, - struct diff_cmd_baton *cmd_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_pool_t *pool = scratch_pool; - const char *url1, *anchor, *anchor_url, *target; - svn_revnum_t rev; + const char *anchor, *anchor_url, *target; svn_ra_session_t *ra_session; svn_depth_t diff_depth; const svn_ra_reporter3_t *reporter; @@ -1842,197 +1889,236 @@ diff_repos_wc(const char *path_or_url1, const char *abspath_or_url1; const char *abspath2; const char *anchor_abspath; - svn_node_kind_t kind1; - svn_node_kind_t kind2; svn_boolean_t is_copy; svn_revnum_t cf_revision; const char *cf_repos_relpath; const char *cf_repos_root_url; + svn_depth_t cf_depth; + const char *copy_root_abspath; + const char *target_url; + svn_client__pathrev_t *loc1; SVN_ERR_ASSERT(! svn_path_is_url(path2)); if (!svn_path_is_url(path_or_url1)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool)); - SVN_ERR(svn_wc__node_get_url(&url1, ctx->wc_ctx, abspath_or_url1, - pool, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, + scratch_pool)); } else { - url1 = path_or_url1; abspath_or_url1 = path_or_url1; } - SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, scratch_pool)); - /* Convert path_or_url1 to a URL to feed to do_diff. */ - SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, - ctx->wc_ctx, path2, - pool, pool)); + /* Check if our diff target is a copied node. */ + SVN_ERR(svn_wc__node_get_origin(&is_copy, + &cf_revision, + &cf_repos_relpath, + &cf_repos_root_url, + NULL, &cf_depth, + ©_root_abspath, + ctx->wc_ctx, abspath2, + FALSE, scratch_pool, scratch_pool)); - /* Fetch the URL of the anchor directory. */ - SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool)); - SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, - pool, pool)); - SVN_ERR_ASSERT(anchor_url != NULL); + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc1, + path_or_url1, abspath2, + peg_revision, revision1, + ctx, scratch_pool)); - /* If we are performing a pegged diff, we need to find out what our - actual URLs will be. */ - if (peg_revision->kind != svn_opt_revision_unspecified) + if (revision2->kind == svn_opt_revision_base || !is_copy) { - SVN_ERR(svn_client__repos_locations(&url1, NULL, NULL, NULL, - NULL, - path_or_url1, - peg_revision, - revision1, NULL, - ctx, pool)); - if (!reverse) + /* Convert path_or_url1 to a URL to feed to do_diff. */ + SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, ctx->wc_ctx, path2, + scratch_pool, scratch_pool)); + + /* Handle the ugly case where target is ".." */ + if (*target && !svn_path_is_single_path_component(target)) + { + anchor = svn_dirent_join(anchor, target, scratch_pool); + target = ""; + } + + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, target); + if (root_is_dir) + *root_is_dir = (*target == '\0'); + + /* Fetch the URL of the anchor directory. */ + SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, scratch_pool)); + SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, + scratch_pool, scratch_pool)); + SVN_ERR_ASSERT(anchor_url != NULL); + + target_url = NULL; + } + else /* is_copy && revision2->kind == svn_opt_revision_base */ + { +#if 0 + svn_node_kind_t kind; +#endif + /* ### Ugly hack ahead ### + * + * We're diffing a locally copied/moved node. + * Describe the copy source to the reporter instead of the copy itself. + * Doing the latter would generate a single add_directory() call to the + * diff editor which results in an unexpected diff (the copy would + * be shown as deleted). + * + * ### But if we will receive any real changes from the repositor we + * will most likely fail to apply them as the wc diff editor assumes + * that we have the data to which the change applies in BASE... + */ + + target_url = svn_path_url_add_component2(cf_repos_root_url, + cf_repos_relpath, + scratch_pool); + +#if 0 + /*SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath2, FALSE, FALSE, + scratch_pool)); + + if (kind != svn_node_dir + || strcmp(copy_root_abspath, abspath2) != 0) */ +#endif { - cmd_baton->orig_path_1 = url1; - cmd_baton->orig_path_2 = - svn_path_url_add_component2(anchor_url, target, pool); + /* We are looking at a subdirectory of the repository, + We can describe the parent directory as the anchor.. + + ### This 'appears to work', but that is really dumb luck + ### for the simple cases in the test suite */ + anchor_abspath = svn_dirent_dirname(abspath2, scratch_pool); + anchor_url = svn_path_url_add_component2(cf_repos_root_url, + svn_relpath_dirname( + cf_repos_relpath, + scratch_pool), + scratch_pool); + target = svn_dirent_basename(abspath2, NULL); + anchor = svn_dirent_dirname(path2, scratch_pool); } +#if 0 else { - cmd_baton->orig_path_1 = - svn_path_url_add_component2(anchor_url, target, pool); - cmd_baton->orig_path_2 = url1; + /* This code, while ok can't be enabled without causing test + * failures. The repository will send some changes against + * BASE for nodes that don't have BASE... + */ + anchor_abspath = abspath2; + anchor_url = svn_path_url_add_component2(cf_repos_root_url, + cf_repos_relpath, + scratch_pool); + anchor = path2; + target = ""; } +#endif } - /* Open an RA session to URL1 to figure out its node kind. */ - SVN_ERR(svn_client_open_ra_session2(&ra_session, url1, abspath2, - ctx, pool, pool)); - /* Resolve the revision to use for URL1. */ - SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, - (strcmp(path_or_url1, url1) == 0) - ? NULL : abspath_or_url1, - ra_session, revision1, pool)); - SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind1, pool)); - - /* Figure out the node kind of the local target. */ - SVN_ERR(svn_wc_read_kind2(&kind2, ctx->wc_ctx, abspath2, - TRUE, FALSE, pool)); - - cmd_baton->ra_session = ra_session; - cmd_baton->anchor = anchor; - - if (!reverse) - cmd_baton->revnum1 = rev; - else - cmd_baton->revnum2 = rev; + SVN_ERR(svn_ra_reparent(ra_session, anchor_url, scratch_pool)); - /* Check if our diff target is a copied node. */ - SVN_ERR(svn_wc__node_get_origin(&is_copy, - &cf_revision, - &cf_repos_relpath, - &cf_repos_root_url, - NULL, NULL, - ctx->wc_ctx, abspath2, - FALSE, pool, pool)); + if (ddi) + { + const char *repos_root_url; + + ddi->anchor = anchor; + + if (!reverse) + { + ddi->orig_path_1 = apr_pstrdup(result_pool, loc1->url); + ddi->orig_path_2 = + svn_path_url_add_component2(anchor_url, target, result_pool); + } + else + { + ddi->orig_path_1 = + svn_path_url_add_component2(anchor_url, target, result_pool); + ddi->orig_path_2 = apr_pstrdup(result_pool, loc1->url); + } + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, + scratch_pool)); + + ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url, + anchor_url, + result_pool); + } + + if (reverse) + diff_processor = svn_diff__tree_processor_reverse_create( + diff_processor, NULL, scratch_pool); /* Use the diff editor to generate the diff. */ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, - SVN_RA_CAPABILITY_DEPTH, pool)); + SVN_RA_CAPABILITY_DEPTH, scratch_pool)); SVN_ERR(svn_wc__get_diff_editor(&diff_editor, &diff_edit_baton, ctx->wc_ctx, anchor_abspath, target, depth, - ignore_ancestry || is_copy, - show_copies_as_adds, - use_git_diff_format, + ignore_ancestry, rev2_is_base, reverse, server_supports_depth, changelists, - callbacks, callback_baton, + diff_processor, ctx->cancel_func, ctx->cancel_baton, - pool, pool)); - SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool)); + scratch_pool, scratch_pool)); if (depth != svn_depth_infinity) diff_depth = depth; else diff_depth = svn_depth_unknown; - if (is_copy) - { - const char *copyfrom_parent_url; - const char *copyfrom_basename; - svn_depth_t copy_depth; - - cmd_baton->repos_wc_diff_target_is_copy = TRUE; - - /* We're diffing a locally copied/moved node. - * Describe the copy source to the reporter instead of the copy itself. - * Doing the latter would generate a single add_directory() call to the - * diff editor which results in an unexpected diff (the copy would - * be shown as deleted). */ - - if (cf_repos_relpath[0] == '\0') - { - copyfrom_parent_url = cf_repos_root_url; - copyfrom_basename = ""; - } - else - { - const char *parent_relpath; - svn_relpath_split(&parent_relpath, ©from_basename, - cf_repos_relpath, scratch_pool); - copyfrom_parent_url = svn_path_url_add_component2(cf_repos_root_url, - parent_relpath, - scratch_pool); - } - SVN_ERR(svn_ra_reparent(ra_session, copyfrom_parent_url, pool)); + if (is_copy && revision2->kind != svn_opt_revision_base) + { /* Tell the RA layer we want a delta to change our txn to URL1 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, - rev, + loc1->rev, target, diff_depth, ignore_ancestry, TRUE, /* text_deltas */ - url1, - diff_editor, diff_edit_baton, pool)); + loc1->url, + diff_editor, diff_edit_baton, + scratch_pool)); /* Report the copy source. */ - SVN_ERR(svn_wc__node_get_depth(©_depth, ctx->wc_ctx, abspath2, - pool)); + if (cf_depth == svn_depth_unknown) + cf_depth = svn_depth_infinity; - if (copy_depth == svn_depth_unknown) - copy_depth = svn_depth_infinity; + /* Reporting the in-wc revision as r0, makes the repository send + everything as added, which avoids using BASE for pristine information, + which is not there (or unrelated) for a copy */ SVN_ERR(reporter->set_path(reporter_baton, "", - cf_revision, - copy_depth, FALSE, NULL, scratch_pool)); + ignore_ancestry ? 0 : cf_revision, + cf_depth, FALSE, NULL, scratch_pool)); - if (strcmp(target, copyfrom_basename) != 0) + if (*target) SVN_ERR(reporter->link_path(reporter_baton, target, - svn_path_url_add_component2( - cf_repos_root_url, - cf_repos_relpath, - scratch_pool), - cf_revision, - copy_depth, FALSE, NULL, scratch_pool)); + target_url, + ignore_ancestry ? 0 : cf_revision, + cf_depth, FALSE, NULL, scratch_pool)); /* Finish the report to generate the diff. */ - SVN_ERR(reporter->finish_report(reporter_baton, pool)); + SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool)); } else { /* Tell the RA layer we want a delta to change our txn to URL1 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, - rev, + loc1->rev, target, diff_depth, ignore_ancestry, TRUE, /* text_deltas */ - url1, - diff_editor, diff_edit_baton, pool)); + loc1->url, + diff_editor, diff_edit_baton, + scratch_pool)); /* Create a txn mirror of path2; the diff editor will print diffs in reverse. :-) */ @@ -2043,18 +2129,18 @@ diff_repos_wc(const char *path_or_url1, FALSE, ctx->cancel_func, ctx->cancel_baton, NULL, NULL, /* notification is N/A */ - pool)); + scratch_pool)); } return SVN_NO_ERROR; } -/* This is basically just the guts of svn_client_diff[_peg]6(). */ +/* This is basically just the guts of svn_client_diff[_summarize][_peg]6(). */ static svn_error_t * -do_diff(const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, - svn_client_ctx_t *ctx, +do_diff(const char **root_relpath, + svn_boolean_t *root_is_dir, + diff_driver_info_t *ddi, const char *path_or_url1, const char *path_or_url2, const svn_opt_revision_t *revision1, @@ -2062,10 +2148,12 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, const svn_opt_revision_t *peg_revision, 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 *changelists, - apr_pool_t *pool) + svn_boolean_t text_deltas, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_boolean_t is_repos1; svn_boolean_t is_repos2; @@ -2079,32 +2167,35 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, if (is_repos2) { /* ### Ignores 'show_copies_as_adds'. */ - SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx, + SVN_ERR(diff_repos_repos(root_relpath, root_is_dir, + ddi, path_or_url1, path_or_url2, revision1, revision2, peg_revision, depth, ignore_ancestry, - pool)); + text_deltas, + diff_processor, ctx, + result_pool, scratch_pool)); } else /* path_or_url2 is a working copy path */ { - SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision, + SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi, + path_or_url1, revision1, peg_revision, path_or_url2, revision2, FALSE, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, callback_baton, - ctx, pool)); + ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); } } else /* path_or_url1 is a working copy path */ { if (is_repos2) { - SVN_ERR(diff_repos_wc(path_or_url2, revision2, peg_revision, + SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi, + path_or_url2, revision2, peg_revision, path_or_url1, revision1, TRUE, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, callback_baton, - ctx, pool)); + ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); } else /* path_or_url2 is a working copy path */ { @@ -2114,352 +2205,44 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, const char *abspath1; const char *abspath2; - SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool)); - SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, + scratch_pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, + scratch_pool)); - SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2, + /* ### What about ddi? */ + + SVN_ERR(svn_client__arbitrary_nodes_diff(root_relpath, root_is_dir, + abspath1, abspath2, depth, - callbacks, - callback_baton, - ctx, pool)); + diff_processor, + ctx, + result_pool, scratch_pool)); } else - SVN_ERR(diff_wc_wc(path_or_url1, revision1, - path_or_url2, revision2, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, ctx, pool)); + { + SVN_ERR(diff_wc_wc(root_relpath, root_is_dir, ddi, + path_or_url1, revision1, + path_or_url2, revision2, + depth, ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); + } } } return SVN_NO_ERROR; } -/* Perform a diff between a repository path and a working-copy path. - - PATH_OR_URL1 may be either a URL or a working copy path. PATH2 is a - working copy path. REVISION1 and REVISION2 are their respective - revisions. If REVERSE is TRUE, the diff will be done in reverse. - If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg - revision, and the actual repository path to be compared is - determined by following copy history. - - All other options are the same as those passed to svn_client_diff6(). */ -static svn_error_t * -diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - const char *path_or_url1, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *peg_revision, - const char *path2, - const svn_opt_revision_t *revision2, - svn_boolean_t reverse, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - const char *anchor, *target; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - struct diff_cmd_baton cmd_baton; - - SVN_ERR_ASSERT(! svn_path_is_url(path2)); - - SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, - ctx->wc_ctx, path2, - pool, pool)); - - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target, reverse, - summarize_func, summarize_baton, pool)); - - SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision, - path2, revision2, reverse, - depth, FALSE, TRUE, FALSE, changelists, - callbacks, callback_baton, &cmd_baton, - ctx, pool)); - return SVN_NO_ERROR; -} - -/* Perform a summary diff between two working-copy paths. - - PATH1 and PATH2 are both working copy paths. REVISION1 and - REVISION2 are their respective revisions. - - All other options are the same as those passed to svn_client_diff6(). */ -static svn_error_t * -diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - const char *path1, - const svn_opt_revision_t *revision1, - const char *path2, - const svn_opt_revision_t *revision2, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - const char *abspath1, *target1; - svn_node_kind_t kind; - - SVN_ERR_ASSERT(! svn_path_is_url(path1)); - SVN_ERR_ASSERT(! svn_path_is_url(path2)); - - /* Currently we support only the case where path1 and path2 are the - same path. */ - if ((strcmp(path1, path2) != 0) - || (! ((revision1->kind == svn_opt_revision_base) - && (revision2->kind == svn_opt_revision_working)))) - return unsupported_diff_error - (svn_error_create - (SVN_ERR_INCORRECT_PARAMS, NULL, - _("Summarized diffs are only supported between a path's text-base " - "and its working files at this time"))); - - /* Find the node kind of PATH1 so that we know whether the diff drive will - be anchored at PATH1 or its parent dir. */ - SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool)); - SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, - TRUE, FALSE, pool)); - target1 = (kind == svn_node_dir) ? "" : svn_dirent_basename(path1, pool); - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target1, FALSE, - summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_wc_diff6(ctx->wc_ctx, - abspath1, - callbacks, callback_baton, - depth, - ignore_ancestry, FALSE /* show_copies_as_adds */, - FALSE /* use_git_diff_format */, changelists, - ctx->cancel_func, ctx->cancel_baton, - pool)); - return SVN_NO_ERROR; -} - -/* Perform a diff summary between two repository paths. */ -static svn_error_t * -diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - svn_client_ctx_t *ctx, - const char *path_or_url1, - const char *path_or_url2, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *revision2, - const svn_opt_revision_t *peg_revision, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - apr_pool_t *pool) -{ - svn_ra_session_t *extra_ra_session; - - const svn_ra_reporter3_t *reporter; - void *reporter_baton; - - const svn_delta_editor_t *diff_editor; - void *diff_edit_baton; - - const svn_diff_tree_processor_t *diff_processor; - - const char *url1; - const char *url2; - const char *base_path; - svn_revnum_t rev1; - svn_revnum_t rev2; - svn_node_kind_t kind1; - svn_node_kind_t kind2; - const char *anchor1; - const char *anchor2; - const char *target1; - const char *target2; - svn_ra_session_t *ra_session; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - - /* Prepare info for the repos repos diff. */ - SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, - &anchor1, &anchor2, &target1, &target2, - &kind1, &kind2, &ra_session, - ctx, path_or_url1, path_or_url2, - revision1, revision2, - peg_revision, pool)); - - /* Set up the repos_diff editor. */ - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, - target1, FALSE, summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, - callbacks, callback_baton, - TRUE /* walk_deleted_dirs */, - pool, pool)); - - - /* The repository can bring in a new working copy, but not delete - everything. Luckily our new diff handler can just be reversed. */ - if (kind2 == svn_node_none) - { - const char *str_tmp; - svn_revnum_t rev_tmp; - - str_tmp = url2; - url2 = url1; - url1 = str_tmp; - - rev_tmp = rev2; - rev2 = rev1; - rev1 = rev_tmp; - - str_tmp = anchor2; - anchor2 = anchor1; - anchor1 = str_tmp; - - str_tmp = target2; - target2 = target1; - target1 = str_tmp; - - diff_processor = svn_diff__tree_processor_reverse_create(diff_processor, - NULL, pool); - } - - /* Now, we open an extra RA session to the correct anchor - location for URL1. This is used to get deleted path information. */ - SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, NULL, - ctx, pool, pool)); - - SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton, - extra_ra_session, - depth, - rev1, - FALSE /* text_deltas */, - diff_processor, - ctx->cancel_func, ctx->cancel_baton, - pool)); - - /* We want to switch our txn into URL2 */ - SVN_ERR(svn_ra_do_diff3 - (ra_session, &reporter, &reporter_baton, rev2, target1, - depth, ignore_ancestry, - FALSE /* do not create text delta */, url2, diff_editor, - diff_edit_baton, pool)); - - /* Drive the reporter; do the diff. */ - SVN_ERR(reporter->set_path(reporter_baton, "", rev1, - svn_depth_infinity, - FALSE, NULL, pool)); - return svn_error_trace(reporter->finish_report(reporter_baton, pool)); -} - -/* This is basically just the guts of svn_client_diff_summarize[_peg]2(). */ -static svn_error_t * -do_diff_summarize(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - svn_client_ctx_t *ctx, - const char *path_or_url1, - const char *path_or_url2, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *revision2, - const svn_opt_revision_t *peg_revision, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - apr_pool_t *pool) -{ - svn_boolean_t is_repos1; - svn_boolean_t is_repos2; - - /* Check if paths/revisions are urls/local. */ - SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2, - revision1, revision2, peg_revision)); - - if (is_repos1) - { - if (is_repos2) - SVN_ERR(diff_summarize_repos_repos(summarize_func, summarize_baton, ctx, - path_or_url1, path_or_url2, - revision1, revision2, - peg_revision, depth, ignore_ancestry, - pool)); - else - SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton, - path_or_url1, revision1, - peg_revision, - path_or_url2, revision2, - FALSE, depth, - ignore_ancestry, - changelists, - ctx, pool)); - } - else /* ! is_repos1 */ - { - if (is_repos2) - SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton, - path_or_url2, revision2, - peg_revision, - path_or_url1, revision1, - TRUE, depth, - ignore_ancestry, - changelists, - ctx, pool)); - else - { - if (revision1->kind == svn_opt_revision_working - && revision2->kind == svn_opt_revision_working) - { - const char *abspath1; - const char *abspath2; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - const char *target; - svn_node_kind_t kind; - - SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool)); - SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool)); - - SVN_ERR(svn_io_check_resolved_path(abspath1, &kind, pool)); - - if (kind == svn_node_dir) - target = ""; - else - target = svn_dirent_basename(path_or_url1, NULL); - - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target, FALSE, - summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2, - depth, - callbacks, - callback_baton, - ctx, pool)); - } - else - SVN_ERR(diff_summarize_wc_wc(summarize_func, summarize_baton, - path_or_url1, revision1, - path_or_url2, revision2, - depth, ignore_ancestry, - changelists, ctx, pool)); - } - } - - return SVN_NO_ERROR; -} - - -/* Initialize DIFF_CMD_BATON.diff_cmd and DIFF_CMD_BATON.options, +/* Initialize DWI.diff_cmd and DWI.options, * according to OPTIONS and CONFIG. CONFIG and OPTIONS may be null. - * Allocate the fields in POOL, which should be at least as long-lived - * as the pool DIFF_CMD_BATON itself is allocated in. + * Allocate the fields in RESULT_POOL, which should be at least as long-lived + * as the pool DWI itself is allocated in. */ static svn_error_t * -set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton, - const apr_array_header_t *options, - apr_hash_t *config, apr_pool_t *pool) +create_diff_writer_info(diff_writer_info_t *dwi, + const apr_array_header_t *options, + apr_hash_t *config, apr_pool_t *result_pool) { const char *diff_cmd = NULL; @@ -2475,41 +2258,41 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton, svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL); if (diff_extensions) - options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool); + options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, + result_pool); } } if (options == NULL) - options = apr_array_make(pool, 0, sizeof(const char *)); + options = apr_array_make(result_pool, 0, sizeof(const char *)); if (diff_cmd) - SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd, - pool)); + SVN_ERR(svn_path_cstring_to_utf8(&dwi->diff_cmd, diff_cmd, + result_pool)); else - diff_cmd_baton->diff_cmd = NULL; + dwi->diff_cmd = NULL; /* If there was a command, arrange options to pass to it. */ - if (diff_cmd_baton->diff_cmd) + if (dwi->diff_cmd) { const char **argv = NULL; int argc = options->nelts; if (argc) { int i; - argv = apr_palloc(pool, argc * sizeof(char *)); + argv = apr_palloc(result_pool, argc * sizeof(char *)); for (i = 0; i < argc; i++) SVN_ERR(svn_utf_cstring_to_utf8(&argv[i], - APR_ARRAY_IDX(options, i, const char *), pool)); + APR_ARRAY_IDX(options, i, const char *), result_pool)); } - diff_cmd_baton->options.for_external.argv = argv; - diff_cmd_baton->options.for_external.argc = argc; + dwi->options.for_external.argv = argv; + dwi->options.for_external.argc = argc; } else /* No command, so arrange options for internal invocation instead. */ { - diff_cmd_baton->options.for_internal - = svn_diff_file_options_create(pool); - SVN_ERR(svn_diff_file_options_parse - (diff_cmd_baton->options.for_internal, options, pool)); + dwi->options.for_internal = svn_diff_file_options_create(result_pool); + SVN_ERR(svn_diff_file_options_parse(dwi->options.for_internal, + options, result_pool)); } return SVN_NO_ERROR; @@ -2574,8 +2357,10 @@ svn_client_diff6(const apr_array_header_t *options, svn_client_ctx_t *ctx, apr_pool_t *pool) { - struct diff_cmd_baton diff_cmd_baton = { 0 }; + diff_writer_info_t dwi = { 0 }; svn_opt_revision_t peg_revision; + const svn_diff_tree_processor_t *diff_processor; + svn_diff_tree_processor_t *processor; if (ignore_properties && properties_only) return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, @@ -2586,36 +2371,54 @@ svn_client_diff6(const apr_array_header_t *options, peg_revision.kind = svn_opt_revision_unspecified; /* setup callback and baton */ - diff_cmd_baton.orig_path_1 = path_or_url1; - diff_cmd_baton.orig_path_2 = path_or_url2; - - SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options, - ctx->config, pool)); - diff_cmd_baton.pool = pool; - diff_cmd_baton.outstream = outstream; - diff_cmd_baton.errstream = errstream; - diff_cmd_baton.header_encoding = header_encoding; - diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM; - diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM; - - diff_cmd_baton.force_binary = ignore_content_type; - diff_cmd_baton.ignore_properties = ignore_properties; - diff_cmd_baton.properties_only = properties_only; - diff_cmd_baton.relative_to_dir = relative_to_dir; - diff_cmd_baton.use_git_diff_format = use_git_diff_format; - diff_cmd_baton.no_diff_added = no_diff_added; - diff_cmd_baton.no_diff_deleted = no_diff_deleted; - diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds; - - diff_cmd_baton.wc_ctx = ctx->wc_ctx; - diff_cmd_baton.ra_session = NULL; - diff_cmd_baton.anchor = NULL; - - return do_diff(&diff_callbacks, &diff_cmd_baton, ctx, - path_or_url1, path_or_url2, revision1, revision2, - &peg_revision, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, pool); + dwi.ddi.orig_path_1 = path_or_url1; + dwi.ddi.orig_path_2 = path_or_url2; + + SVN_ERR(create_diff_writer_info(&dwi, options, + ctx->config, pool)); + dwi.pool = pool; + dwi.outstream = outstream; + dwi.errstream = errstream; + dwi.header_encoding = header_encoding; + + dwi.force_binary = ignore_content_type; + dwi.ignore_properties = ignore_properties; + dwi.properties_only = properties_only; + dwi.relative_to_dir = relative_to_dir; + dwi.use_git_diff_format = use_git_diff_format; + dwi.no_diff_added = no_diff_added; + dwi.no_diff_deleted = no_diff_deleted; + dwi.show_copies_as_adds = show_copies_as_adds; + + dwi.cancel_func = ctx->cancel_func; + dwi.cancel_baton = ctx->cancel_baton; + + dwi.wc_ctx = ctx->wc_ctx; + dwi.ddi.session_relpath = NULL; + dwi.ddi.anchor = NULL; + + processor = svn_diff__tree_processor_create(&dwi, pool); + + processor->dir_added = diff_dir_added; + processor->dir_changed = diff_dir_changed; + processor->dir_deleted = diff_dir_deleted; + + processor->file_added = diff_file_added; + processor->file_changed = diff_file_changed; + processor->file_deleted = diff_file_deleted; + + diff_processor = processor; + + /* --show-copies-as-adds and --git imply --notice-ancestry */ + if (show_copies_as_adds || use_git_diff_format) + ignore_ancestry = FALSE; + + return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi, + path_or_url1, path_or_url2, + revision1, revision2, &peg_revision, + depth, ignore_ancestry, changelists, + TRUE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2641,7 +2444,9 @@ svn_client_diff_peg6(const apr_array_header_t *options, svn_client_ctx_t *ctx, apr_pool_t *pool) { - struct diff_cmd_baton diff_cmd_baton = { 0 }; + diff_writer_info_t dwi = { 0 }; + const svn_diff_tree_processor_t *diff_processor; + svn_diff_tree_processor_t *processor; if (ignore_properties && properties_only) return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, @@ -2649,36 +2454,54 @@ svn_client_diff_peg6(const apr_array_header_t *options, "properties at the same time")); /* setup callback and baton */ - diff_cmd_baton.orig_path_1 = path_or_url; - diff_cmd_baton.orig_path_2 = path_or_url; - - SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options, - ctx->config, pool)); - diff_cmd_baton.pool = pool; - diff_cmd_baton.outstream = outstream; - diff_cmd_baton.errstream = errstream; - diff_cmd_baton.header_encoding = header_encoding; - diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM; - diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM; - - diff_cmd_baton.force_binary = ignore_content_type; - diff_cmd_baton.ignore_properties = ignore_properties; - diff_cmd_baton.properties_only = properties_only; - diff_cmd_baton.relative_to_dir = relative_to_dir; - diff_cmd_baton.use_git_diff_format = use_git_diff_format; - diff_cmd_baton.no_diff_added = no_diff_added; - diff_cmd_baton.no_diff_deleted = no_diff_deleted; - diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds; - - diff_cmd_baton.wc_ctx = ctx->wc_ctx; - diff_cmd_baton.ra_session = NULL; - diff_cmd_baton.anchor = NULL; - - return do_diff(&diff_callbacks, &diff_cmd_baton, ctx, - path_or_url, path_or_url, start_revision, end_revision, - peg_revision, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, pool); + dwi.ddi.orig_path_1 = path_or_url; + dwi.ddi.orig_path_2 = path_or_url; + + SVN_ERR(create_diff_writer_info(&dwi, options, + ctx->config, pool)); + dwi.pool = pool; + dwi.outstream = outstream; + dwi.errstream = errstream; + dwi.header_encoding = header_encoding; + + dwi.force_binary = ignore_content_type; + dwi.ignore_properties = ignore_properties; + dwi.properties_only = properties_only; + dwi.relative_to_dir = relative_to_dir; + dwi.use_git_diff_format = use_git_diff_format; + dwi.no_diff_added = no_diff_added; + dwi.no_diff_deleted = no_diff_deleted; + dwi.show_copies_as_adds = show_copies_as_adds; + + dwi.cancel_func = ctx->cancel_func; + dwi.cancel_baton = ctx->cancel_baton; + + dwi.wc_ctx = ctx->wc_ctx; + dwi.ddi.session_relpath = NULL; + dwi.ddi.anchor = NULL; + + processor = svn_diff__tree_processor_create(&dwi, pool); + + processor->dir_added = diff_dir_added; + processor->dir_changed = diff_dir_changed; + processor->dir_deleted = diff_dir_deleted; + + processor->file_added = diff_file_added; + processor->file_changed = diff_file_changed; + processor->file_deleted = diff_file_deleted; + + diff_processor = processor; + + /* --show-copies-as-adds and --git imply --notice-ancestry */ + if (show_copies_as_adds || use_git_diff_format) + ignore_ancestry = FALSE; + + return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi, + path_or_url, path_or_url, + start_revision, end_revision, peg_revision, + depth, ignore_ancestry, changelists, + TRUE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2694,14 +2517,24 @@ svn_client_diff_summarize2(const char *path_or_url1, svn_client_ctx_t *ctx, apr_pool_t *pool) { - /* We will never do a pegged diff from here. */ + const svn_diff_tree_processor_t *diff_processor; svn_opt_revision_t peg_revision; + const char **p_root_relpath; + + /* We will never do a pegged diff from here. */ peg_revision.kind = svn_opt_revision_unspecified; - return do_diff_summarize(summarize_func, summarize_baton, ctx, - path_or_url1, path_or_url2, revision1, revision2, - &peg_revision, - depth, ignore_ancestry, changelists, pool); + SVN_ERR(svn_client__get_diff_summarize_callbacks( + &diff_processor, &p_root_relpath, + summarize_func, summarize_baton, + path_or_url1, pool, pool)); + + return svn_error_trace(do_diff(p_root_relpath, NULL, NULL, + path_or_url1, path_or_url2, + revision1, revision2, &peg_revision, + depth, ignore_ancestry, changelists, + FALSE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2717,22 +2550,19 @@ svn_client_diff_summarize_peg2(const char *path_or_url, svn_client_ctx_t *ctx, apr_pool_t *pool) { - return do_diff_summarize(summarize_func, summarize_baton, ctx, - path_or_url, path_or_url, - start_revision, end_revision, peg_revision, - depth, ignore_ancestry, changelists, pool); -} - -svn_client_diff_summarize_t * -svn_client_diff_summarize_dup(const svn_client_diff_summarize_t *diff, - apr_pool_t *pool) -{ - svn_client_diff_summarize_t *dup_diff = apr_palloc(pool, sizeof(*dup_diff)); - - *dup_diff = *diff; - - if (diff->path) - dup_diff->path = apr_pstrdup(pool, diff->path); + const svn_diff_tree_processor_t *diff_processor; + const char **p_root_relpath; - return dup_diff; + SVN_ERR(svn_client__get_diff_summarize_callbacks( + &diff_processor, &p_root_relpath, + summarize_func, summarize_baton, + path_or_url, pool, pool)); + + return svn_error_trace(do_diff(p_root_relpath, NULL, NULL, + path_or_url, path_or_url, + start_revision, end_revision, peg_revision, + depth, ignore_ancestry, changelists, + FALSE /* text_deltas */, + diff_processor, ctx, pool, pool)); } + diff --git a/contrib/subversion/subversion/libsvn_client/diff_local.c b/contrib/subversion/subversion/libsvn_client/diff_local.c index 2dd8a1b67..df6bc0a8c 100644 --- a/contrib/subversion/subversion/libsvn_client/diff_local.c +++ b/contrib/subversion/subversion/libsvn_client/diff_local.c @@ -45,7 +45,9 @@ #include "svn_subst.h" #include "client.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" +#include "private/svn_diff_tree.h" #include "svn_private_config.h" @@ -81,559 +83,718 @@ get_props(apr_hash_t **props, return SVN_NO_ERROR; } -/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and - * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS. - * Use PATH as the name passed to diff callbacks. - * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback - * function to use to compare the files (added/deleted/changed). +/* Forward declaration */ +static svn_error_t * +do_file_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +/* Forward declaration */ +static svn_error_t * +do_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +/* Produce a diff of depth DEPTH between two arbitrary directories at + * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks + * to show file changes and, for versioned nodes, property changes. + * + * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH. * - * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties - * instead of reading properties from LOCAL_ABSPATH1. This is required when - * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that - * file content must be diffed against, but properties to diff against come - * from the replaced directory. */ + * If LEFT_ONLY is TRUE, only the left source exists (= everything will + * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source + * exists (= everything will be reported as added). + * + * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is + * reported first. If false, right is reported first. (This is to allow + * producing a proper inverse diff). + * + * Walk the sources according to depth, and report with parent baton + * PARENT_BATON. */ static svn_error_t * -do_arbitrary_files_diff(const char *local_abspath1, - const char *local_abspath2, - const char *path, - svn_boolean_t file1_is_empty, - svn_boolean_t file2_is_empty, - apr_hash_t *original_props_override, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) +inner_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - apr_hash_t *original_props; - apr_hash_t *modified_props; - apr_array_header_t *prop_changes; - svn_string_t *original_mime_type = NULL; - svn_string_t *modified_mime_type = NULL; - - if (ctx->cancel_func) - SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - - /* Try to get properties from either file. It's OK if the files do not - * have properties, or if they are unversioned. */ - if (original_props_override) - original_props = original_props_override; - else - SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx, - scratch_pool, scratch_pool)); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *left_dirents; + apr_hash_t *right_dirents; + apr_array_header_t *sorted_dirents; + svn_error_t *err; + svn_depth_t depth_below_here; + int i; - SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, - scratch_pool)); + SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity); - /* Try to determine the mime-type of each file. */ - original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE); - if (!file1_is_empty && !original_mime_type) + if (!right_only) { - const char *mime_type; - SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, - ctx->mimetypes_map, scratch_pool)); + err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE, + scratch_pool, iterpool); - if (mime_type) - original_mime_type = svn_string_create(mime_type, scratch_pool); + if (err && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + left_dirents = apr_hash_make(scratch_pool); + right_only = TRUE; + } + else + SVN_ERR(err); } + else + left_dirents = apr_hash_make(scratch_pool); - modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE); - if (!file2_is_empty && !modified_mime_type) + if (!left_only) { - const char *mime_type; - SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, - ctx->mimetypes_map, scratch_pool)); + err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE, + scratch_pool, iterpool); - if (mime_type) - modified_mime_type = svn_string_create(mime_type, scratch_pool); + if (err && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + right_dirents = apr_hash_make(scratch_pool); + right_only = TRUE; + } + else + SVN_ERR(err); } + else + right_dirents = apr_hash_make(scratch_pool); + + if (left_only && right_only) + return SVN_NO_ERROR; /* Somebody deleted the directory?? */ - /* Produce the diff. */ - if (file1_is_empty && !file2_is_empty) - SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path, - local_abspath1, local_abspath2, - /* ### TODO get real revision info - * for versioned files? */ - SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - /* ### TODO get copyfrom? */ - NULL, SVN_INVALID_REVNUM, - prop_changes, original_props, - diff_baton, scratch_pool)); - else if (!file1_is_empty && file2_is_empty) - SVN_ERR(callbacks->file_deleted(NULL, NULL, path, - local_abspath1, local_abspath2, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - original_props, - diff_baton, scratch_pool)); + if (depth != svn_depth_infinity) + depth_below_here = svn_depth_empty; else + depth_below_here = svn_depth_infinity; + + sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents, + right_dirents, NULL, NULL), + svn_sort_compare_items_as_paths, + scratch_pool); + + for (i = 0; i < sorted_dirents->nelts; i++) { - svn_stream_t *file1; - svn_stream_t *file2; - svn_boolean_t same; - svn_string_t *val; - /* We have two files, which may or may not be the same. + svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); + svn_io_dirent2_t *left_dirent; + svn_io_dirent2_t *right_dirent; + const char *child_left_abspath; + const char *child_right_abspath; - ### Our caller assumes that we should ignore symlinks here and - handle them as normal paths. Perhaps that should change? - */ - SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool, - scratch_pool)); + svn_pool_clear(iterpool); - SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool, - scratch_pool)); + if (ctx->cancel_func) + SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - /* Wrap with normalization, etc. if necessary */ - if (original_props) - { - val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE); + if (svn_wc_is_adm_dir(elt->key, iterpool)) + continue; - if (val) + left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key); + right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key); + + child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool); + child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool); + + if (((left_dirent == NULL) != (right_dirent == NULL)) + || (left_dirent->kind != right_dirent->kind)) + { + /* Report delete and/or add */ + if (left_dirent && left_before_right) { - svn_subst_eol_style_t style; - const char *eol; - svn_subst_eol_style_from_value(&style, &eol, val->data); - - /* ### Ignoring keywords */ - if (eol) - file1 = svn_subst_stream_translated(file1, eol, TRUE, - NULL, FALSE, - scratch_pool); + if (left_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } - } - if (modified_props) - { - val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE); + if (right_dirent) + { + if (right_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); + } - if (val) + if (left_dirent && !left_before_right) { - svn_subst_eol_style_t style; - const char *eol; - svn_subst_eol_style_from_value(&style, &eol, val->data); - - /* ### Ignoring keywords */ - if (eol) - file2 = svn_subst_stream_translated(file2, eol, TRUE, - NULL, FALSE, - scratch_pool); + if (left_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } } - - SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool)); - - if (! same || prop_changes->nelts > 0) + else if (left_dirent->kind == svn_node_file) { - /* ### We should probably pass the normalized data we created using - the subst streams as that is what diff users expect */ - SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path, - same ? NULL : local_abspath1, - same ? NULL : local_abspath2, - /* ### TODO get real revision info - * for versioned files? */ - SVN_INVALID_REVNUM /* rev1 */, - SVN_INVALID_REVNUM /* rev2 */, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - prop_changes, original_props, - diff_baton, scratch_pool)); + /* Perform file-file diff */ + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + } + else if (depth >= svn_depth_immediates) + { + /* Perform dir-dir diff */ + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } } return SVN_NO_ERROR; } -struct arbitrary_diff_walker_baton { - /* The root directories of the trees being compared. */ - const char *root1_abspath; - const char *root2_abspath; - - /* TRUE if recursing within an added subtree of root2_abspath that - * does not exist in root1_abspath. */ - svn_boolean_t recursing_within_added_subtree; +/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the + file needs translation. *LEFT_ABSPATH is updated to point to a file that + lives at least as long as RESULT_POOL when translation is necessary. + Otherwise the value is not updated */ +static svn_error_t * +translate_if_necessary(const char **local_abspath, + apr_hash_t *props, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_string_t *eol_style_val; + const svn_string_t *keywords_val; + svn_subst_eol_style_t eol_style; + const char *eol; + apr_hash_t *keywords; + svn_stream_t *contents; + svn_stream_t *dst; + + /* if (svn_hash_gets(props, SVN_PROP_SPECIAL)) + ### TODO: Implement */ + + eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE); + keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS); + + if (eol_style_val) + svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data); + else + { + eol = NULL; + eol_style = svn_subst_eol_style_none; + } - /* TRUE if recursing within an administrative (.i.e. .svn) directory. */ - svn_boolean_t recursing_within_adm_dir; + if (keywords_val) + SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, + APR_STRINGIFY(SVN_INVALID_REVNUM), + "", "", 0, "", scratch_pool)); + else + keywords = NULL; - /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE. - * Else this is NULL.*/ - const char *adm_dir_abspath; + if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE)) + return SVN_NO_ERROR; - /* A path to an empty file used for diffs that add/delete files. */ - const char *empty_file_abspath; + SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath, + scratch_pool, scratch_pool)); - const svn_wc_diff_callbacks4_t *callbacks; - void *diff_baton; - svn_client_ctx_t *ctx; - apr_pool_t *pool; -} arbitrary_diff_walker_baton; + SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); -/* Forward declaration needed because this function has a cyclic - * dependency with do_arbitrary_dirs_diff(). */ -static svn_error_t * -arbitrary_diff_walker(void *baton, const char *local_abspath, - const apr_finfo_t *finfo, - apr_pool_t *scratch_pool); + dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */, + keywords, FALSE /* expand */, + scratch_pool); -/* Another forward declaration. */ -static svn_error_t * -arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, - const char *local_abspath, - svn_depth_t depth, - apr_pool_t *scratch_pool); + SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton, + scratch_pool)); -/* Produce a diff of depth DEPTH between two arbitrary directories at - * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks - * to show file changes and, for versioned nodes, property changes. - * - * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs - * relative to these roots, rather than relative to LOCAL_ABSPATH1 and - * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists - * only within LOCAL_ABSPATH2. */ -static svn_error_t * -do_arbitrary_dirs_diff(const char *local_abspath1, - const char *local_abspath2, - const char *root_abspath1, - const char *root_abspath2, - svn_depth_t depth, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - apr_file_t *empty_file; - svn_node_kind_t kind1; - - struct arbitrary_diff_walker_baton b; - - /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead - * and compare it to LOCAL_ABSPATH1, showing only additions. - * This case can only happen during recursion from arbitrary_diff_walker(), - * because do_arbitrary_nodes_diff() prevents this from happening at - * the root of the comparison. */ - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); - b.recursing_within_added_subtree = (kind1 != svn_node_dir); - - b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1; - b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2; - b.recursing_within_adm_dir = FALSE; - b.adm_dir_abspath = NULL; - b.callbacks = callbacks; - b.diff_baton = diff_baton; - b.ctx = ctx; - b.pool = scratch_pool; - - SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath, - NULL, svn_io_file_del_on_pool_cleanup, - scratch_pool, scratch_pool)); - - if (depth <= svn_depth_immediates) - SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool)); - else if (depth == svn_depth_infinity) - SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2 - : local_abspath1, - 0, arbitrary_diff_walker, &b, scratch_pool)); return SVN_NO_ERROR; } -/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH, - * using information from the arbitrary_diff_walker_baton B. - * LOCAL_ABSPATH is the path being crawled and can be on either side - * of the diff depending on baton->recursing_within_added_subtree. */ +/* Handles reporting of a file for inner_dir_diff */ static svn_error_t * -arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, - const char *local_abspath, - svn_depth_t depth, - apr_pool_t *scratch_pool) +do_file_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - const char *local_abspath1; - const char *local_abspath2; - svn_node_kind_t kind1; - svn_node_kind_t kind2; - const char *child_relpath; - apr_hash_t *dirents1; - apr_hash_t *dirents2; - apr_hash_t *merged_dirents; - apr_array_header_t *sorted_dirents; - int i; - apr_pool_t *iterpool; - - if (b->recursing_within_adm_dir) - { - if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath)) - return SVN_NO_ERROR; - else - { - b->recursing_within_adm_dir = FALSE; - b->adm_dir_abspath = NULL; - } - } - else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), - scratch_pool)) - { - b->recursing_within_adm_dir = TRUE; - b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath); - return SVN_NO_ERROR; - } + const char *relpath; + svn_diff_source_t *left_source; + svn_diff_source_t *right_source; + svn_boolean_t skip = FALSE; + apr_hash_t *left_props; + apr_hash_t *right_props; + void *file_baton; + + relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); + + if (! right_only) + left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + else + left_source = NULL; - if (b->recursing_within_added_subtree) - child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath); + if (! left_only) + right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); else - child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath); - if (!child_relpath) + right_source = NULL; + + SVN_ERR(diff_processor->file_opened(&file_baton, &skip, + relpath, + left_source, + right_source, + NULL /* copyfrom_source */, + parent_baton, + diff_processor, + scratch_pool, + scratch_pool)); + + if (skip) return SVN_NO_ERROR; - local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath, - scratch_pool); - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); + if (! right_only) + { + SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); - local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath, - scratch_pool); - SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); + /* We perform a mimetype detection to avoid diffing binary files + for textual changes.*/ + if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE)) + { + const char *mime_type; - if (depth > svn_depth_empty) - { - if (kind1 == svn_node_dir) - SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1, - TRUE, /* only_check_type */ - scratch_pool, scratch_pool)); - else - dirents1 = apr_hash_make(scratch_pool); + /* ### Use libmagic magic? */ + SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath, + ctx->mimetypes_map, scratch_pool)); + + if (mime_type) + svn_hash_sets(left_props, SVN_PROP_MIME_TYPE, + svn_string_create(mime_type, scratch_pool)); + } + + SVN_ERR(translate_if_necessary(&left_abspath, left_props, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); } + else + left_props = NULL; - if (kind2 == svn_node_dir) + if (! left_only) { - apr_hash_t *original_props; - apr_hash_t *modified_props; - apr_array_header_t *prop_changes; - - /* Show any property changes for this directory. */ - SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx, + SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, scratch_pool, scratch_pool)); - SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, - scratch_pool)); - if (prop_changes->nelts > 0) - SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath, - FALSE /* was_added */, - prop_changes, original_props, - b->diff_baton, - scratch_pool)); - - if (depth > svn_depth_empty) - { - /* Read directory entries. */ - SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2, - TRUE, /* only_check_type */ - scratch_pool, scratch_pool)); - } - } - else if (depth > svn_depth_empty) - dirents2 = apr_hash_make(scratch_pool); - if (depth <= svn_depth_empty) - return SVN_NO_ERROR; + /* We perform a mimetype detection to avoid diffing binary files + for textual changes.*/ + if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE)) + { + const char *mime_type; - /* Compare dirents1 to dirents2 and show added/deleted/changed files. */ - merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2, - NULL, NULL); - sorted_dirents = svn_sort__hash(merged_dirents, - svn_sort_compare_items_as_paths, - scratch_pool); - iterpool = svn_pool_create(scratch_pool); - for (i = 0; i < sorted_dirents->nelts; i++) - { - svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); - const char *name = elt.key; - svn_io_dirent2_t *dirent1; - svn_io_dirent2_t *dirent2; - const char *child1_abspath; - const char *child2_abspath; + /* ### Use libmagic magic? */ + SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath, + ctx->mimetypes_map, scratch_pool)); - svn_pool_clear(iterpool); + if (mime_type) + svn_hash_sets(right_props, SVN_PROP_MIME_TYPE, + svn_string_create(mime_type, scratch_pool)); + } - if (b->ctx->cancel_func) - SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); + SVN_ERR(translate_if_necessary(&right_abspath, right_props, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); - if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0) - continue; + } + else + right_props = NULL; - dirent1 = svn_hash_gets(dirents1, name); - if (!dirent1) - { - dirent1 = svn_io_dirent2_create(iterpool); - dirent1->kind = svn_node_none; - } - dirent2 = svn_hash_gets(dirents2, name); - if (!dirent2) - { - dirent2 = svn_io_dirent2_create(iterpool); - dirent2->kind = svn_node_none; - } + if (left_only) + { + SVN_ERR(diff_processor->file_deleted(relpath, + left_source, + left_abspath, + left_props, + file_baton, + diff_processor, + scratch_pool)); + } + else if (right_only) + { + SVN_ERR(diff_processor->file_added(relpath, + NULL /* copyfrom_source */, + right_source, + NULL /* copyfrom_file */, + right_abspath, + NULL /* copyfrom_props */, + right_props, + file_baton, + diff_processor, + scratch_pool)); + } + else + { + /* ### Perform diff -> close/changed */ + svn_boolean_t same; + apr_array_header_t *prop_changes; - child1_abspath = svn_dirent_join(local_abspath1, name, iterpool); - child2_abspath = svn_dirent_join(local_abspath2, name, iterpool); + SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath, + scratch_pool)); - if (dirent1->special) - SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind, - iterpool)); - if (dirent2->special) - SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind, - iterpool)); + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, + scratch_pool)); - if (dirent1->kind == svn_node_dir && - dirent2->kind == svn_node_dir) + if (!same || prop_changes->nelts > 0) { - if (depth == svn_depth_immediates) - { - /* Not using the walker, so show property diffs on these dirs. */ - SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, - b->root1_abspath, b->root2_abspath, - svn_depth_empty, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); - } - else - { - /* Either the walker will visit these directories (with - * depth=infinity) and they will be processed as 'this dir' - * later, or we're showing file children only (depth=files). */ - continue; - } - + SVN_ERR(diff_processor->file_changed(relpath, + left_source, + right_source, + same ? NULL : left_abspath, + same ? NULL : right_abspath, + left_props, + right_props, + !same, + prop_changes, + file_baton, + diff_processor, + scratch_pool)); } - - /* Files that exist only in dirents1. */ - if (dirent1->kind == svn_node_file && - (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none)) - SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - FALSE, TRUE, NULL, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); - - /* Files that exist only in dirents2. */ - if (dirent2->kind == svn_node_file && - (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none)) + else { - apr_hash_t *original_props; - - SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - TRUE, FALSE, original_props, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); + SVN_ERR(diff_processor->file_closed(relpath, + left_source, + right_source, + file_baton, + diff_processor, + scratch_pool)); } - - /* Files that exist in dirents1 and dirents2. */ - if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file) - SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - FALSE, FALSE, NULL, - b->callbacks, b->diff_baton, - b->ctx, scratch_pool)); - - /* Directories that only exist in dirents2. These aren't crawled - * by this walker so we have to crawl them separately. */ - if (depth > svn_depth_files && - dirent2->kind == svn_node_dir && - (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none)) - SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, - b->root1_abspath, b->root2_abspath, - depth <= svn_depth_immediates - ? svn_depth_empty - : svn_depth_infinity , - b->callbacks, b->diff_baton, - b->ctx, iterpool)); } - - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; } -/* An implementation of svn_io_walk_func_t. - * Note: LOCAL_ABSPATH is the path being crawled and can be on either side - * of the diff depending on baton->recursing_within_added_subtree. */ + +/* Handles reporting of a directory and its children for inner_dir_diff */ static svn_error_t * -arbitrary_diff_walker(void *baton, const char *local_abspath, - const apr_finfo_t *finfo, - apr_pool_t *scratch_pool) +do_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - struct arbitrary_diff_walker_baton *b = baton; + const char *relpath; + svn_diff_source_t *left_source; + svn_diff_source_t *right_source; + svn_boolean_t skip = FALSE; + svn_boolean_t skip_children = FALSE; + void *dir_baton; + apr_hash_t *left_props; + apr_hash_t *right_props; + + relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); + + if (! right_only) + { + left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); + } + else + { + left_source = NULL; + left_props = NULL; + } + + if (! left_only) + { + right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); + } + else + { + right_source = NULL; + right_props = NULL; + } - if (b->ctx->cancel_func) - SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); + SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, + relpath, + left_source, + right_source, + NULL /* copyfrom_source */, + parent_baton, + diff_processor, + scratch_pool, scratch_pool)); - if (finfo->filetype != APR_DIR) + if (!skip_children) + { + if (depth >= svn_depth_files) + SVN_ERR(inner_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + left_only, right_only, + left_before_right, depth, + dir_baton, + diff_processor, ctx, scratch_pool)); + } + else if (skip) return SVN_NO_ERROR; - SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity, - scratch_pool)); + if (left_props && right_props) + { + apr_array_header_t *prop_diffs; + + SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props, + scratch_pool)); + + if (prop_diffs->nelts) + { + SVN_ERR(diff_processor->dir_changed(relpath, + left_source, + right_source, + left_props, + right_props, + prop_diffs, + dir_baton, + diff_processor, + scratch_pool)); + return SVN_NO_ERROR; + } + } + + if (left_source && right_source) + { + SVN_ERR(diff_processor->dir_closed(relpath, + left_source, + right_source, + dir_baton, + diff_processor, + scratch_pool)); + } + else if (left_source) + { + SVN_ERR(diff_processor->dir_deleted(relpath, + left_source, + left_props, + dir_baton, + diff_processor, + scratch_pool)); + } + else + { + SVN_ERR(diff_processor->dir_added(relpath, + NULL /* copyfrom_source */, + right_source, + NULL /* copyfrom_props */, + right_props, + dir_baton, + diff_processor, + scratch_pool)); + } return SVN_NO_ERROR; } svn_error_t * -svn_client__arbitrary_nodes_diff(const char *local_abspath1, - const char *local_abspath2, +svn_client__arbitrary_nodes_diff(const char **root_relpath, + svn_boolean_t *root_is_dir, + const char *left_abspath, + const char *right_abspath, svn_depth_t depth, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_node_kind_t kind1; - svn_node_kind_t kind2; + svn_node_kind_t left_kind; + svn_node_kind_t right_kind; + const char *left_root_abspath; + const char *right_root_abspath; + svn_boolean_t left_before_right = TRUE; /* Future argument? */ - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); - SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); + if (depth == svn_depth_unknown) + depth = svn_depth_infinity; - if (kind1 != kind2) - return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, - _("'%s' is not the same node kind as '%s'"), - svn_dirent_local_style(local_abspath1, - scratch_pool), - svn_dirent_local_style(local_abspath2, - scratch_pool)); + SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool)); + SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool)); if (depth == svn_depth_unknown) depth = svn_depth_infinity; - if (kind1 == svn_node_file) - SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2, - svn_dirent_basename(local_abspath1, - scratch_pool), - FALSE, FALSE, NULL, - callbacks, diff_baton, - ctx, scratch_pool)); - else if (kind1 == svn_node_dir) - SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2, - NULL, NULL, depth, - callbacks, diff_baton, - ctx, scratch_pool)); + if (left_kind == svn_node_dir && right_kind == svn_node_dir) + { + left_root_abspath = left_abspath; + right_root_abspath = right_abspath; + + if (root_relpath) + *root_relpath = ""; + if (root_is_dir) + *root_is_dir = TRUE; + } + else + { + svn_dirent_split(&left_root_abspath, root_relpath, left_abspath, + scratch_pool); + right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool); + + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, *root_relpath); + if (root_is_dir) + *root_is_dir = FALSE; + } + + if (left_kind == svn_node_dir && right_kind == svn_node_dir) + { + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + else if (left_kind == svn_node_file && right_kind == svn_node_file) + { + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, + NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + else if (left_kind == svn_node_file || left_kind == svn_node_dir + || right_kind == svn_node_file || right_kind == svn_node_dir) + { + void *dir_baton; + svn_boolean_t skip = FALSE; + svn_boolean_t skip_children = FALSE; + svn_diff_source_t *left_src; + svn_diff_source_t *right_src; + + left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + + /* The root is replaced... */ + /* Report delete and/or add */ + + SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "", + left_src, + right_src, + NULL /* copyfrom_src */, + NULL, + diff_processor, + scratch_pool, scratch_pool)); + + if (skip) + return SVN_NO_ERROR; + else if (!skip_children) + { + if (left_before_right) + { + if (left_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (left_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + + if (right_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (right_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + + if (! left_before_right) + { + if (left_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (left_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + } + + SVN_ERR(diff_processor->dir_closed("", + left_src, + right_src, + dir_baton, + diff_processor, + scratch_pool)); + } else return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("'%s' is not a file or directory"), - kind1 == svn_node_none - ? svn_dirent_local_style(local_abspath1, - scratch_pool) - : svn_dirent_local_style(local_abspath2, - scratch_pool)); + svn_dirent_local_style( + (left_kind == svn_node_none) + ? left_abspath + : right_abspath, + scratch_pool)); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/diff_summarize.c b/contrib/subversion/subversion/libsvn_client/diff_summarize.c index df0911baf..9e258cdfc 100644 --- a/contrib/subversion/subversion/libsvn_client/diff_summarize.c +++ b/contrib/subversion/subversion/libsvn_client/diff_summarize.c @@ -25,32 +25,29 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" +#include "svn_path.h" #include "svn_props.h" #include "svn_pools.h" +#include "private/svn_wc_private.h" + #include "client.h" /* Diff callbacks baton. */ struct summarize_baton_t { + apr_pool_t *baton_pool; /* For allocating skip_path */ + /* The target path of the diff, relative to the anchor; "" if target == anchor. */ - const char *target; + const char *skip_relpath; /* The summarize callback passed down from the API */ svn_client_diff_summarize_func_t summarize_func; - /* Is the diff handling reversed? (add<->delete) */ - svn_boolean_t reversed; - /* The summarize callback baton */ void *summarize_func_baton; - - /* Which paths have a prop change. Key is a (const char *) path; the value - * is any non-null pointer to indicate that this path has a prop change. */ - apr_hash_t *prop_changes; }; - /* Call B->summarize_func with B->summarize_func_baton, passing it a * summary object composed from PATH (but made to be relative to the target * of the diff), SUMMARIZE_KIND, PROP_CHANGED (or FALSE if the action is an @@ -68,24 +65,9 @@ send_summary(struct summarize_baton_t *b, SVN_ERR_ASSERT(summarize_kind != svn_client_diff_summarize_kind_normal || prop_changed); - if (b->reversed) - { - switch(summarize_kind) - { - case svn_client_diff_summarize_kind_added: - summarize_kind = svn_client_diff_summarize_kind_deleted; - break; - case svn_client_diff_summarize_kind_deleted: - summarize_kind = svn_client_diff_summarize_kind_added; - break; - default: - break; - } - } - /* PATH is relative to the anchor of the diff, but SUM->path needs to be relative to the target of the diff. */ - sum->path = svn_relpath_skip_ancestor(b->target, path); + sum->path = svn_relpath_skip_ancestor(b->skip_relpath, path); sum->summarize_kind = summarize_kind; if (summarize_kind == svn_client_diff_summarize_kind_modified || summarize_kind == svn_client_diff_summarize_kind_normal) @@ -96,6 +78,29 @@ send_summary(struct summarize_baton_t *b, return SVN_NO_ERROR; } +/* Are there any changes to relevant (normal) props in PROPS? */ +static svn_boolean_t +props_changed_hash(apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + if (!props) + return FALSE; + + for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi)) + { + const char *name = apr_hash_this_key(hi); + + if (svn_property_kind2(name) == svn_prop_regular_kind) + { + return TRUE; + } + } + + return FALSE; +} + /* Are there any changes to relevant (normal) props in PROPCHANGES? */ static svn_boolean_t props_changed(const apr_array_header_t *propchanges, @@ -108,210 +113,201 @@ props_changed(const apr_array_header_t *propchanges, return (props->nelts != 0); } - +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_dir_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *path, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - struct summarize_baton_t *b = diff_baton; + /* struct summarize_baton_t *b = processor->baton; */ - SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted, - FALSE, svn_node_dir, scratch_pool)); + /* ### Send here instead of from dir_added() ? */ + /*if (!left_source) + { + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added, + FALSE, svn_node_dir, scratch_pool)); + }*/ return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_file_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *path, - const char *tmpfile1, - const char *tmpfile2, - const char *mimetype1, - const char *mimetype2, - apr_hash_t *originalprops, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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 summarize_baton_t *b = diff_baton; + struct summarize_baton_t *b = processor->baton; - SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted, - FALSE, svn_node_file, scratch_pool)); + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_normal, + TRUE, svn_node_dir, scratch_pool)); return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_dir_added(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *path, - svn_revnum_t rev, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - return SVN_NO_ERROR; -} + struct summarize_baton_t *b = processor->baton; + + /* ### Send from dir_opened without prop info? */ + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added, + props_changed_hash(right_props, scratch_pool), + svn_node_dir, scratch_pool)); -static svn_error_t * -cb_dir_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *path, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) -{ return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_dir_closed(svn_wc_notify_state_t *contentstate, - svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *path, - svn_boolean_t dir_was_added, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - struct summarize_baton_t *b = diff_baton; - svn_boolean_t prop_change; + struct summarize_baton_t *b = processor->baton; - if (! svn_relpath_skip_ancestor(b->target, path)) - return SVN_NO_ERROR; - - prop_change = svn_hash_gets(b->prop_changes, path) != NULL; - if (dir_was_added || prop_change) - SVN_ERR(send_summary(b, path, - dir_was_added ? svn_client_diff_summarize_kind_added - : svn_client_diff_summarize_kind_normal, - prop_change, svn_node_dir, scratch_pool)); + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_deleted, + FALSE, svn_node_dir, scratch_pool)); return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_file_added(svn_wc_notify_state_t *contentstate, - svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *path, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - const apr_array_header_t *propchanges, - apr_hash_t *originalprops, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - struct summarize_baton_t *b = diff_baton; + struct summarize_baton_t *b = processor->baton; - SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_added, - props_changed(propchanges, scratch_pool), + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added, + props_changed_hash(right_props, scratch_pool), svn_node_file, scratch_pool)); return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_file_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - const char *path, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - return SVN_NO_ERROR; -} + struct summarize_baton_t *b = processor->baton; -static svn_error_t * -cb_file_changed(svn_wc_notify_state_t *contentstate, - svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *path, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, - const apr_array_header_t *propchanges, - apr_hash_t *originalprops, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - struct summarize_baton_t *b = diff_baton; - svn_boolean_t text_change = (tmpfile2 != NULL); - svn_boolean_t prop_change = props_changed(propchanges, scratch_pool); - - if (text_change || prop_change) - SVN_ERR(send_summary(b, path, - text_change ? svn_client_diff_summarize_kind_modified + SVN_ERR(send_summary(b, relpath, + file_modified ? svn_client_diff_summarize_kind_modified : svn_client_diff_summarize_kind_normal, - prop_change, svn_node_file, scratch_pool)); + props_changed(prop_changes, scratch_pool), + svn_node_file, scratch_pool)); return SVN_NO_ERROR; } +/* svn_diff_tree_processor_t callback */ static svn_error_t * -cb_dir_props_changed(svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *path, - svn_boolean_t dir_was_added, - const apr_array_header_t *propchanges, - apr_hash_t *original_props, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_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) { - struct summarize_baton_t *b = diff_baton; + struct summarize_baton_t *b = processor->baton; - if (props_changed(propchanges, scratch_pool)) - svn_hash_sets(b->prop_changes, path, path); + SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_deleted, + FALSE, svn_node_file, scratch_pool)); return SVN_NO_ERROR; } svn_error_t * svn_client__get_diff_summarize_callbacks( - svn_wc_diff_callbacks4_t **callbacks, - void **callback_baton, - const char *target, - svn_boolean_t reversed, + const svn_diff_tree_processor_t **diff_processor, + const char ***p_root_relpath, svn_client_diff_summarize_func_t summarize_func, void *summarize_baton, - apr_pool_t *pool) + const char *original_target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc_diff_callbacks4_t *cb = apr_palloc(pool, sizeof(*cb)); - struct summarize_baton_t *b = apr_palloc(pool, sizeof(*b)); + svn_diff_tree_processor_t *dp; + struct summarize_baton_t *b = apr_pcalloc(result_pool, sizeof(*b)); - b->target = target; + b->baton_pool = result_pool; b->summarize_func = summarize_func; b->summarize_func_baton = summarize_baton; - b->prop_changes = apr_hash_make(pool); - b->reversed = reversed; - - cb->file_opened = cb_file_opened; - cb->file_changed = cb_file_changed; - cb->file_added = cb_file_added; - cb->file_deleted = cb_file_deleted; - cb->dir_deleted = cb_dir_deleted; - cb->dir_opened = cb_dir_opened; - cb->dir_added = cb_dir_added; - cb->dir_props_changed = cb_dir_props_changed; - cb->dir_closed = cb_dir_closed; - - *callbacks = cb; - *callback_baton = b; + + dp = svn_diff__tree_processor_create(b, result_pool); + + /*dp->file_opened = diff_file_opened;*/ + dp->file_added = diff_file_added; + dp->file_deleted = diff_file_deleted; + dp->file_changed = diff_file_changed; + + dp->dir_opened = diff_dir_opened; + dp->dir_changed = diff_dir_changed; + dp->dir_deleted = diff_dir_deleted; + dp->dir_added = diff_dir_added; + + *diff_processor = dp; + *p_root_relpath = &b->skip_relpath; return SVN_NO_ERROR; } + +svn_client_diff_summarize_t * +svn_client_diff_summarize_dup(const svn_client_diff_summarize_t *diff, + apr_pool_t *pool) +{ + svn_client_diff_summarize_t *dup_diff = apr_palloc(pool, sizeof(*dup_diff)); + + *dup_diff = *diff; + + if (diff->path) + dup_diff->path = apr_pstrdup(pool, diff->path); + + return dup_diff; +} diff --git a/contrib/subversion/subversion/libsvn_client/export.c b/contrib/subversion/subversion/libsvn_client/export.c index c14a5f0bc..63cd87a92 100644 --- a/contrib/subversion/subversion/libsvn_client/export.c +++ b/contrib/subversion/subversion/libsvn_client/export.c @@ -1274,8 +1274,8 @@ export_file(const char *from_path_or_url, * with information. */ for (hi = apr_hash_first(scratch_pool, 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); + const char *propname = apr_hash_this_key(hi); + const svn_string_t *propval = apr_hash_this_val(hi); SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool)); } @@ -1542,7 +1542,7 @@ svn_client_export5(svn_revnum_t *result_rev, hi; hi = apr_hash_next(hi)) { - const char *external_abspath = svn__apr_hash_index_key(hi); + const char *external_abspath = apr_hash_this_key(hi); const char *relpath; const char *target_abspath; @@ -1581,7 +1581,7 @@ svn_client_export5(svn_revnum_t *result_rev, = svn_wc_create_notify(to_path, svn_wc_notify_update_completed, pool); notify->revision = edit_revision; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } if (result_rev) diff --git a/contrib/subversion/subversion/libsvn_client/externals.c b/contrib/subversion/subversion/libsvn_client/externals.c index 52c236c60..851b260de 100644 --- a/contrib/subversion/subversion/libsvn_client/externals.c +++ b/contrib/subversion/subversion/libsvn_client/externals.c @@ -151,6 +151,7 @@ switch_dir_external(const char *local_abspath, const svn_opt_revision_t *revision, const char *defining_abspath, svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -170,7 +171,7 @@ switch_dir_external(const char *local_abspath, 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 @@ -186,7 +187,6 @@ switch_dir_external(const char *local_abspath, &repos_root_url, &repos_uuid, NULL, ctx->wc_ctx, local_abspath, TRUE, /* ignore_enoent */ - TRUE, /* show hidden */ pool, pool)); if (kind != svn_node_unknown) { @@ -231,17 +231,53 @@ switch_dir_external(const char *local_abspath, if (node_url) { + svn_boolean_t is_wcroot; + + SVN_ERR(svn_wc__is_wcroot(&is_wcroot, ctx->wc_ctx, local_abspath, + pool)); + + if (! is_wcroot) + { + /* This can't be a directory external! */ + + err = svn_wc__external_remove(ctx->wc_ctx, defining_abspath, + local_abspath, + TRUE /* declaration_only */, + ctx->cancel_func, ctx->cancel_baton, + pool); + + if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + { + /* New external... No problem that we can't remove it */ + svn_error_clear(err); + err = NULL; + } + else if (err) + return svn_error_trace(err); + + 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 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, + SVN_ERR(svn_client__update_internal(NULL, timestamp_sleep, + local_abspath, revision, svn_depth_unknown, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, - timestamp_sleep, - ctx, subpool)); + ra_session, ctx, subpool)); /* We just decided that this existing directory is an external, so update the external registry with this information, like @@ -372,9 +408,11 @@ switch_dir_external(const char *local_abspath, } /* ... Hello, new hotness. */ - SVN_ERR(svn_client__checkout_internal(NULL, url, local_abspath, peg_revision, + SVN_ERR(svn_client__checkout_internal(NULL, timestamp_sleep, + url, local_abspath, peg_revision, revision, svn_depth_infinity, - FALSE, FALSE, timestamp_sleep, + FALSE, FALSE, + ra_session, ctx, pool)); SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, @@ -401,14 +439,15 @@ switch_dir_external(const char *local_abspath, 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 +/* Try to update a file external at LOCAL_ABSPATH to SWITCH_LOC. This function + assumes caller has a write lock in CTX. 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 svn_client__pathrev_t *switch_loc, + const char *record_url, + const svn_opt_revision_t *record_peg_revision, + const svn_opt_revision_t *record_revision, const char *def_dir_abspath, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, @@ -485,7 +524,8 @@ switch_file_external(const char *local_abspath, 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)); + switch_loc->url, + svn_dirent_local_style(local_abspath, scratch_pool)); } } else @@ -507,28 +547,17 @@ switch_file_external(const char *local_abspath, 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); - - /* ### 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)); + const char *target = svn_dirent_basename(local_abspath, 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), + SVN_ERR(svn_ra_reparent(ra_session, + svn_uri_dirname(switch_loc->url, scratch_pool), scratch_pool)); SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton, @@ -542,9 +571,9 @@ switch_file_external(const char *local_abspath, use_commit_times, diff3_cmd, preserved_exts, def_dir_abspath, - url, peg_revision, revision, - ctx->conflict_func2, - ctx->conflict_baton2, + record_url, + record_peg_revision, + record_revision, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, @@ -578,7 +607,7 @@ switch_file_external(const char *local_abspath, = 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); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } } @@ -691,7 +720,7 @@ handle_external_item_removal(const svn_client_ctx_t *ctx, notify->kind = kind; notify->err = err; - (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) { @@ -701,7 +730,7 @@ handle_external_item_removal(const svn_client_ctx_t *ctx, notify->kind = svn_node_dir; notify->err = err; - (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } } @@ -722,10 +751,10 @@ handle_external_item_change(svn_client_ctx_t *ctx, const char *local_abspath, const char *old_defining_abspath, const svn_wc_external_item2_t *new_item, + svn_ra_session_t *ra_session, 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; @@ -746,12 +775,39 @@ handle_external_item_change(svn_client_ctx_t *ctx, 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)); + /* Get the RA connection, if needed. */ + if (ra_session) + { + svn_error_t *err = svn_ra_reparent(ra_session, new_url, scratch_pool); + + if (err) + { + if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) + { + svn_error_clear(err); + ra_session = NULL; + } + else + return svn_error_trace(err); + } + else + { + SVN_ERR(svn_client__resolve_rev_and_url(&new_loc, + ra_session, new_url, + &(new_item->peg_revision), + &(new_item->revision), ctx, + scratch_pool)); + + SVN_ERR(svn_ra_reparent(ra_session, new_loc->url, scratch_pool)); + } + } + + if (!ra_session) + 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)); @@ -775,7 +831,7 @@ handle_external_item_change(svn_client_ctx_t *ctx, /* First notify that we're about to handle an external. */ if (ctx->notify_func2) { - (*ctx->notify_func2)( + ctx->notify_func2( ctx->notify_baton2, svn_wc_create_notify(local_abspath, svn_wc_notify_update_external, @@ -800,7 +856,7 @@ handle_external_item_change(svn_client_ctx_t *ctx, &(new_item->peg_revision), &(new_item->revision), parent_dir_abspath, - timestamp_sleep, ctx, + timestamp_sleep, ra_session, ctx, scratch_pool)); break; case svn_node_file: @@ -858,6 +914,7 @@ handle_external_item_change(svn_client_ctx_t *ctx, } SVN_ERR(switch_file_external(local_abspath, + new_loc, new_url, &new_item->peg_revision, &new_item->revision, @@ -908,6 +965,7 @@ handle_externals_change(svn_client_ctx_t *ctx, apr_hash_t *old_externals, svn_depth_t ambient_depth, svn_depth_t requested_depth, + svn_ra_session_t *ra_session, apr_pool_t *scratch_pool) { apr_array_header_t *new_desc; @@ -976,7 +1034,7 @@ handle_externals_change(svn_client_ctx_t *ctx, local_abspath, url, target_abspath, old_defining_abspath, - new_item, + new_item, ra_session, timestamp_sleep, iterpool), iterpool)); @@ -999,6 +1057,7 @@ svn_client__handle_externals(apr_hash_t *externals_new, const char *target_abspath, svn_depth_t requested_depth, svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { @@ -1018,8 +1077,8 @@ svn_client__handle_externals(apr_hash_t *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); + const char *local_abspath = apr_hash_this_key(hi); + const char *desc_text = apr_hash_this_val(hi); svn_depth_t ambient_depth = svn_depth_infinity; svn_pool_clear(iterpool); @@ -1029,7 +1088,7 @@ svn_client__handle_externals(apr_hash_t *externals_new, const char *ambient_depth_w; ambient_depth_w = apr_hash_get(ambient_depths, local_abspath, - svn__apr_hash_index_klen(hi)); + apr_hash_this_key_len(hi)); if (ambient_depth_w == NULL) { @@ -1048,7 +1107,7 @@ svn_client__handle_externals(apr_hash_t *externals_new, local_abspath, desc_text, old_external_defs, ambient_depth, requested_depth, - iterpool)); + ra_session, iterpool)); } /* Remove the remaining externals */ @@ -1056,8 +1115,8 @@ svn_client__handle_externals(apr_hash_t *externals_new, 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 *item_abspath = apr_hash_this_key(hi); + const char *defining_abspath = apr_hash_this_val(hi); const char *parent_abspath; svn_pool_clear(iterpool); @@ -1131,8 +1190,8 @@ svn_client__export_externals(apr_hash_t *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_abspath = apr_hash_this_key(hi); + const char *desc_text = apr_hash_this_val(hi); const char *local_relpath; const char *dir_url; apr_array_header_t *items; @@ -1188,6 +1247,17 @@ svn_client__export_externals(apr_hash_t *externals, sub_iterpool), sub_iterpool)); + /* First notify that we're about to handle an external. */ + if (ctx->notify_func2) + { + ctx->notify_func2( + ctx->notify_baton2, + svn_wc_create_notify(item_abspath, + svn_wc_notify_update_external, + sub_iterpool), + sub_iterpool); + } + SVN_ERR(wrap_external_error( ctx, item_abspath, svn_client_export5(NULL, new_url, item_abspath, diff --git a/contrib/subversion/subversion/libsvn_client/import.c b/contrib/subversion/subversion/libsvn_client/import.c index 43e0d79d2..b5ee0776d 100644 --- a/contrib/subversion/subversion/libsvn_client/import.c +++ b/contrib/subversion/subversion/libsvn_client/import.c @@ -47,25 +47,15 @@ #include "svn_props.h" #include "client.h" -#include "private/svn_subr_private.h" #include "private/svn_ra_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" #include "private/svn_magic.h" #include "svn_private_config.h" -/* Import context baton. - - ### TODO: Add the following items to this baton: - /` import editor/baton. `/ - const svn_delta_editor_t *editor; - void *edit_baton; +/* Import context baton. */ - /` Client context baton `/ - svn_client_ctx_t `ctx; - - /` Paths (keys) excluded from the import (values ignored) `/ - apr_hash_t *excludes; -*/ typedef struct import_ctx_t { /* Whether any changes were made to the repository */ @@ -238,8 +228,8 @@ import_file(const svn_delta_editor_t *editor, { for (hi = apr_hash_first(pool, properties); 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); + const char *pname = apr_hash_this_key(hi); + const svn_string_t *pval = apr_hash_this_val(hi); SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool)); } @@ -255,7 +245,7 @@ import_file(const svn_delta_editor_t *editor, notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } /* If this is a special file, we need to set the svn:special @@ -279,7 +269,7 @@ import_file(const svn_delta_editor_t *editor, text_checksum = svn_checksum_to_cstring(svn_checksum__from_digest_md5(digest, pool), pool); - return editor->close_file(file_baton, text_checksum, pool); + return svn_error_trace(editor->close_file(file_baton, text_checksum, pool)); } @@ -312,8 +302,8 @@ get_filtered_children(apr_hash_t **children, for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - const char *base_name = svn__apr_hash_index_key(hi); - const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); + const char *base_name = apr_hash_this_key(hi); + const svn_io_dirent2_t *dirent = apr_hash_this_val(hi); const char *local_abspath; svn_pool_clear(iterpool); @@ -338,7 +328,7 @@ get_filtered_children(apr_hash_t **children, notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; - (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool); + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); } svn_hash_sets(dirents, base_name, NULL); @@ -480,7 +470,7 @@ import_children(const char *dir_abspath, notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; - (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool); + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); } } else @@ -569,7 +559,7 @@ import_dir(const svn_delta_editor_t *editor, notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } } @@ -590,9 +580,15 @@ import_dir(const svn_delta_editor_t *editor, /* Recursively import PATH to a repository using EDITOR and * EDIT_BATON. PATH can be a file or directory. * + * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by + * a successfull commit, otherwise to FALSE. + * * DEPTH is the depth at which to import PATH; it behaves as for * svn_client_import4(). * + * BASE_REV is the revision to use for the root of the commit. We + * checked the preconditions against this revision. + * * NEW_ENTRIES is an ordered array of path components that must be * created in the repository (where the ordering direction is * parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty @@ -636,11 +632,14 @@ import_dir(const svn_delta_editor_t *editor, * not necessarily the root.) */ static svn_error_t * -import(const char *local_abspath, +import(svn_boolean_t *updated_repository, + const char *local_abspath, + const char *url, const apr_array_header_t *new_entries, const svn_delta_editor_t *editor, void *edit_baton, svn_depth_t depth, + svn_revnum_t base_rev, apr_hash_t *excludes, apr_hash_t *autoprops, apr_array_header_t *local_ignores, @@ -656,17 +655,17 @@ import(const char *local_abspath, void *root_baton; apr_array_header_t *batons = NULL; const char *edit_path = ""; - import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx)); + import_ctx_t import_ctx = { FALSE }; const svn_io_dirent2_t *dirent; - import_ctx->autoprops = autoprops; - svn_magic__init(&import_ctx->magic_cookie, pool); + *updated_repository = FALSE; - /* Get a root dir baton. We pass an invalid revnum to open_root - to mean "base this on the youngest revision". Should we have an - SVN_YOUNGEST_REVNUM defined for these purposes? */ - SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, - pool, &root_baton)); + import_ctx.autoprops = autoprops; + SVN_ERR(svn_magic__init(&import_ctx.magic_cookie, ctx->config, pool)); + + /* Get a root dir baton. We pass the revnum we used for testing our + assumptions and obtaining inherited properties. */ + SVN_ERR(editor->open_root(edit_baton, base_rev, pool, &root_baton)); /* Import a file or a directory tree. */ SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE, @@ -697,7 +696,7 @@ import(const char *local_abspath, pool, &root_baton)); /* Remember that the repository was modified */ - import_ctx->repos_changed = TRUE; + import_ctx.repos_changed = TRUE; } } else if (dirent->kind == svn_node_file) @@ -728,7 +727,7 @@ import(const char *local_abspath, if (!ignores_match) SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path, - dirent, import_ctx, ctx, pool)); + dirent, &import_ctx, ctx, pool)); } else if (dirent->kind == svn_node_dir) { @@ -748,7 +747,7 @@ import(const char *local_abspath, root_baton, depth, excludes, global_ignores, no_ignore, no_autoprops, ignore_unknown_node_types, filter_callback, - filter_baton, import_ctx, ctx, pool)); + filter_baton, &import_ctx, ctx, pool)); } else if (dirent->kind == svn_node_none @@ -770,10 +769,23 @@ import(const char *local_abspath, } } - if (import_ctx->repos_changed) - return editor->close_edit(edit_baton, pool); - else - return editor->abort_edit(edit_baton, pool); + if (import_ctx.repos_changed) + { + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(url, + svn_wc_notify_commit_finalizing, + pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } + + SVN_ERR(editor->close_edit(edit_baton, pool)); + + *updated_repository = TRUE; + } + + return SVN_NO_ERROR; } @@ -809,6 +821,10 @@ svn_client_import5(const char *path, apr_hash_t *autoprops = NULL; apr_array_header_t *global_ignores; apr_array_header_t *local_ignores_arr; + svn_revnum_t base_rev; + apr_array_header_t *inherited_props = NULL; + apr_hash_t *url_props = NULL; + svn_boolean_t updated_repository; if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, @@ -816,6 +832,8 @@ svn_client_import5(const char *path, SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); + SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); + /* Create a new commit item and add it to the array. */ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) { @@ -828,7 +846,9 @@ svn_client_import5(const char *path, = apr_array_make(scratch_pool, 1, sizeof(item)); item = svn_client_commit_item3_create(scratch_pool); - item->path = apr_pstrdup(scratch_pool, path); + item->path = local_abspath; + item->url = url; + item->kind = kind; item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; @@ -844,15 +864,14 @@ svn_client_import5(const char *path, } } - SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); - SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL, ctx, scratch_pool, iterpool)); + SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool)); + /* Figure out all the path components we need to create just to have a place to stick our imported tree. */ - SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, - iterpool)); + SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool)); /* We can import into directories, but if a file already exists, that's an error. */ @@ -871,8 +890,7 @@ svn_client_import5(const char *path, APR_ARRAY_PUSH(new_entries, const char *) = dir; SVN_ERR(svn_ra_reparent(ra_session, url, iterpool)); - SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, - iterpool)); + SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool)); } /* Reverse the order of the components we added to our NEW_ENTRIES array. */ @@ -895,6 +913,17 @@ svn_client_import5(const char *path, SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, log_msg, ctx, scratch_pool)); + /* Obtain properties before opening the commit editor, as at that point we are + not allowed to use the existing ra-session */ + if (! no_ignore /*|| ! no_autoprops*/) + { + SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "", + base_rev, SVN_DIRENT_KIND, scratch_pool)); + + SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev, + scratch_pool, iterpool)); + } + /* Fetch RA commit editor. */ SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, svn_client__get_shim_callbacks(ctx->wc_ctx, @@ -907,8 +936,13 @@ svn_client_import5(const char *path, /* Get inherited svn:auto-props, svn:global-ignores, and svn:ignores for the location we are importing to. */ if (!no_autoprops) - SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx, - scratch_pool, iterpool)); + { + /* ### This should use inherited_props and url_props to avoid creating + another ra session to obtain the same values, but using a possibly + different HEAD revision */ + SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx, + scratch_pool, iterpool)); + } if (no_ignore) { global_ignores = NULL; @@ -916,49 +950,62 @@ svn_client_import5(const char *path, } else { - svn_opt_revision_t rev; apr_array_header_t *config_ignores; - apr_hash_t *local_ignores_hash; + svn_string_t *val; + int i; + + global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *)); - SVN_ERR(svn_client__get_inherited_ignores(&global_ignores, url, ctx, - scratch_pool, iterpool)); SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config, scratch_pool)); global_ignores = apr_array_append(scratch_pool, global_ignores, config_ignores); - rev.kind = svn_opt_revision_head; - SVN_ERR(svn_client_propget5(&local_ignores_hash, NULL, SVN_PROP_IGNORE, url, - &rev, &rev, NULL, svn_depth_empty, NULL, ctx, - scratch_pool, scratch_pool)); + val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES); + if (val) + svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ", + FALSE, scratch_pool); + + for (i = 0; i < inherited_props->nelts; i++) + { + svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( + inherited_props, i, svn_prop_inherited_item_t *); + + val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES); + + if (val) + svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ", + FALSE, scratch_pool); + } local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *)); - if (apr_hash_count(local_ignores_hash)) + val = svn_hash_gets(url_props, SVN_PROP_IGNORE); + + if (val) { - svn_string_t *propval = svn_hash_gets(local_ignores_hash, url); - if (propval) - { - svn_cstring_split_append(local_ignores_arr, propval->data, - "\n\r\t\v ", FALSE, scratch_pool); - } + svn_cstring_split_append(local_ignores_arr, val->data, + "\n\r\t\v ", FALSE, scratch_pool); } } - /* If an error occurred during the commit, abort the edit and return - the error. We don't even care if the abort itself fails. */ - if ((err = import(local_abspath, new_entries, editor, edit_baton, - depth, excludes, autoprops, local_ignores_arr, - global_ignores, no_ignore, no_autoprops, - ignore_unknown_node_types, filter_callback, - filter_baton, ctx, iterpool))) + /* If an error occurred during the commit, properly abort the edit. */ + err = svn_error_trace(import(&updated_repository, + local_abspath, url, new_entries, editor, + edit_baton, depth, base_rev, excludes, + autoprops, local_ignores_arr, global_ignores, + no_ignore, no_autoprops, + ignore_unknown_node_types, filter_callback, + filter_baton, ctx, iterpool)); + + svn_pool_destroy(iterpool); + + if (err || !updated_repository) { return svn_error_compose_create( err, - editor->abort_edit(edit_baton, iterpool)); + editor->abort_edit(edit_baton, scratch_pool)); } - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/info.c b/contrib/subversion/subversion/libsvn_client/info.c index f49f22e8e..39b5eb112 100644 --- a/contrib/subversion/subversion/libsvn_client/info.c +++ b/contrib/subversion/subversion/libsvn_client/info.c @@ -27,14 +27,16 @@ #include "client.h" #include "svn_client.h" -#include "svn_pools.h" #include "svn_dirent_uri.h" -#include "svn_path.h" #include "svn_hash.h" +#include "svn_pools.h" +#include "svn_sorts.h" + #include "svn_wc.h" #include "svn_private_config.h" #include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" @@ -59,6 +61,78 @@ svn_client_info2_dup(const svn_client_info2_t *info, return new_info; } +/* Handle externals for svn_client_info4() */ + +static svn_error_t * +do_external_info(apr_hash_t *external_map, + svn_depth_t depth, + svn_boolean_t fetch_excluded, + svn_boolean_t fetch_actual_only, + const apr_array_header_t *changelists, + svn_client_info_receiver2_t receiver, + void *receiver_baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_array_header_t *externals; + int i; + + externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically, + scratch_pool); + + /* Loop over the hash of new values (we don't care about the old + ones). This is a mapping of versioned directories to property + values. */ + for (i = 0; i < externals->nelts; i++) + { + svn_node_kind_t external_kind; + svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t); + const char *local_abspath = item.key; + const char *defining_abspath = item.value; + svn_opt_revision_t opt_rev; + svn_node_kind_t kind; + + svn_pool_clear(iterpool); + + /* Obtain information on the expected external. */ + SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, + &opt_rev.value.number, + ctx->wc_ctx, defining_abspath, + local_abspath, FALSE, + iterpool, iterpool)); + + if (external_kind != svn_node_dir) + continue; + + SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool)); + if (kind != svn_node_dir) + continue; + + /* Tell the client we're starting an external info. */ + if (ctx->notify_func2) + ctx->notify_func2( + ctx->notify_baton2, + svn_wc_create_notify(local_abspath, + svn_wc_notify_info_external, + iterpool), iterpool); + + SVN_ERR(svn_client_info4(local_abspath, + NULL /* peg_revision */, + NULL /* revision */, + depth, + fetch_excluded, + fetch_actual_only, + TRUE /* include_externals */, + changelists, + receiver, receiver_baton, + ctx, iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + /* Set *INFO to a new info struct built from DIRENT and (possibly NULL) svn_lock_t LOCK, all allocated in POOL. Pointer fields are copied by reference, not dup'd. */ @@ -129,8 +203,8 @@ push_dir_info(svn_ra_session_t *ra_session, const char *path, *fs_path; svn_lock_t *lock; svn_client_info2_t *info; - const char *name = svn__apr_hash_index_key(hi); - svn_dirent_t *the_ent = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_dirent_t *the_ent = apr_hash_this_val(hi); svn_client__pathrev_t *child_pathrev; svn_pool_clear(subpool); @@ -249,12 +323,13 @@ wc_info_receiver(void *baton, } svn_error_t * -svn_client_info3(const char *abspath_or_url, +svn_client_info4(const char *abspath_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t fetch_excluded, svn_boolean_t fetch_actual_only, + svn_boolean_t include_externals, const apr_array_header_t *changelists, svn_client_info_receiver2_t receiver, void *receiver_baton, @@ -283,11 +358,26 @@ svn_client_info3(const char *abspath_or_url, b.client_receiver_func = receiver; b.client_receiver_baton = receiver_baton; - return svn_error_trace( - svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth, - fetch_excluded, fetch_actual_only, changelists, - wc_info_receiver, &b, - ctx->cancel_func, ctx->cancel_baton, pool)); + SVN_ERR(svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth, + fetch_excluded, fetch_actual_only, changelists, + wc_info_receiver, &b, + ctx->cancel_func, ctx->cancel_baton, pool)); + + if (include_externals && SVN_DEPTH_IS_RECURSIVE(depth)) + { + apr_hash_t *external_map; + + SVN_ERR(svn_wc__externals_defined_below(&external_map, + ctx->wc_ctx, abspath_or_url, + pool, pool)); + + SVN_ERR(do_external_info(external_map, + depth, fetch_excluded, fetch_actual_only, + changelists, + receiver, receiver_baton, ctx, pool)); + } + + return SVN_NO_ERROR; } /* Go repository digging instead. */ @@ -298,12 +388,10 @@ svn_client_info3(const char *abspath_or_url, SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, abspath_or_url, NULL, peg_revision, revision, ctx, pool)); - - svn_uri_split(NULL, &base_name, pathrev->url, pool); + base_name = svn_uri_basename(pathrev->url, pool); /* Get the dirent for the URL itself. */ - SVN_ERR(svn_client__ra_stat_compatible(ra_session, pathrev->rev, &the_ent, - DIRENT_FIELDS, ctx, pool)); + SVN_ERR(svn_ra_stat(ra_session, "", pathrev->rev, &the_ent, pool)); if (! the_ent) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, @@ -353,9 +441,7 @@ svn_client_info3(const char *abspath_or_url, pool); /* Catch specific errors thrown by old mod_dav_svn or svnserve. */ - if (err && - (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED - || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) + if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); locks = apr_hash_make(pool); /* use an empty hash */ diff --git a/contrib/subversion/subversion/libsvn_client/iprops.c b/contrib/subversion/subversion/libsvn_client/iprops.c index 653ce8cfb..9d725bf42 100644 --- a/contrib/subversion/subversion/libsvn_client/iprops.c +++ b/contrib/subversion/subversion/libsvn_client/iprops.c @@ -176,8 +176,8 @@ get_inheritable_props(apr_hash_t **wcroot_iprops, hi; hi = apr_hash_next(hi)) { - const char *child_abspath = svn__apr_hash_index_key(hi); - const char *child_repos_relpath = svn__apr_hash_index_val(hi); + const char *child_abspath = apr_hash_this_key(hi); + const char *child_repos_relpath = apr_hash_this_val(hi); const char *url; apr_array_header_t *inherited_props; svn_error_t *err; @@ -244,6 +244,8 @@ svn_client__get_inheritable_props(apr_hash_t **wcroot_iprops, const char *old_session_url; svn_error_t *err; + *wcroot_iprops = NULL; + if (!SVN_IS_VALID_REVNUM(revision)) return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_client/libsvn_client.pc.in b/contrib/subversion/subversion/libsvn_client/libsvn_client.pc.in new file mode 100644 index 000000000..7cc786564 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_client/libsvn_client.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_client +Description: Subversion Client Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_wc libsvn_ra libsvn_delta libsvn_diff libsvn_subr +Libs: -L${libdir} -lsvn_client +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_client/list.c b/contrib/subversion/subversion/libsvn_client/list.c index 4093893f0..c29b8bd68 100644 --- a/contrib/subversion/subversion/libsvn_client/list.c +++ b/contrib/subversion/subversion/libsvn_client/list.c @@ -34,6 +34,7 @@ #include "private/svn_fspath.h" #include "private/svn_ra_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -128,6 +129,10 @@ get_dir_contents(apr_uint32_t dirent_fields, } SVN_ERR(err); + /* Locks will often be empty. Prevent pointless lookups in that case. */ + if (locks && apr_hash_count(locks) == 0) + locks = NULL; + /* Filter out svn:externals from all properties hash. */ if (prop_hash) prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS); @@ -187,114 +192,6 @@ get_dir_contents(apr_uint32_t dirent_fields, return SVN_NO_ERROR; } -/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */ -/* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */ -svn_error_t * -svn_client__ra_stat_compatible(svn_ra_session_t *ra_session, - svn_revnum_t rev, - svn_dirent_t **dirent_p, - apr_uint32_t dirent_fields, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - svn_error_t *err; - - err = svn_ra_stat(ra_session, "", rev, dirent_p, pool); - - /* svnserve before 1.2 doesn't support the above, so fall back on - a less efficient method. */ - if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) - { - const char *repos_root_url; - const char *session_url; - svn_node_kind_t kind; - svn_dirent_t *dirent; - - svn_error_clear(err); - - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); - SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool)); - - SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool)); - - if (kind != svn_node_none) - { - if (strcmp(session_url, repos_root_url) != 0) - { - svn_ra_session_t *parent_session; - apr_hash_t *parent_ents; - const char *parent_url, *base_name; - apr_pool_t *subpool = svn_pool_create(pool); - - /* Open another session to the path's parent. This server - doesn't support svn_ra_reparent anyway, so don't try it. */ - svn_uri_split(&parent_url, &base_name, session_url, subpool); - - SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url, - NULL, ctx, - subpool, subpool)); - - /* Get all parent's entries, no props. */ - SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL, - NULL, "", rev, dirent_fields, subpool)); - - /* Get the relevant entry. */ - dirent = svn_hash_gets(parent_ents, base_name); - - if (dirent) - *dirent_p = svn_dirent_dup(dirent, pool); - else - *dirent_p = NULL; - - svn_pool_destroy(subpool); /* Close RA session */ - } - else - { - /* We can't get the directory entry for the repository root, - but we can still get the information we want. - The created-rev of the repository root must, by definition, - be rev. */ - dirent = apr_palloc(pool, sizeof(*dirent)); - dirent->kind = kind; - dirent->size = SVN_INVALID_FILESIZE; - if (dirent_fields & SVN_DIRENT_HAS_PROPS) - { - apr_hash_t *props; - SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props, - "", rev, 0 /* no dirent fields */, - pool)); - dirent->has_props = (apr_hash_count(props) != 0); - } - dirent->created_rev = rev; - if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR)) - { - apr_hash_t *props; - svn_string_t *val; - - SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props, - pool)); - val = svn_hash_gets(props, SVN_PROP_REVISION_DATE); - if (val) - SVN_ERR(svn_time_from_cstring(&dirent->time, val->data, - pool)); - else - dirent->time = 0; - - val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR); - dirent->last_author = val ? val->data : NULL; - } - - *dirent_p = dirent; - } - } - else - *dirent_p = NULL; - } - else - SVN_ERR(err); - - return SVN_NO_ERROR; -} /* List the file/directory entries for PATH_OR_URL at REVISION. The actual node revision selected is determined by the path as @@ -369,8 +266,7 @@ list_internal(const char *path_or_url, fs_path = svn_client__pathrev_fspath(loc, pool); - SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent, - dirent_fields, ctx, pool)); + SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool)); if (! dirent) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("URL '%s' non-existent in revision %ld"), @@ -530,8 +426,8 @@ list_externals(apr_hash_t *externals, hi; hi = apr_hash_next(hi)) { - const char *externals_parent_url = svn__apr_hash_index_key(hi); - svn_string_t *externals_desc = svn__apr_hash_index_val(hi); + const char *externals_parent_url = apr_hash_this_key(hi); + svn_string_t *externals_desc = apr_hash_this_val(hi); apr_array_header_t *external_items; svn_pool_clear(iterpool); diff --git a/contrib/subversion/subversion/libsvn_client/locking_commands.c b/contrib/subversion/subversion/libsvn_client/locking_commands.c index c768503ef..2e0164fa7 100644 --- a/contrib/subversion/subversion/libsvn_client/locking_commands.c +++ b/contrib/subversion/subversion/libsvn_client/locking_commands.c @@ -46,7 +46,8 @@ struct lock_baton { const char *base_dir_abspath; - apr_hash_t *urls_to_paths; + apr_hash_t *urls_to_paths; /* url -> abspath */ + const char *base_url; svn_client_ctx_t *ctx; apr_pool_t *pool; }; @@ -56,7 +57,7 @@ struct lock_baton * BATON is a 'struct lock_baton *', PATH is the path being locked, * and LOCK is the lock itself. * - * If BATON->base_dir_abspath is not null, then this function either + * If BATON->urls_to_paths is not null, then this function either * stores the LOCK on REL_URL or removes any lock tokens from REL_URL * (depending on whether DO_LOCK is true or false respectively), but * only if RA_ERR is null, or (in the unlock case) is something other @@ -73,9 +74,12 @@ store_locks_callback(void *baton, { struct lock_baton *lb = baton; svn_wc_notify_t *notify; + const char *local_abspath = lb->urls_to_paths + ? svn_hash_gets(lb->urls_to_paths, rel_url) + : NULL; /* Create the notify struct first, so we can tweak it below. */ - notify = svn_wc_create_notify(rel_url, + notify = svn_wc_create_notify(local_abspath ? local_abspath : rel_url, do_lock ? (ra_err ? svn_wc_notify_failed_lock @@ -87,20 +91,14 @@ store_locks_callback(void *baton, notify->lock = lock; notify->err = ra_err; - if (lb->base_dir_abspath) + if (local_abspath) { - char *path = svn_hash_gets(lb->urls_to_paths, rel_url); - const char *local_abspath; - - local_abspath = svn_dirent_join(lb->base_dir_abspath, path, pool); - /* Notify a valid working copy path */ - notify->path = local_abspath; notify->path_prefix = lb->base_dir_abspath; if (do_lock) { - if (!ra_err) + if (!ra_err && lock) { SVN_ERR(svn_wc_add_lock2(lb->ctx->wc_ctx, local_abspath, lock, lb->pool)); @@ -112,12 +110,14 @@ store_locks_callback(void *baton, else /* unlocking */ { /* Remove our wc lock token either a) if we got no error, or b) if - we got any error except for owner mismatch. Note that the only - errors that are handed to this callback will be locking-related - errors. */ + we got any error except for owner mismatch or hook failure (the + hook would be pre-unlock rather than post-unlock). Note that the + only errors that are handed to this callback will be + locking-related errors. */ if (!ra_err || - (ra_err && (ra_err->apr_err != SVN_ERR_FS_LOCK_OWNER_MISMATCH))) + (ra_err && (ra_err->apr_err != SVN_ERR_FS_LOCK_OWNER_MISMATCH + && ra_err->apr_err != SVN_ERR_REPOS_HOOK_FAILURE))) { SVN_ERR(svn_wc_remove_lock2(lb->ctx->wc_ctx, local_abspath, lb->pool)); @@ -128,7 +128,7 @@ store_locks_callback(void *baton, } } else - notify->url = rel_url; /* Notify that path is actually a url */ + notify->url = svn_path_url_add_component2(lb->base_url, rel_url, pool); if (lb->ctx->notify_func2) lb->ctx->notify_func2(lb->ctx->notify_baton2, notify, pool); @@ -195,9 +195,15 @@ struct wc_lock_item_t { svn_revnum_t revision; const char *lock_token; + const char *url; }; -/* Set *COMMON_PARENT_URL to the nearest common parent URL of all TARGETS. +/* + * Sets LOCK_PATHS to an array of working copy paths that this function + * has obtained lock on. The caller is responsible to release the locks + * EVEN WHEN THIS FUNCTION RETURNS AN ERROR. + * + * Set *COMMON_PARENT_URL to the nearest common parent URL of all TARGETS. * If TARGETS are local paths, then the entry for each path is examined * and *COMMON_PARENT is set to the common parent URL for all the * targets (as opposed to the common local path). @@ -217,8 +223,7 @@ struct wc_lock_item_t * * If TARGETS is an array of urls, REL_FS_PATHS_P is set to NULL. * Otherwise each key in REL_FS_PATHS_P is an repository path (relative to - * COMMON_PARENT) mapped to the target path for TARGET (relative to - * the common parent WC path). working copy targets that they "belong" to. + * COMMON_PARENT) mapped to the absolute path for TARGET. * * If *COMMON_PARENT is a URL, then the values are a pointer to * SVN_INVALID_REVNUM (allocated in pool) if DO_LOCK, else "". @@ -226,8 +231,9 @@ struct wc_lock_item_t * TARGETS may not be empty. */ static svn_error_t * -organize_lock_targets(const char **common_parent_url, - const char **base_dir, +organize_lock_targets(apr_array_header_t **lock_paths, + const char **common_parent_url, + const char **base_dir_abspath, apr_hash_t **rel_targets_p, apr_hash_t **rel_fs_paths_p, const apr_array_header_t *targets, @@ -238,13 +244,12 @@ organize_lock_targets(const char **common_parent_url, apr_pool_t *scratch_pool) { const char *common_url = NULL; - const char *common_dirent = NULL; apr_hash_t *rel_targets_ret = apr_hash_make(result_pool); apr_hash_t *rel_fs_paths = NULL; - apr_array_header_t *rel_targets; apr_hash_t *wc_info = apr_hash_make(scratch_pool); svn_boolean_t url_mode; - int i; + + *lock_paths = NULL; SVN_ERR_ASSERT(targets->nelts); SVN_ERR(svn_client__assert_homogeneous_target_type(targets)); @@ -253,6 +258,8 @@ organize_lock_targets(const char **common_parent_url, if (url_mode) { + apr_array_header_t *rel_targets; + int i; svn_revnum_t *invalid_revnum = apr_palloc(result_pool, sizeof(*invalid_revnum)); @@ -281,58 +288,108 @@ organize_lock_targets(const char **common_parent_url, else { apr_array_header_t *rel_urls, *target_urls; + apr_hash_t *wcroot_target = apr_hash_make(scratch_pool); apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + int i; - /* Get the common parent dirent and a bunch of relpaths, one per - target. */ - SVN_ERR(condense_targets(&common_dirent, &rel_targets, targets, - FALSE, TRUE, result_pool, scratch_pool)); - if (! (common_dirent && *common_dirent)) - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, - _("No common parent found, unable to operate " - "on disjoint arguments")); + *lock_paths = apr_array_make(result_pool, 1, sizeof(const char *)); - /* Get the URL for each target (which also serves to verify that - the dirent targets are sane). */ - target_urls = apr_array_make(scratch_pool, rel_targets->nelts, - sizeof(const char *)); - for (i = 0; i < rel_targets->nelts; i++) + for (i = 0; i < targets->nelts; i++) { - const char *rel_target; - const char *repos_relpath; - const char *repos_root_url; - const char *target_url; - struct wc_lock_item_t *wli; - const char *local_abspath; - svn_node_kind_t kind; + const char *target_abspath; + const char *wcroot_abspath; + apr_array_header_t *wc_targets; svn_pool_clear(iterpool); - rel_target = APR_ARRAY_IDX(rel_targets, i, const char *); - local_abspath = svn_dirent_join(common_dirent, rel_target, scratch_pool); - wli = apr_pcalloc(scratch_pool, sizeof(*wli)); + SVN_ERR(svn_dirent_get_absolute(&target_abspath, + APR_ARRAY_IDX(targets, i, const char*), + result_pool)); - SVN_ERR(svn_wc__node_get_base(&kind, &wli->revision, &repos_relpath, - &repos_root_url, NULL, - &wli->lock_token, - wc_ctx, local_abspath, - FALSE /* ignore_enoent */, - FALSE /* show_hidden */, - result_pool, iterpool)); + SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, target_abspath, + iterpool, iterpool)); - if (kind != svn_node_file) - return svn_error_createf(SVN_ERR_WC_NOT_FILE, NULL, - _("The node '%s' is not a file"), - svn_dirent_local_style(local_abspath, - iterpool)); + wc_targets = svn_hash_gets(wcroot_target, wcroot_abspath); - svn_hash_sets(wc_info, local_abspath, wli); + if (!wc_targets) + { + wc_targets = apr_array_make(scratch_pool, 1, sizeof(const char*)); + svn_hash_sets(wcroot_target, apr_pstrdup(scratch_pool, wcroot_abspath), + wc_targets); + } - target_url = svn_path_url_add_component2(repos_root_url, - repos_relpath, - scratch_pool); + APR_ARRAY_PUSH(wc_targets, const char *) = target_abspath; + } - APR_ARRAY_PUSH(target_urls, const char *) = target_url; + for (hi = apr_hash_first(scratch_pool, wcroot_target); + hi; + hi = apr_hash_next(hi)) + { + const char *lock_abspath; + apr_array_header_t *paths = apr_hash_this_val(hi); + + /* Use parent dir of a single file target */ + if (paths->nelts == 1) + lock_abspath = svn_dirent_dirname( + APR_ARRAY_IDX(paths, 0, const char *), + result_pool); + else + SVN_ERR(svn_dirent_condense_targets(&lock_abspath, NULL, paths, + FALSE, result_pool, + scratch_pool)); + + SVN_ERR(svn_wc__acquire_write_lock(&lock_abspath, + wc_ctx, lock_abspath, FALSE, + result_pool, scratch_pool)); + + APR_ARRAY_PUSH(*lock_paths, const char *) = lock_abspath; + } + + /* Get the URL for each target (which also serves to verify that + the dirent targets are sane). */ + target_urls = apr_array_make(scratch_pool, targets->nelts, + sizeof(const char *)); + for (hi = apr_hash_first(scratch_pool, wcroot_target); + hi; + hi = apr_hash_next(hi)) + { + apr_array_header_t *wc_targets = apr_hash_this_val(hi); + + for (i = 0; i < wc_targets->nelts; i++) + { + const char *repos_relpath; + const char *repos_root_url; + struct wc_lock_item_t *wli; + const char *local_abspath; + svn_node_kind_t kind; + + svn_pool_clear(iterpool); + + local_abspath = APR_ARRAY_IDX(wc_targets, i, const char *); + wli = apr_pcalloc(scratch_pool, sizeof(*wli)); + + SVN_ERR(svn_wc__node_get_base(&kind, &wli->revision, + &repos_relpath, + &repos_root_url, NULL, + &wli->lock_token, + wc_ctx, local_abspath, + FALSE /* ignore_enoent */, + result_pool, iterpool)); + + if (kind != svn_node_file) + return svn_error_createf(SVN_ERR_WC_NOT_FILE, NULL, + _("The node '%s' is not a file"), + svn_dirent_local_style(local_abspath, + iterpool)); + + wli->url = svn_path_url_add_component2(repos_root_url, + repos_relpath, + scratch_pool); + svn_hash_sets(wc_info, local_abspath, wli); + + APR_ARRAY_PUSH(target_urls, const char *) = wli->url; + } } /* Now that we have a bunch of URLs for our dirent targets, @@ -345,39 +402,29 @@ organize_lock_targets(const char **common_parent_url, _("Unable to lock/unlock across multiple " "repositories")); - /* Now we need to create a couple of different hash mappings. */ + /* Now we need to create our mapping. */ rel_fs_paths = apr_hash_make(result_pool); - for (i = 0; i < rel_targets->nelts; i++) + + for (hi = apr_hash_first(scratch_pool, wc_info); + hi; + hi = apr_hash_next(hi)) { - const char *rel_target, *rel_url; - const char *local_abspath; + const char *local_abspath = apr_hash_this_key(hi); + struct wc_lock_item_t *wli = apr_hash_this_val(hi); + const char *rel_url; svn_pool_clear(iterpool); - /* First, we need to map our REL_URL (which is relative to - COMMON_URL) to our REL_TARGET (which is relative to - COMMON_DIRENT). */ - rel_target = APR_ARRAY_IDX(rel_targets, i, const char *); - rel_url = APR_ARRAY_IDX(rel_urls, i, const char *); - svn_hash_sets(rel_fs_paths, rel_url, - apr_pstrdup(result_pool, rel_target)); + rel_url = svn_uri_skip_ancestor(common_url, wli->url, result_pool); - /* Then, we map our REL_URL (again) to either the base - revision of the dirent target with which it is associated - (if our caller is locking) or to a (possible empty) lock - token string (if the caller is unlocking). */ - local_abspath = svn_dirent_join(common_dirent, rel_target, iterpool); + svn_hash_sets(rel_fs_paths, rel_url, + apr_pstrdup(result_pool, local_abspath)); if (do_lock) /* Lock. */ { svn_revnum_t *revnum; - struct wc_lock_item_t *wli; revnum = apr_palloc(result_pool, sizeof(* revnum)); - wli = svn_hash_gets(wc_info, local_abspath); - - SVN_ERR_ASSERT(wli != NULL); - *revnum = wli->revision; svn_hash_sets(rel_targets_ret, rel_url, revnum); @@ -385,15 +432,10 @@ organize_lock_targets(const char **common_parent_url, else /* Unlock. */ { const char *lock_token; - struct wc_lock_item_t *wli; /* If not forcing the unlock, get the lock token. */ if (! force) { - wli = svn_hash_gets(wc_info, local_abspath); - - SVN_ERR_ASSERT(wli != NULL); - if (! wli->lock_token) return svn_error_createf( SVN_ERR_CLIENT_MISSING_LOCK_TOKEN, NULL, @@ -419,7 +461,10 @@ organize_lock_targets(const char **common_parent_url, /* Set our return variables. */ *common_parent_url = common_url; - *base_dir = common_dirent; + if (*lock_paths && (*lock_paths)->nelts == 1) + *base_dir_abspath = APR_ARRAY_IDX(*lock_paths, 0, const char*); + else + *base_dir_abspath = NULL; *rel_targets_p = rel_targets_ret; *rel_fs_paths_p = rel_fs_paths; @@ -437,7 +482,7 @@ fetch_tokens(svn_ra_session_t *ra_session, apr_hash_t *path_tokens, for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); svn_lock_t *lock; svn_pool_clear(iterpool); @@ -464,12 +509,13 @@ svn_client_lock(const apr_array_header_t *targets, svn_client_ctx_t *ctx, apr_pool_t *pool) { - const char *base_dir; const char *base_dir_abspath = NULL; const char *common_parent_url; svn_ra_session_t *ra_session; apr_hash_t *path_revs, *urls_to_paths; struct lock_baton cb; + apr_array_header_t *lock_abspaths; + svn_error_t *err; if (apr_is_empty_array(targets)) return SVN_NO_ERROR; @@ -483,26 +529,49 @@ svn_client_lock(const apr_array_header_t *targets, _("Lock comment contains illegal characters")); } - SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_revs, - &urls_to_paths, targets, TRUE, steal_lock, - ctx->wc_ctx, pool, pool)); + err = organize_lock_targets(&lock_abspaths, &common_parent_url, + &base_dir_abspath, &path_revs, + &urls_to_paths, + targets, TRUE, steal_lock, + ctx->wc_ctx, pool, pool); + + if (err) + goto release_locks; - /* Open an RA session to the common parent of TARGETS. */ - if (base_dir) - SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool)); - SVN_ERR(svn_client_open_ra_session2(&ra_session, common_parent_url, - base_dir_abspath, ctx, pool, pool)); + /* Open an RA session to the common parent URL of TARGETS. */ + err = svn_client_open_ra_session2(&ra_session, common_parent_url, + base_dir_abspath, ctx, pool, pool); + + if (err) + goto release_locks; cb.base_dir_abspath = base_dir_abspath; + cb.base_url = common_parent_url; cb.urls_to_paths = urls_to_paths; cb.ctx = ctx; cb.pool = pool; /* Lock the paths. */ - SVN_ERR(svn_ra_lock(ra_session, path_revs, comment, - steal_lock, store_locks_callback, &cb, pool)); + err = svn_ra_lock(ra_session, path_revs, comment, + steal_lock, store_locks_callback, &cb, pool); - return SVN_NO_ERROR; +release_locks: + if (lock_abspaths) + { + int i; + + for (i = 0; i < lock_abspaths->nelts; i++) + { + err = svn_error_compose_create( + err, + svn_wc__release_write_lock(ctx->wc_ctx, + APR_ARRAY_IDX(lock_abspaths, i, + const char *), + pool)); + } + } + + return svn_error_trace(err); } svn_error_t * @@ -511,42 +580,70 @@ svn_client_unlock(const apr_array_header_t *targets, svn_client_ctx_t *ctx, apr_pool_t *pool) { - const char *base_dir; const char *base_dir_abspath = NULL; const char *common_parent_url; svn_ra_session_t *ra_session; apr_hash_t *path_tokens, *urls_to_paths; + apr_array_header_t *lock_abspaths; struct lock_baton cb; + svn_error_t *err; if (apr_is_empty_array(targets)) return SVN_NO_ERROR; - SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_tokens, - &urls_to_paths, targets, FALSE, break_lock, - ctx->wc_ctx, pool, pool)); + err = organize_lock_targets(&lock_abspaths, &common_parent_url, + &base_dir_abspath, &path_tokens, &urls_to_paths, + targets, FALSE, break_lock, + ctx->wc_ctx, pool, pool); + + if (err) + goto release_locks; - /* Open an RA session. */ - if (base_dir) - SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool)); - SVN_ERR(svn_client_open_ra_session2(&ra_session, common_parent_url, - base_dir_abspath, ctx, pool, pool)); + /* Open an RA session to the common parent URL of TARGETS. */ + err = svn_client_open_ra_session2(&ra_session, common_parent_url, + base_dir_abspath, ctx, pool, pool); + + if (err) + goto release_locks; /* If break_lock is not set, lock tokens are required by the server. If the targets were all URLs, ensure that we provide lock tokens, so the repository will only check that the user owns the locks. */ - if (! base_dir && !break_lock) - SVN_ERR(fetch_tokens(ra_session, path_tokens, pool)); + if (! lock_abspaths && !break_lock) + { + err = fetch_tokens(ra_session, path_tokens, pool); + + if (err) + goto release_locks; + } cb.base_dir_abspath = base_dir_abspath; + cb.base_url = common_parent_url; cb.urls_to_paths = urls_to_paths; cb.ctx = ctx; cb.pool = pool; /* Unlock the paths. */ - SVN_ERR(svn_ra_unlock(ra_session, path_tokens, break_lock, - store_locks_callback, &cb, pool)); + err = svn_ra_unlock(ra_session, path_tokens, break_lock, + store_locks_callback, &cb, pool); - return SVN_NO_ERROR; +release_locks: + if (lock_abspaths) + { + int i; + + for (i = 0; i < lock_abspaths->nelts; i++) + { + err = svn_error_compose_create( + err, + svn_wc__release_write_lock(ctx->wc_ctx, + APR_ARRAY_IDX(lock_abspaths, i, + const char *), + pool)); + } + } + + return svn_error_trace(err); } diff --git a/contrib/subversion/subversion/libsvn_client/log.c b/contrib/subversion/subversion/libsvn_client/log.c index 91961559d..adaee6104 100644 --- a/contrib/subversion/subversion/libsvn_client/log.c +++ b/contrib/subversion/subversion/libsvn_client/log.c @@ -96,6 +96,7 @@ 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_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -103,18 +104,50 @@ svn_client__get_copy_source(const char **original_repos_relpath, 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; + const char *old_session_url = NULL; 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)); + if (!ra_session) + { + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &at_loc, + path_or_url, NULL, + revision, revision, + ctx, sesspool)); + } + else + { + const char *url; + if (svn_path_is_url(path_or_url)) + url = path_or_url; + else + { + SVN_ERR(svn_client_url_from_path2(&url, path_or_url, ctx, sesspool, + sesspool)); + + if (! url) + return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, + _("'%s' has no URL"), path_or_url); + } + + SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, + url, sesspool)); + + err = svn_client__resolve_rev_and_url(&at_loc, ra_session, path_or_url, + revision, revision, ctx, + sesspool); + + /* On error reparent back (and return), otherwise reparent to new + location */ + SVN_ERR(svn_error_compose_create( + err, + svn_ra_reparent(ra_session, err ? old_session_url + : at_loc->url, sesspool))); + } /* Find the copy source. Walk the location segments to find the revision at which this node was created (copied or added). */ @@ -124,6 +157,11 @@ svn_client__get_copy_source(const char **original_repos_relpath, copyfrom_info_receiver, ©from_info, scratch_pool); + if (old_session_url) + err = svn_error_compose_create( + err, + svn_ra_reparent(ra_session, old_session_url, sesspool)); + svn_pool_destroy(sesspool); if (err) @@ -265,7 +303,7 @@ limit_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) 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. + relative paths -- anything else will raise an error. PEG_REVISION, TARGETS, and CTX are as per svn_client_log5. @@ -604,7 +642,7 @@ run_ra_get_log(apr_array_header_t *revision_ranges, 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. */ + /* The following are as per svn_client_log5. */ const apr_array_header_t *targets, int limit, svn_boolean_t discover_changed_paths, @@ -723,7 +761,7 @@ run_ra_get_log(apr_array_header_t *revision_ranges, 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...*/ @@ -905,8 +943,8 @@ svn_client_log5(const apr_array_header_t *targets, 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)); + include_merged_revisions, revprops, + real_receiver, real_receiver_baton, ctx, pool)); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/merge.c b/contrib/subversion/subversion/libsvn_client/merge.c index f0ff9a233..e2435754e 100644 --- a/contrib/subversion/subversion/libsvn_client/merge.c +++ b/contrib/subversion/subversion/libsvn_client/merge.c @@ -54,13 +54,12 @@ #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_mergeinfo_private.h" #include "private/svn_client_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_subr_private.h" +#include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -570,14 +569,16 @@ perform_obstruction_check(svn_wc_notify_state_t *obstruction_state, } /* Create *LEFT and *RIGHT conflict versions for conflict victim - * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained - * from MERGE_SOURCE and TARGET. + * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND + * and merge-right node kind MERGE_RIGHT_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, + svn_node_kind_t merge_left_node_kind, + svn_node_kind_t merge_right_node_kind, const merge_source_t *merge_source, const merge_target_t *target, apr_pool_t *result_pool, @@ -597,13 +598,15 @@ make_conflict_versions(const svn_wc_conflict_version_t **left, 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); + merge_source->loc1->rev, + merge_left_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); + merge_source->loc2->rev, + merge_right_node_kind, result_pool); return SVN_NO_ERROR; } @@ -633,8 +636,8 @@ split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo, 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); + const char *merge_source_path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_pool_clear(iterpool); @@ -764,7 +767,7 @@ filter_self_referential_mergeinfo(apr_array_header_t **props, /* 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, + &target_base.repos_uuid, NULL, NULL, ctx->wc_ctx, target_abspath, FALSE, pool, pool)); @@ -863,8 +866,8 @@ filter_self_referential_mergeinfo(apr_array_header_t **props, 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 *source_path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); const char *merge_source_url; svn_rangelist_t *adjusted_rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *)); @@ -1182,6 +1185,9 @@ struct merge_dir_baton_t */ svn_wc_conflict_reason_t tree_conflict_reason; svn_wc_conflict_action_t tree_conflict_action; + svn_node_kind_t tree_conflict_local_node_kind; + svn_node_kind_t tree_conflict_merge_left_node_kind; + svn_node_kind_t tree_conflict_merge_right_node_kind; /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to add to the notification */ @@ -1233,6 +1239,9 @@ struct merge_file_baton_t merge_tree_baton_t for an explanation. */ svn_wc_conflict_reason_t tree_conflict_reason; svn_wc_conflict_action_t tree_conflict_action; + svn_node_kind_t tree_conflict_local_node_kind; + svn_node_kind_t tree_conflict_merge_left_node_kind; + svn_node_kind_t tree_conflict_merge_right_node_kind; /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to add to the notification */ @@ -1280,8 +1289,8 @@ record_skip(merge_cmd_baton_t *merge_b, notify->kind = kind; notify->content_state = notify->prop_state = state; - (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, - scratch_pool); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, + scratch_pool); } return SVN_NO_ERROR; } @@ -1294,8 +1303,6 @@ record_skip(merge_cmd_baton_t *merge_b, * 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. */ @@ -1303,7 +1310,9 @@ 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_node_kind_t local_node_kind, + svn_node_kind_t merge_left_node_kind, + svn_node_kind_t merge_right_node_kind, svn_wc_conflict_action_t action, svn_wc_conflict_reason_t reason, const svn_wc_conflict_description2_t *existing_conflict, @@ -1357,7 +1366,9 @@ record_tree_conflict(merge_cmd_baton_t *merge_b, reason = svn_wc_conflict_reason_moved_here; } - SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind, + SVN_ERR(make_conflict_versions(&left, &right, local_abspath, + merge_left_node_kind, + merge_right_node_kind, &merge_b->merge_source, merge_b->target, result_pool, scratch_pool)); @@ -1366,7 +1377,8 @@ record_tree_conflict(merge_cmd_baton_t *merge_b, left = existing_conflict->src_left_version; conflict = svn_wc_conflict_description_create_tree2( - local_abspath, node_kind, svn_wc_operation_merge, + local_abspath, local_node_kind, + svn_wc_operation_merge, left, right, result_pool); conflict->action = action; @@ -1402,10 +1414,10 @@ record_tree_conflict(merge_cmd_baton_t *merge_b, notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict, scratch_pool); - notify->kind = node_kind; + notify->kind = local_node_kind; - (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, - scratch_pool); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1439,8 +1451,8 @@ record_update_add(merge_cmd_baton_t *merge_b, 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); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1473,8 +1485,8 @@ record_update_update(merge_cmd_baton_t *merge_b, notify->content_state = content_state; notify->prop_state = prop_state; - (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, - scratch_pool); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, + scratch_pool); } return SVN_NO_ERROR; @@ -1531,17 +1543,17 @@ handle_pending_notifications(merge_cmd_baton_t *merge_b, hi; hi = apr_hash_next(hi)) { - const char *del_abspath = svn__apr_hash_index_key(hi); + const char *del_abspath = apr_hash_this_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)); + apr_hash_this_val(hi)); - (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, - notify, scratch_pool); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, + notify, scratch_pool); } db->pending_deletes = NULL; @@ -1607,9 +1619,9 @@ mark_dir_edited(merge_cmd_baton_t *merge_b, 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); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, + notify, + scratch_pool); } if (merge_b->merge_source.ancestral @@ -1623,7 +1635,10 @@ mark_dir_edited(merge_cmd_baton_t *merge_b, /* 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_local_node_kind, + db->tree_conflict_merge_left_node_kind, + db->tree_conflict_merge_right_node_kind, + db->tree_conflict_action, db->tree_conflict_reason, NULL, TRUE, scratch_pool)); @@ -1686,9 +1701,9 @@ mark_file_edited(merge_cmd_baton_t *merge_b, 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); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, + notify, + scratch_pool); } if (merge_b->merge_source.ancestral @@ -1702,7 +1717,10 @@ mark_file_edited(merge_cmd_baton_t *merge_b, /* 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_local_node_kind, + fb->tree_conflict_merge_left_node_kind, + fb->tree_conflict_merge_right_node_kind, + fb->tree_conflict_action, fb->tree_conflict_reason, NULL, TRUE, scratch_pool)); @@ -1742,6 +1760,16 @@ merge_file_opened(void **new_file_baton, fb->tree_conflict_action = svn_wc_conflict_action_edit; fb->skip_reason = svn_wc_notify_state_unknown; + if (left_source) + fb->tree_conflict_merge_left_node_kind = svn_node_file; + else + fb->tree_conflict_merge_left_node_kind = svn_node_none; + + if (right_source) + fb->tree_conflict_merge_right_node_kind = svn_node_file; + else + fb->tree_conflict_merge_right_node_kind = svn_node_none; + *new_file_baton = fb; if (pdb) @@ -1758,7 +1786,6 @@ merge_file_opened(void **new_file_baton, 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; @@ -1770,7 +1797,8 @@ merge_file_opened(void **new_file_baton, svn_wc_notify_state_t obstr_state; SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded, - &kind, &parent_depth, + &fb->tree_conflict_local_node_kind, + &parent_depth, merge_b, local_abspath, scratch_pool)); @@ -1783,10 +1811,10 @@ merge_file_opened(void **new_file_baton, } if (is_deleted) - kind = svn_node_none; + fb->tree_conflict_local_node_kind = svn_node_none; } - if (kind == svn_node_none) + if (fb->tree_conflict_local_node_kind == svn_node_none) { fb->shadowed = TRUE; @@ -1820,11 +1848,16 @@ merge_file_opened(void **new_file_baton, return SVN_NO_ERROR; /* ### /Similar */ } - else if (kind != svn_node_file) + else if (fb->tree_conflict_local_node_kind != svn_node_file) { + svn_boolean_t added; fb->shadowed = TRUE; - fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed; + SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx, + local_abspath, scratch_pool)); + + fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added + : svn_wc_conflict_reason_obstructed; /* ### Similar to directory */ *skip = TRUE; @@ -1879,6 +1912,8 @@ merge_file_opened(void **new_file_baton, /* Update the tree conflict to store that this is a replace */ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, + old_tc->node_kind, + old_tc->src_left_version->node_kind, svn_node_file, fb->tree_conflict_action, fb->tree_conflict_reason, @@ -1905,12 +1940,11 @@ merge_file_opened(void **new_file_baton, && ((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, + &fb->tree_conflict_local_node_kind, + NULL, merge_b, local_abspath, scratch_pool)); if (obstr_state != svn_wc_notify_state_inapplicable) @@ -1920,11 +1954,18 @@ merge_file_opened(void **new_file_baton, fb->tree_conflict_reason = CONFLICT_REASON_SKIP; fb->skip_reason = obstr_state; } - else if (kind != svn_node_none && !is_deleted) + else if (fb->tree_conflict_local_node_kind != svn_node_none + && !is_deleted) { /* Set a tree conflict */ + svn_boolean_t added; + fb->shadowed = TRUE; - fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed; + SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx, + local_abspath, scratch_pool)); + + fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added + : svn_wc_conflict_reason_obstructed; } } @@ -2000,7 +2041,8 @@ merge_file_changed(const char *relpath, scratch_pool, scratch_pool)); SVN_ERR(make_conflict_versions(&left, &right, local_abspath, - svn_node_file, &merge_b->merge_source, merge_b->target, + svn_node_file, 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 */ @@ -2307,17 +2349,47 @@ files_same_p(svn_boolean_t *same, { svn_stream_t *mine_stream; svn_stream_t *older_stream; - svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } }; + svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL); + svn_string_t *eol_style = svn_hash_gets(working_props, SVN_PROP_EOL_STYLE); + svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS); /* Compare the file content, translating 'mine' to 'normal' form. */ - if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL) + if (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(&mine_stream, mine_abspath, + scratch_pool, scratch_pool)); + + if (!special && (eol_style || keywords)) + { + apr_hash_t *kw = NULL; + const char *eol = NULL; + svn_subst_eol_style_t style; + + /* We used to use svn_client__get_normalized_stream() here, but + that doesn't work in 100% of the cases because it doesn't + convert EOLs to the repository form; just to '\n'. + */ + + if (eol_style) + { + svn_subst_eol_style_from_value(&style, &eol, eol_style->data); + + if (style == svn_subst_eol_style_native) + eol = SVN_SUBST_NATIVE_EOL_STR; + else if (style != svn_subst_eol_style_fixed + && style != svn_subst_eol_style_none) + return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); + } + + if (keywords) + SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "", + "", 0, "", scratch_pool)); + + mine_stream = svn_subst_stream_translated( + mine_stream, eol, FALSE, kw, FALSE, scratch_pool); + } SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath, scratch_pool, scratch_pool)); @@ -2427,6 +2499,8 @@ merge_file_deleted(const char *relpath, */ SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton, svn_node_file, + svn_node_file, + svn_node_none, svn_wc_conflict_action_delete, svn_wc_conflict_reason_edited, NULL, TRUE, @@ -2447,7 +2521,7 @@ merge_file_deleted(const char *relpath, When *SKIP is TRUE, the diff driver avoids work on getting the details for the closing callbacks. - The SKIP and SKIP_DESCENDANTS work independantly. + The SKIP and SKIP_DESCENDANTS work independently. */ static svn_error_t * merge_dir_opened(void **new_dir_baton, @@ -2477,6 +2551,16 @@ merge_dir_opened(void **new_dir_baton, *new_dir_baton = db; + if (left_source) + db->tree_conflict_merge_left_node_kind = svn_node_dir; + else + db->tree_conflict_merge_left_node_kind = svn_node_none; + + if (right_source) + db->tree_conflict_merge_right_node_kind = svn_node_dir; + else + db->tree_conflict_merge_right_node_kind = svn_node_none; + if (pdb) { db->parent_baton = pdb; @@ -2493,7 +2577,6 @@ merge_dir_opened(void **new_dir_baton, 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; @@ -2505,9 +2588,9 @@ merge_dir_opened(void **new_dir_baton, { 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)); + &db->tree_conflict_local_node_kind, + &parent_depth, merge_b, + local_abspath, scratch_pool)); if (obstr_state != svn_wc_notify_state_inapplicable) { @@ -2542,10 +2625,10 @@ merge_dir_opened(void **new_dir_baton, } if (is_deleted) - kind = svn_node_none; + db->tree_conflict_local_node_kind = svn_node_none; } - if (kind == svn_node_none) + if (db->tree_conflict_local_node_kind == svn_node_none) { db->shadowed = TRUE; @@ -2581,11 +2664,16 @@ merge_dir_opened(void **new_dir_baton, return SVN_NO_ERROR; /* ### /avoid breaking tests */ } - else if (kind != svn_node_dir) + else if (db->tree_conflict_local_node_kind != svn_node_dir) { + svn_boolean_t added; + db->shadowed = TRUE; + SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx, + local_abspath, scratch_pool)); - db->tree_conflict_reason = svn_wc_conflict_reason_obstructed; + db->tree_conflict_reason = added ? svn_wc_conflict_reason_added + : svn_wc_conflict_reason_obstructed; /* ### To avoid breaking tests */ *skip = TRUE; @@ -2672,6 +2760,8 @@ merge_dir_opened(void **new_dir_baton, /* Update the tree conflict to store that this is a replace */ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, + old_tc->node_kind, + old_tc->src_left_version->node_kind, svn_node_dir, db->tree_conflict_action, db->tree_conflict_reason, @@ -2686,12 +2776,11 @@ merge_dir_opened(void **new_dir_baton, && ((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, + &db->tree_conflict_local_node_kind, + NULL, merge_b, local_abspath, scratch_pool)); /* In this case of adding a directory, we have an exception to the @@ -2701,7 +2790,8 @@ merge_dir_opened(void **new_dir_baton, * 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)) + && (is_deleted || + db->tree_conflict_local_node_kind == svn_node_none)) { svn_node_kind_t disk_kind; @@ -2722,17 +2812,18 @@ merge_dir_opened(void **new_dir_baton, db->tree_conflict_reason = CONFLICT_REASON_SKIP; db->skip_reason = obstr_state; } - else if (kind != svn_node_none && !is_deleted) + else if (db->tree_conflict_local_node_kind != svn_node_none + && !is_deleted) { /* Set a tree conflict */ + svn_boolean_t added; 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); - } + SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx, + local_abspath, scratch_pool)); + + db->tree_conflict_reason = added ? svn_wc_conflict_reason_added + : svn_wc_conflict_reason_obstructed; } } @@ -2758,7 +2849,7 @@ merge_dir_opened(void **new_dir_baton, if (old_tc) { - /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node + /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node over an existing tree conflict */ /* ### These functions should take some tree conflict argument @@ -2796,8 +2887,9 @@ merge_dir_opened(void **new_dir_baton, } else { - SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath, + SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath, apr_hash_make(scratch_pool), + FALSE /* skip checks */, NULL, NULL /* no notify! */, scratch_pool)); } @@ -2806,6 +2898,8 @@ merge_dir_opened(void **new_dir_baton, { /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */ SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, + old_tc->node_kind, + svn_node_none, svn_node_dir, db->tree_conflict_action, db->tree_conflict_reason, @@ -2875,7 +2969,8 @@ merge_dir_changed(const char *relpath, svn_wc_notify_state_t prop_state; SVN_ERR(make_conflict_versions(&left, &right, local_abspath, - svn_node_dir, &merge_b->merge_source, + svn_node_dir, svn_node_dir, + &merge_b->merge_source, merge_b->target, scratch_pool, scratch_pool)); @@ -3225,6 +3320,8 @@ merge_dir_deleted(const char *relpath, */ SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton, svn_node_dir, + svn_node_dir, + svn_node_none, svn_wc_conflict_action_delete, svn_wc_conflict_reason_edited, NULL, TRUE, @@ -3643,8 +3740,8 @@ notify_merge_begin(merge_cmd_baton_t *merge_b, notify->merge_range = NULL; } - (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, - scratch_pool); + merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify, + scratch_pool); return SVN_NO_ERROR; } @@ -3792,111 +3889,96 @@ adjust_deleted_subtree_ranges(svn_client__merge_path_t *child, 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) + const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */ + svn_node_kind_t kind; + + if (err->apr_err != SVN_ERR_FS_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + + /* 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. */ + + 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) { - /* 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; + /* 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; - svn_error_clear(err); - err = NULL; + /* 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)); - SVN_ERR(svn_ra_get_path_relative_to_session( - ra_session, &rel_source_path, primary_url, 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)); - SVN_ERR(svn_ra_check_path(ra_session, rel_source_path, - older_rev, &kind, scratch_pool)); - if (kind == svn_node_none) + /* 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) { - /* 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); + /* 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)); } - 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)); - /* 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)); + /* 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)); - } + /* 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. */ { @@ -4684,7 +4766,6 @@ calculate_remaining_ranges(svn_client__merge_path_t *parent, 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. */ @@ -4779,7 +4860,7 @@ find_gaps_in_merge_source_history(svn_revnum_t *gap_start, *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) + if (labs(source->loc1->rev - source->loc2->rev) == 1) return SVN_NO_ERROR; /* Get SOURCE as mergeinfo. */ @@ -5174,8 +5255,8 @@ update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog, 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); + const char *local_abspath = apr_hash_this_key(hi); + svn_rangelist_t *ranges = apr_hash_this_val(hi); svn_rangelist_t *rangelist; svn_error_t *err; const char *local_abspath_rel_to_target; @@ -5326,7 +5407,7 @@ record_skips_in_mergeinfo(const char *mergeinfo_path, 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); + const char *skipped_abspath = apr_hash_this_key(hi); svn_wc_notify_state_t obstruction_state; svn_pool_clear(iterpool); @@ -5882,7 +5963,7 @@ slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo, 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); + svn_sort__array_insert(child->remaining_ranges, &split_range2, 1); } } } @@ -6017,11 +6098,11 @@ insert_child_to_merge(apr_array_header_t *children_with_mergeinfo, /* Find where to insert the new element */ insert_index = - svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo, + svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element, 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); + svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index); } /* Helper for get_mergeinfo_paths(). @@ -6088,8 +6169,9 @@ insert_parent_and_sibs_of_sw_absent_del_subtree( } /*(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)); + SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx, + parent_abspath, + pool, pool)); iterpool = svn_pool_create(pool); for (i = 0; i < children->nelts; i++) { @@ -6188,7 +6270,7 @@ pre_merge_status_cb(void *baton, hi; hi = apr_hash_next(hi)) { - const char *missing_root_path = svn__apr_hash_index_key(hi); + const char *missing_root_path = apr_hash_this_key(hi); if (svn_dirent_is_ancestor(missing_root_path, local_abspath)) @@ -6246,8 +6328,8 @@ get_wc_explicit_mergeinfo_catalog(apr_hash_t **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); + const char *wc_path = apr_hash_this_key(hi); + svn_string_t *mergeinfo_string = apr_hash_this_val(hi); svn_mergeinfo_t mergeinfo; svn_error_t *err; @@ -6360,8 +6442,8 @@ get_mergeinfo_paths(apr_array_header_t *children_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); + const char *wc_path = apr_hash_this_key(hi); + svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi); svn_client__merge_path_t *mergeinfo_child = svn_client__merge_path_create(wc_path, result_pool); @@ -6429,7 +6511,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, svn_pool_clear(iterpool); svn_stringbuf_appendcstr(missing_subtree_err_buf, svn_dirent_local_style( - svn__apr_hash_index_key(hi), iterpool)); + apr_hash_this_key(hi), iterpool)); svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n"); } @@ -6445,7 +6527,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, hi; hi = apr_hash_next(hi)) { - const char *wc_path = svn__apr_hash_index_key(hi); + const char *wc_path = apr_hash_this_key(hi); svn_client__merge_path_t *child = get_child_with_mergeinfo( children_with_mergeinfo, wc_path); @@ -6473,8 +6555,8 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, 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); + const char *wc_path = apr_hash_this_key(hi); + svn_depth_t *child_depth = apr_hash_this_val(hi); svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo( children_with_mergeinfo, wc_path); @@ -6528,7 +6610,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, hi; hi = apr_hash_next(hi)) { - const char *wc_path = svn__apr_hash_index_key(hi); + const char *wc_path = apr_hash_this_key(hi); svn_client__merge_path_t *child = get_child_with_mergeinfo( children_with_mergeinfo, wc_path); @@ -6571,7 +6653,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, SVN_ERR(svn_wc__node_get_children_of_working_node( &immediate_children, ctx->wc_ctx, - target->abspath, FALSE, scratch_pool, scratch_pool)); + target->abspath, scratch_pool, scratch_pool)); for (j = 0; j < immediate_children->nelts; j++) { @@ -6655,9 +6737,10 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, const apr_array_header_t *children; int j; - SVN_ERR(svn_wc__node_get_children(&children, + SVN_ERR(svn_wc__node_get_children_of_working_node( + &children, ctx->wc_ctx, - child->abspath, FALSE, + child->abspath, iterpool, iterpool)); for (j = 0; j < children->nelts; j++) { @@ -7104,7 +7187,8 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p, SVN_ERR(svn_client__get_copy_source(&original_repos_relpath, &original_revision, segment_url, - &range_start_rev, ctx, + &range_start_rev, + ra_session, 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 @@ -7117,7 +7201,7 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p, 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); + svn_sort__array_insert(segments, &new_segment, 0); } } } @@ -7783,7 +7867,7 @@ process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b, hi; hi = apr_hash_next(hi)) { - const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi); + const char *abspath_with_new_mergeinfo = apr_hash_this_key(hi); svn_mergeinfo_t path_inherited_mergeinfo; svn_mergeinfo_t path_explicit_mergeinfo; svn_client__merge_path_t *new_child; @@ -7886,7 +7970,7 @@ path_is_subtree(const char *local_abspath, 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); + const char *path_touched_by_merge = apr_hash_this_key(hi); if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge)) return TRUE; } @@ -8001,8 +8085,8 @@ log_find_operative_subtree_revs(void *baton, 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 *path = apr_hash_this_key(hi); + svn_log_changed_path2_t *change = apr_hash_this_val(hi); { const char *child; @@ -8638,7 +8722,7 @@ record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog, /* 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 + for now. do_merge() will eventually try to elide that when the merge is complete. */ SVN_ERR(svn_client__elide_mergeinfo( child->abspath, @@ -8687,7 +8771,7 @@ record_mergeinfo_for_added_subtrees( 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 *added_abspath = apr_hash_this_key(hi); const char *dir_abspath; svn_mergeinfo_t parent_mergeinfo; svn_mergeinfo_t added_path_mergeinfo; @@ -8896,7 +8980,7 @@ log_noop_revs(void *baton, hi; hi = apr_hash_next(hi)) { - const char *fspath = svn__apr_hash_index_key(hi); + const char *fspath = apr_hash_this_key(hi); const char *rel_path; const char *cwmi_abspath; svn_rangelist_t *paths_explicit_rangelist = NULL; @@ -9053,24 +9137,23 @@ remove_noop_subtree_ranges(const merge_source_t *source, 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. */ + /* CHILD->REMAINING_RANGES will be NULL if child is absent. */ if (child->remaining_ranges && child->remaining_ranges->nelts) { + /* 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 (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_ERR(svn_rangelist_merge2(subtree_remaining_ranges, + child->remaining_ranges, + scratch_pool, iterpool)); + } } svn_pool_destroy(iterpool); @@ -9464,7 +9547,7 @@ do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog, { if (!merge_b->record_only) { - /* Reset cur_ancestor_abspath to null so that subsequent cherry + /* Reset the last notification path so that subsequent cherry picked revision ranges will be notified upon subsequent operative merge. */ merge_b->notify_begin.last_abspath = NULL; @@ -10226,7 +10309,7 @@ ensure_wc_is_suitable_merge_target(const char *target_abspath, svn_boolean_t is_modified; SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx, - target_abspath, + target_abspath, TRUE, ctx->cancel_func, ctx->cancel_baton, scratch_pool)); @@ -10724,7 +10807,7 @@ log_find_operative_revs(void *baton, hi = apr_hash_next(hi)) { const char *subtree_missing_this_rev; - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); const char *rel_path; const char *source_rel_path; svn_boolean_t in_catalog; @@ -10852,7 +10935,7 @@ find_unsynced_ranges(const svn_client__pathrev_t *source_loc, hi_catalog; hi_catalog = apr_hash_next(hi_catalog)) { - svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog); + svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog); SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges, mergeinfo, @@ -11058,8 +11141,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog, 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 *target_path = apr_hash_this_key(hi); + svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi); const char *path_rel_to_session = svn_relpath_skip_ancestor(target_repos_rel_path, target_path); const char *source_path; @@ -11143,11 +11226,11 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog, hi; hi = apr_hash_next(hi)) { - const char *source_path = svn__apr_hash_index_key(hi); + const char *source_path = apr_hash_this_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 source_mergeinfo = apr_hash_this_val(hi); svn_mergeinfo_t filtered_mergeinfo; svn_client__pathrev_t *target_pathrev; svn_mergeinfo_t target_history_as_mergeinfo; @@ -11296,7 +11379,7 @@ calculate_left_hand_side(svn_client__pathrev_t **left_p, hi; hi = apr_hash_next(hi)) { - const char *local_abspath = svn__apr_hash_index_key(hi); + const char *local_abspath = apr_hash_this_key(hi); svn_client__pathrev_t *target_child; const char *repos_relpath; svn_mergeinfo_t target_history_as_mergeinfo; @@ -11523,7 +11606,7 @@ find_reintegrate_merge(merge_source_t **source_p, SVN_ERR(svn_mergeinfo__catalog_to_formatted_string( &source_mergeinfo_cat_string, final_unmerged_catalog, - " ", " Missing ranges: ", scratch_pool)); + " ", _(" Missing ranges: "), scratch_pool)); return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, _("Reintegrate can only be used if " @@ -11583,9 +11666,6 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p, 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 " @@ -11594,6 +11674,10 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p, svn_dirent_local_style(target->abspath, scratch_pool)); + SVN_ERR(svn_client_open_ra_session2(target_ra_session_p, + target->loc.url, target->abspath, + ctx, result_pool, 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, @@ -11647,6 +11731,7 @@ merge_reintegrate_locked(conflict_report_t **conflict_report, if (! source) { + *conflict_report = NULL; return SVN_NO_ERROR; } @@ -11943,8 +12028,8 @@ location_on_branch_at_rev(const branch_history_t *branch_history, 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); + const char *fspath = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); int i; for (i = 0; i < rangelist->nelts; i++) @@ -12347,7 +12432,16 @@ find_base_on_target(svn_client__pathrev_t **base_p, return SVN_NO_ERROR; } -/* The body of client_find_automatic_merge(), which see. +/* Find the last point at which the branch at S_T->source was completely + * merged to the branch at S_T->target or vice-versa. + * + * Fill in S_T->source_branch and S_T->target_branch and S_T->yca. + * Set *BASE_P to the merge base. Set *IS_REINTEGRATE_LIKE to true if + * an automatic merge from source to target would be a reintegration + * merge: that is, if the last automatic merge was in the opposite + * direction; or to false otherwise. + * + * If there is no youngest common ancestor, throw an error. */ static svn_error_t * find_automatic_merge(svn_client__pathrev_t **base_p, @@ -12417,6 +12511,9 @@ find_automatic_merge(svn_client__pathrev_t **base_p, * 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. + * + * Set *MERGE_P to a new structure with all fields filled in except the + * 'allow_*' flags. */ static svn_error_t * find_automatic_merge_no_wc(automatic_merge_t **merge_p, @@ -12492,6 +12589,8 @@ client_find_automatic_merge(automatic_merge_t **merge_p, source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t)); automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge)); + SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath)); + /* "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 @@ -12503,12 +12602,19 @@ client_find_automatic_merge(automatic_merge_t **merge_p, allow_switched_subtrees, ctx, result_pool, scratch_pool)); + if (!s_t->target->loc.url) + return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, + _("Can't perform automatic merge into '%s' " + "because it is locally added and therefore " + "not related to the merge source"), + svn_dirent_local_style(target_abspath, + 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, @@ -12523,6 +12629,7 @@ client_find_automatic_merge(automatic_merge_t **merge_p, ctx, result_pool, scratch_pool)); merge->yca = s_t->yca; merge->right = s_t->source; + merge->target = &s_t->target->loc; merge->allow_mixed_rev = allow_mixed_rev; merge->allow_local_mods = allow_local_mods; merge->allow_switched_subtrees = allow_switched_subtrees; @@ -12711,14 +12818,21 @@ svn_client_get_merging_summary(svn_boolean_t *needs_reintegration, 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); + || target_revision->kind == svn_opt_revision_working + || target_revision->kind == svn_opt_revision_base); 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)); + { + const char *target_abspath; + + SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url, + scratch_pool)); + SVN_ERR(client_find_automatic_merge( + &merge, + source_path_or_url, source_revision, + target_abspath, + TRUE, TRUE, TRUE, /* allow_* */ + ctx, scratch_pool, scratch_pool)); + } else SVN_ERR(find_automatic_merge_no_wc( &merge, diff --git a/contrib/subversion/subversion/libsvn_client/mergeinfo.c b/contrib/subversion/subversion/libsvn_client/mergeinfo.c index 2d277f5f6..622dbbca2 100644 --- a/contrib/subversion/subversion/libsvn_client/mergeinfo.c +++ b/contrib/subversion/subversion/libsvn_client/mergeinfo.c @@ -38,12 +38,13 @@ #include "svn_client.h" #include "svn_hash.h" +#include "private/svn_client_private.h" #include "private/svn_opt_private.h" #include "private/svn_mergeinfo_private.h" -#include "private/svn_wc_private.h" #include "private/svn_ra_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_wc_private.h" #include "private/svn_fspath.h" -#include "private/svn_client_private.h" #include "client.h" #include "mergeinfo.h" #include "svn_private_config.h" @@ -223,7 +224,6 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo, SVN_ERR(svn_wc__node_get_base(NULL, &base_revision, NULL, NULL, NULL, NULL, ctx->wc_ctx, local_abspath, TRUE /* ignore_enoent */, - FALSE /* show_hidden */, scratch_pool, scratch_pool)); iterpool = svn_pool_create(scratch_pool); @@ -294,7 +294,7 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo, SVN_ERR(svn_wc__node_get_base(NULL, &parent_base_rev, NULL, NULL, NULL, NULL, ctx->wc_ctx, local_abspath, - TRUE, FALSE, + TRUE /* ignore_enoent */, scratch_pool, scratch_pool)); /* ### This checks the WORKING changed_rev, so invalid on replacement @@ -360,7 +360,7 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo, SVN_ERR(svn_mergeinfo_inheritable2(mergeinfo, *mergeinfo, NULL, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE, result_pool, scratch_pool)); - svn_mergeinfo__remove_empty_rangelists(*mergeinfo, result_pool); + svn_mergeinfo__remove_empty_rangelists(*mergeinfo, scratch_pool); } if (inherited_p) @@ -429,8 +429,8 @@ svn_client__get_wc_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat, hi; hi = apr_hash_next(hi)) { - const char *node_abspath = svn__apr_hash_index_key(hi); - svn_string_t *propval = svn__apr_hash_index_val(hi); + const char *node_abspath = apr_hash_this_key(hi); + svn_string_t *propval = apr_hash_this_val(hi); svn_mergeinfo_t subtree_mergeinfo; const char *repos_relpath; @@ -482,7 +482,7 @@ svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo, descendants. So if there is anything in the catalog it is the mergeinfo for REL_PATH. */ *target_mergeinfo = - svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat)); + apr_hash_this_val(apr_hash_first(pool, tgt_mergeinfo_cat)); } @@ -582,7 +582,7 @@ svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo, so we can peek into our catalog, but it ought to be the only thing in the catalog, so we'll just fetch the first hash item. */ *target_mergeinfo = - svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat)); + apr_hash_this_val(apr_hash_first(pool, tgt_mergeinfo_cat)); } @@ -623,7 +623,7 @@ svn_client__get_wc_or_repos_mergeinfo_catalog( a URL and without that we cannot get accurate mergeinfo for TARGET_WCPATH. */ SVN_ERR(svn_wc__node_get_origin(NULL, &target_rev, &repos_relpath, - &repos_root, NULL, NULL, + &repos_root, NULL, NULL, NULL, ctx->wc_ctx, local_abspath, FALSE, scratch_pool, scratch_pool)); @@ -921,16 +921,13 @@ svn_client__elide_mergeinfo(const char *target_abspath, { svn_mergeinfo_t target_mergeinfo; svn_mergeinfo_t mergeinfo = NULL; - svn_boolean_t inherited; - const char *walk_path; svn_error_t *err; /* Get the TARGET_WCPATH's explicit mergeinfo. */ - err = svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited, - svn_mergeinfo_inherited, + err = svn_client__get_wc_mergeinfo(&target_mergeinfo, NULL, + svn_mergeinfo_explicit, target_abspath, - limit_abspath, - &walk_path, FALSE, + NULL, NULL, FALSE, ctx, pool, pool); if (err) { @@ -950,7 +947,7 @@ svn_client__elide_mergeinfo(const char *target_abspath, /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to elide, we're done. */ - if (inherited || target_mergeinfo == NULL) + if (target_mergeinfo == NULL) return SVN_NO_ERROR; /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */ @@ -958,7 +955,7 @@ svn_client__elide_mergeinfo(const char *target_abspath, svn_mergeinfo_nearest_ancestor, target_abspath, limit_abspath, - &walk_path, FALSE, ctx, pool, pool); + NULL, FALSE, ctx, pool, pool); if (err) { if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) @@ -1345,8 +1342,8 @@ filter_log_entry_with_rangelist(void *baton, hi = apr_hash_next(hi)) { int i; - const char *path = svn__apr_hash_index_key(hi); - svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi); + const char *path = apr_hash_this_key(hi); + svn_log_changed_path2_t *change = apr_hash_this_val(hi); const char *target_fspath_affected; svn_mergeinfo_t nearest_ancestor_mergeinfo; svn_boolean_t found_this_revision = FALSE; @@ -1430,8 +1427,8 @@ filter_log_entry_with_rangelist(void *baton, hi2; hi2 = apr_hash_next(hi2)) { - const char *mergeinfo_path = svn__apr_hash_index_key(hi2); - svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi2); + const char *mergeinfo_path = apr_hash_this_key(hi2); + svn_rangelist_t *rangelist = apr_hash_this_val(hi2); /* Does the mergeinfo for PATH reflect if LOG_ENTRY->REVISION was previously merged @@ -1608,8 +1605,8 @@ mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo, for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi)) { - const char *key = svn__apr_hash_index_key(hi); - void *val = svn__apr_hash_index_val(hi); + const char *key = apr_hash_this_key(hi); + void *val = apr_hash_this_val(hi); svn_hash_sets(full_path_mergeinfo, svn_path_url_add_component2(repos_root_url, key + 1, @@ -1927,14 +1924,14 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged, hi_catalog; hi_catalog = apr_hash_next(hi_catalog)) { - svn_mergeinfo_t subtree_mergeinfo = svn__apr_hash_index_val(hi_catalog); + svn_mergeinfo_t subtree_mergeinfo = apr_hash_this_val(hi_catalog); svn_mergeinfo_t subtree_history; svn_mergeinfo_t subtree_source_history; svn_mergeinfo_t subtree_inheritable_mergeinfo; svn_mergeinfo_t subtree_noninheritable_mergeinfo; svn_mergeinfo_t merged_noninheritable; svn_mergeinfo_t merged; - const char *subtree_path = svn__apr_hash_index_key(hi_catalog); + const char *subtree_path = apr_hash_this_key(hi_catalog); svn_boolean_t is_subtree = strcmp(subtree_path, target_repos_relpath) != 0; svn_pool_clear(iterpool); @@ -2057,8 +2054,7 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged, { svn_rangelist_t *deleted_rangelist; svn_rangelist_t *added_rangelist; - svn_rangelist_t *subtree_merged_rangelist = - svn__apr_hash_index_val(hi); + svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi); svn_pool_clear(iterpool); @@ -2134,15 +2130,14 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged, svn_rangelist__initialize(youngest_range->end - 1, youngest_range->end, youngest_range->inheritable, - scratch_pool);; + scratch_pool); for (hi = apr_hash_first(scratch_pool, source_history); hi; hi = apr_hash_next(hi)) { - const char *key = svn__apr_hash_index_key(hi); - svn_rangelist_t *subtree_merged_rangelist = - svn__apr_hash_index_val(hi); + const char *key = apr_hash_this_key(hi); + svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi); svn_rangelist_t *intersecting_rangelist; svn_pool_clear(iterpool); @@ -2230,6 +2225,8 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions, svn_mergeinfo_catalog_t mergeinfo_cat; svn_mergeinfo_t mergeinfo; apr_hash_index_t *hi; + apr_pool_t *session_pool = svn_pool_create(pool); + svn_ra_session_t *ra_session; list = apr_array_make(pool, 1, sizeof(const char *)); @@ -2248,26 +2245,32 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions, 1. The copyfrom source. 2. All remaining merge sources (unordered). */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, path_or_url, + NULL, peg_revision, peg_revision, + ctx, session_pool)); - /* ### TODO: Share ra_session batons to improve efficiency? */ SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url, - peg_revision, FALSE, FALSE, ctx, NULL, pool, pool)); + peg_revision, FALSE, FALSE, + ctx, ra_session, session_pool, session_pool)); if (mergeinfo_cat && apr_hash_count(mergeinfo_cat)) { /* We asked only for the PATH_OR_URL's mergeinfo, not any of its descendants. So if there is anything in the catalog it is the mergeinfo for PATH_OR_URL. */ - mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat)); + mergeinfo = apr_hash_this_val(apr_hash_first(session_pool, + mergeinfo_cat)); } else { mergeinfo = NULL; } + /* ### Should we only add the last source or all copy sources back to + the origin? */ SVN_ERR(svn_client__get_copy_source(©from_path, ©from_rev, - path_or_url, peg_revision, ctx, - pool, pool)); + path_or_url, peg_revision, ra_session, + ctx, session_pool, session_pool)); if (copyfrom_path) { APR_ARRAY_PUSH(list, const char *) = @@ -2276,9 +2279,11 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions, if (mergeinfo) { - for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) + for (hi = apr_hash_first(session_pool, mergeinfo); + hi; + hi = apr_hash_next(hi)) { - const char *rel_path = svn__apr_hash_index_key(hi); + const char *rel_path = apr_hash_this_key(hi); if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0) APR_ARRAY_PUSH(list, const char *) = \ @@ -2286,6 +2291,8 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions, } } + svn_pool_destroy(session_pool); + *suggestions = list; return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_client/mergeinfo.h b/contrib/subversion/subversion/libsvn_client/mergeinfo.h index 0c4cf05d5..1d6d524f5 100644 --- a/contrib/subversion/subversion/libsvn_client/mergeinfo.h +++ b/contrib/subversion/subversion/libsvn_client/mergeinfo.h @@ -316,7 +316,9 @@ svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p, /* Parse any explicit mergeinfo on LOCAL_ABSPATH and store it in *MERGEINFO. If no record of any mergeinfo exists, set *MERGEINFO to NULL. - Does not acount for inherited mergeinfo. */ + Does not acount for inherited mergeinfo. + + Allocate the result deeply in @a result_pool. */ svn_error_t * svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo, svn_wc_context_t *wc_ctx, @@ -358,8 +360,6 @@ svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog, working copy or the nearest switched parent for an elision destination, if none is found check the repository, otherwise check as far as WC_ELISION_LIMIT_ABSPATH within the working copy. - TARGET_WCPATH and WC_ELISION_LIMIT_ABSPATH, if it exists, must both be - absolute or relative to the working directory. Elision occurs if: diff --git a/contrib/subversion/subversion/libsvn_client/mtcc.c b/contrib/subversion/subversion/libsvn_client/mtcc.c new file mode 100644 index 000000000..e0fc1e944 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_client/mtcc.c @@ -0,0 +1,1429 @@ +/* + * mtcc.c -- Multi Command Context implementation. This allows + * performing many operations without a 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 "svn_dirent_uri.h" +#include "svn_hash.h" +#include "svn_path.h" +#include "svn_props.h" +#include "svn_pools.h" +#include "svn_subst.h" + +#include "private/svn_client_mtcc.h" + + +#include "svn_private_config.h" + +#include "client.h" + +#include + +#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0') + +/* The kind of operation to perform in an mtcc_op_t */ +typedef enum mtcc_kind_t +{ + OP_OPEN_DIR, + OP_OPEN_FILE, + OP_ADD_DIR, + OP_ADD_FILE, + OP_DELETE +} mtcc_kind_t; + +typedef struct mtcc_op_t +{ + const char *name; /* basename of operation */ + mtcc_kind_t kind; /* editor operation */ + + apr_array_header_t *children; /* List of mtcc_op_t * */ + + const char *src_relpath; /* For ADD_DIR, ADD_FILE */ + svn_revnum_t src_rev; /* For ADD_DIR, ADD_FILE */ + svn_stream_t *src_stream; /* For ADD_FILE, OPEN_FILE */ + svn_checksum_t *src_checksum; /* For ADD_FILE, OPEN_FILE */ + svn_stream_t *base_stream; /* For ADD_FILE, OPEN_FILE */ + const svn_checksum_t *base_checksum; /* For ADD_FILE, OPEN_FILE */ + + apr_array_header_t *prop_mods; /* For all except DELETE + List of svn_prop_t */ + + svn_boolean_t performed_stat; /* Verified kind with repository */ +} mtcc_op_t; + +/* Check if the mtcc doesn't contain any modifications yet */ +#define MTCC_UNMODIFIED(mtcc) \ + ((mtcc->root_op->kind == OP_OPEN_DIR \ + || mtcc->root_op->kind == OP_OPEN_FILE) \ + && (mtcc->root_op->prop_mods == NULL \ + || !mtcc->root_op->prop_mods->nelts) \ + && (mtcc->root_op->children == NULL \ + || !mtcc->root_op->children->nelts)) + +struct svn_client__mtcc_t +{ + apr_pool_t *pool; + svn_revnum_t head_revision; + svn_revnum_t base_revision; + + svn_ra_session_t *ra_session; + svn_client_ctx_t *ctx; + + mtcc_op_t *root_op; +}; + +static mtcc_op_t * +mtcc_op_create(const char *name, + svn_boolean_t add, + svn_boolean_t directory, + apr_pool_t *result_pool) +{ + mtcc_op_t *op; + + op = apr_pcalloc(result_pool, sizeof(*op)); + op->name = name ? apr_pstrdup(result_pool, name) : ""; + + if (add) + op->kind = directory ? OP_ADD_DIR : OP_ADD_FILE; + else + op->kind = directory ? OP_OPEN_DIR : OP_OPEN_FILE; + + if (directory) + op->children = apr_array_make(result_pool, 4, sizeof(mtcc_op_t *)); + + op->src_rev = SVN_INVALID_REVNUM; + + return op; +} + +static svn_error_t * +mtcc_op_find(mtcc_op_t **op, + svn_boolean_t *created, + const char *relpath, + mtcc_op_t *base_op, + svn_boolean_t find_existing, + svn_boolean_t find_deletes, + svn_boolean_t create_file, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *name; + const char *child; + int i; + + assert(svn_relpath_is_canonical(relpath)); + if (created) + *created = FALSE; + + if (SVN_PATH_IS_EMPTY(relpath)) + { + if (find_existing) + *op = base_op; + else + *op = NULL; + + return SVN_NO_ERROR; + } + + child = strchr(relpath, '/'); + + if (child) + { + name = apr_pstrmemdup(scratch_pool, relpath, (child-relpath)); + child++; /* Skip '/' */ + } + else + name = relpath; + + if (!base_op->children) + { + if (!created) + { + *op = NULL; + return SVN_NO_ERROR; + } + else + return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't operate on '%s' because '%s' is not a " + "directory"), + name, base_op->name); + } + + for (i = base_op->children->nelts-1; i >= 0 ; i--) + { + mtcc_op_t *cop; + + cop = APR_ARRAY_IDX(base_op->children, i, mtcc_op_t *); + + if (! strcmp(cop->name, name) + && (find_deletes || cop->kind != OP_DELETE)) + { + return svn_error_trace( + mtcc_op_find(op, created, child ? child : "", cop, + find_existing, find_deletes, create_file, + result_pool, scratch_pool)); + } + } + + if (!created) + { + *op = NULL; + return SVN_NO_ERROR; + } + + { + mtcc_op_t *cop; + + cop = mtcc_op_create(name, FALSE, child || !create_file, result_pool); + + APR_ARRAY_PUSH(base_op->children, mtcc_op_t *) = cop; + + if (!child) + { + *op = cop; + *created = TRUE; + return SVN_NO_ERROR; + } + + return svn_error_trace( + mtcc_op_find(op, created, child, cop, find_existing, + find_deletes, create_file, + result_pool, scratch_pool)); + } +} + +/* Gets the original repository location of RELPATH, checking things + like copies, moves, etc. */ +static svn_error_t * +get_origin(svn_boolean_t *done, + const char **origin_relpath, + svn_revnum_t *rev, + mtcc_op_t *op, + const char *relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *child; + const char *name; + if (SVN_PATH_IS_EMPTY(relpath)) + { + if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) + *done = TRUE; + *origin_relpath = op->src_relpath + ? apr_pstrdup(result_pool, op->src_relpath) + : NULL; + *rev = op->src_rev; + return SVN_NO_ERROR; + } + + child = strchr(relpath, '/'); + if (child) + { + name = apr_pstrmemdup(scratch_pool, relpath, child-relpath); + child++; /* Skip '/' */ + } + else + name = relpath; + + if (op->children && op->children->nelts) + { + int i; + + for (i = op->children->nelts-1; i >= 0; i--) + { + mtcc_op_t *cop; + + cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); + + if (! strcmp(cop->name, name)) + { + if (cop->kind == OP_DELETE) + { + *done = TRUE; + return SVN_NO_ERROR; + } + + SVN_ERR(get_origin(done, origin_relpath, rev, + cop, child ? child : "", + result_pool, scratch_pool)); + + if (*origin_relpath || *done) + return SVN_NO_ERROR; + + break; + } + } + } + + if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) + { + *done = TRUE; + if (op->src_relpath) + { + *origin_relpath = svn_relpath_join(op->src_relpath, relpath, + result_pool); + *rev = op->src_rev; + } + } + + return SVN_NO_ERROR; +} + +/* Obtains the original repository location for an mtcc relpath as + *ORIGIN_RELPATH @ *REV, if it has one. If it has not and IGNORE_ENOENT + is TRUE report *ORIGIN_RELPATH as NULL, otherwise return an error */ +static svn_error_t * +mtcc_get_origin(const char **origin_relpath, + svn_revnum_t *rev, + const char *relpath, + svn_boolean_t ignore_enoent, + svn_client__mtcc_t *mtcc, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t done = FALSE; + + *origin_relpath = NULL; + *rev = SVN_INVALID_REVNUM; + + SVN_ERR(get_origin(&done, origin_relpath, rev, mtcc->root_op, relpath, + result_pool, scratch_pool)); + + if (!*origin_relpath && !done) + { + *origin_relpath = apr_pstrdup(result_pool, relpath); + *rev = mtcc->base_revision; + } + else if (!ignore_enoent) + { + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("No origin found for node at '%s'"), + relpath); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_create(svn_client__mtcc_t **mtcc, + const char *anchor_url, + svn_revnum_t base_revision, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *mtcc_pool; + + mtcc_pool = svn_pool_create(result_pool); + + *mtcc = apr_pcalloc(mtcc_pool, sizeof(**mtcc)); + (*mtcc)->pool = mtcc_pool; + + (*mtcc)->root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc_pool); + + (*mtcc)->ctx = ctx; + + SVN_ERR(svn_client_open_ra_session2(&(*mtcc)->ra_session, anchor_url, + NULL /* wri_abspath */, ctx, + mtcc_pool, scratch_pool)); + + SVN_ERR(svn_ra_get_latest_revnum((*mtcc)->ra_session, &(*mtcc)->head_revision, + scratch_pool)); + + if (SVN_IS_VALID_REVNUM(base_revision)) + (*mtcc)->base_revision = base_revision; + else + (*mtcc)->base_revision = (*mtcc)->head_revision; + + if ((*mtcc)->base_revision > (*mtcc)->head_revision) + return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld (HEAD is %ld)"), + base_revision, (*mtcc)->head_revision); + + return SVN_NO_ERROR; +} + +static svn_error_t * +update_copy_src(mtcc_op_t *op, + const char *add_relpath, + apr_pool_t *result_pool) +{ + int i; + + if (op->src_relpath) + op->src_relpath = svn_relpath_join(add_relpath, op->src_relpath, + result_pool); + + if (!op->children) + return SVN_NO_ERROR; + + for (i = 0; i < op->children->nelts; i++) + { + mtcc_op_t *cop; + + cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); + + SVN_ERR(update_copy_src(cop, add_relpath, result_pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +mtcc_reparent(const char *new_anchor_url, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + const char *session_url; + const char *up; + + SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, + scratch_pool)); + + up = svn_uri_skip_ancestor(new_anchor_url, session_url, scratch_pool); + + if (! up) + { + return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, + _("'%s' is not an ancestor of '%s'"), + new_anchor_url, session_url); + } + else if (!*up) + { + return SVN_NO_ERROR; /* Same url */ + } + + /* Update copy origins recursively...:( */ + SVN_ERR(update_copy_src(mtcc->root_op, up, mtcc->pool)); + + SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor_url, scratch_pool)); + + /* Create directory open operations for new ancestors */ + while (*up) + { + mtcc_op_t *root_op; + + mtcc->root_op->name = svn_relpath_basename(up, mtcc->pool); + up = svn_relpath_dirname(up, scratch_pool); + + root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc->pool); + + APR_ARRAY_PUSH(root_op->children, mtcc_op_t *) = mtcc->root_op; + + mtcc->root_op = root_op; + } + + return SVN_NO_ERROR; +} + +/* Check if it is safe to create a new node at NEW_RELPATH. Return a proper + error if it is not */ +static svn_error_t * +mtcc_verify_create(svn_client__mtcc_t *mtcc, + const char *new_relpath, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + + if (*new_relpath || !MTCC_UNMODIFIED(mtcc)) + { + mtcc_op_t *op; + + SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, FALSE, + FALSE, mtcc->pool, scratch_pool)); + + if (op) + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '%s' already exists"), + new_relpath); + + SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE, + FALSE, mtcc->pool, scratch_pool)); + + if (op) + return SVN_NO_ERROR; /* Node is explicitly deleted. We can replace */ + } + + /* mod_dav_svn used to allow overwriting existing directories. Let's hide + that for users of this api */ + SVN_ERR(svn_client__mtcc_check_path(&kind, new_relpath, FALSE, + mtcc, scratch_pool)); + + if (kind != svn_node_none) + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '%s' already exists"), + new_relpath); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_client__mtcc_add_add_file(const char *relpath, + svn_stream_t *src_stream, + const svn_checksum_t *src_checksum, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + svn_boolean_t created; + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream); + + SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool)); + + if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) + { + /* Turn the root operation into a file addition */ + op = mtcc->root_op; + } + else + { + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE, + TRUE, mtcc->pool, scratch_pool)); + + if (!op || !created) + { + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't add file at '%s'"), + relpath); + } + } + + op->kind = OP_ADD_FILE; + op->src_stream = src_stream; + op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool) + : NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_copy(const char *src_relpath, + svn_revnum_t revision, + const char *dst_relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + svn_boolean_t created; + svn_node_kind_t kind; + + SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath) + && svn_relpath_is_canonical(dst_relpath)); + + if (! SVN_IS_VALID_REVNUM(revision)) + revision = mtcc->head_revision; + else if (revision > mtcc->head_revision) + { + return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), revision); + } + + SVN_ERR(mtcc_verify_create(mtcc, dst_relpath, scratch_pool)); + + /* Subversion requires the kind of a copy */ + SVN_ERR(svn_ra_check_path(mtcc->ra_session, src_relpath, revision, &kind, + scratch_pool)); + + if (kind != svn_node_dir && kind != svn_node_file) + { + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("Path '%s' not found in revision %ld"), + src_relpath, revision); + } + + SVN_ERR(mtcc_op_find(&op, &created, dst_relpath, mtcc->root_op, FALSE, FALSE, + (kind == svn_node_file), mtcc->pool, scratch_pool)); + + if (!op || !created) + { + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't add node at '%s'"), + dst_relpath); + } + + op->kind = (kind == svn_node_file) ? OP_ADD_FILE : OP_ADD_DIR; + op->src_relpath = apr_pstrdup(mtcc->pool, src_relpath); + op->src_rev = revision; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_delete(const char *relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + svn_boolean_t created; + svn_node_kind_t kind; + + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); + + SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, + mtcc, scratch_pool)); + + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("Can't delete node at '%s' as it " + "does not exist"), + relpath); + + if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) + { + /* Turn root operation into delete */ + op = mtcc->root_op; + } + else + { + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE, + TRUE, mtcc->pool, scratch_pool)); + + if (!op || !created) + { + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't delete node at '%s'"), + relpath); + } + } + + op->kind = OP_DELETE; + op->children = NULL; + op->prop_mods = NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_mkdir(const char *relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + svn_boolean_t created; + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); + + SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool)); + + if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) + { + /* Turn the root of the operation in an MKDIR */ + mtcc->root_op->kind = OP_ADD_DIR; + + return SVN_NO_ERROR; + } + + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE, + FALSE, mtcc->pool, scratch_pool)); + + if (!op || !created) + { + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't create directory at '%s'"), + relpath); + } + + op->kind = OP_ADD_DIR; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_move(const char *src_relpath, + const char *dst_relpath, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + const char *origin_relpath; + svn_revnum_t origin_rev; + + SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, + src_relpath, FALSE, mtcc, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_client__mtcc_add_copy(src_relpath, mtcc->base_revision, + dst_relpath, mtcc, scratch_pool)); + SVN_ERR(svn_client__mtcc_add_delete(src_relpath, mtcc, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Baton for mtcc_prop_getter */ +struct mtcc_prop_get_baton +{ + svn_client__mtcc_t *mtcc; + const char *relpath; + svn_cancel_func_t cancel_func; + void *cancel_baton; +}; + +/* Implements svn_wc_canonicalize_svn_prop_get_file_t */ +static svn_error_t * +mtcc_prop_getter(const svn_string_t **mime_type, + svn_stream_t *stream, + void *baton, + apr_pool_t *pool) +{ + struct mtcc_prop_get_baton *mpgb = baton; + const char *origin_relpath; + svn_revnum_t origin_rev; + apr_hash_t *props = NULL; + + mtcc_op_t *op; + + if (mime_type) + *mime_type = NULL; + + /* Check if we have the information locally */ + SVN_ERR(mtcc_op_find(&op, NULL, mpgb->relpath, mpgb->mtcc->root_op, TRUE, + FALSE, FALSE, pool, pool)); + + if (op) + { + if (mime_type) + { + int i; + + for (i = 0; op->prop_mods && i < op->prop_mods->nelts; i++) + { + const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, + svn_prop_t); + + if (! strcmp(mod->name, SVN_PROP_MIME_TYPE)) + { + *mime_type = svn_string_dup(mod->value, pool); + mime_type = NULL; + } + } + } + + if (stream && op->src_stream) + { + svn_stream_mark_t *mark; + svn_error_t *err; + + /* Is the source stream capable of being read multiple times? */ + err = svn_stream_mark(op->src_stream, &mark, pool); + + if (err && err->apr_err != SVN_ERR_STREAM_SEEK_NOT_SUPPORTED) + return svn_error_trace(err); + svn_error_clear(err); + + if (!err) + { + err = svn_stream_copy3(svn_stream_disown(op->src_stream, pool), + svn_stream_disown(stream, pool), + mpgb->cancel_func, mpgb->cancel_baton, + pool); + + SVN_ERR(svn_error_compose_create( + err, + svn_stream_seek(op->src_stream, mark))); + } + /* else: ### Create tempfile? */ + + stream = NULL; /* Stream is handled */ + } + } + + if (!stream && !mime_type) + return SVN_NO_ERROR; + + SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, mpgb->relpath, TRUE, + mpgb->mtcc, pool, pool)); + + if (!origin_relpath) + return SVN_NO_ERROR; /* Nothing to fetch at repository */ + + SVN_ERR(svn_ra_get_file(mpgb->mtcc->ra_session, origin_relpath, origin_rev, + stream, NULL, mime_type ? &props : NULL, pool)); + + if (mime_type && props) + *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_propset(const char *relpath, + const char *propname, + const svn_string_t *propval, + svn_boolean_t skip_checks, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); + + if (! svn_prop_name_is_valid(propname)) + return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("Bad property name: '%s'"), propname); + + if (svn_prop_is_known_svn_rev_prop(propname)) + return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("Revision property '%s' not allowed " + "in this context"), propname); + + if (svn_property_kind2(propname) == svn_prop_wc_kind) + return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, + _("'%s' is a wcprop, thus not accessible " + "to clients"), propname); + + if (!skip_checks && svn_prop_needs_translation(propname)) + { + svn_string_t *translated_value; + SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL, + NULL, propval, + NULL, FALSE, + scratch_pool, scratch_pool), + _("Error normalizing property value")); + + propval = translated_value; + } + + if (propval && svn_prop_is_svn_prop(propname)) + { + struct mtcc_prop_get_baton mpbg; + svn_node_kind_t kind; + SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, mtcc, + scratch_pool)); + + mpbg.mtcc = mtcc; + mpbg.relpath = relpath; + mpbg.cancel_func = mtcc->ctx->cancel_func; + mpbg.cancel_baton = mtcc->ctx->cancel_baton; + + SVN_ERR(svn_wc_canonicalize_svn_prop(&propval, propname, propval, + relpath, kind, skip_checks, + mtcc_prop_getter, &mpbg, + scratch_pool)); + } + + if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) + { + svn_node_kind_t kind; + + /* Probing the node for an unmodified root will fix the node type to + a file if necessary */ + + SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, + mtcc, scratch_pool)); + + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't set properties at not existing '%s'"), + relpath); + + op = mtcc->root_op; + } + else + { + SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE, + FALSE, mtcc->pool, scratch_pool)); + + if (!op) + { + svn_node_kind_t kind; + svn_boolean_t created; + + /* ### TODO: Check if this node is within a newly copied directory, + and update origin values accordingly */ + + SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, + mtcc, scratch_pool)); + + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't set properties at not existing '%s'"), + relpath); + + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE, + (kind != svn_node_dir), + mtcc->pool, scratch_pool)); + + SVN_ERR_ASSERT(op != NULL); + } + } + + if (!op->prop_mods) + op->prop_mods = apr_array_make(mtcc->pool, 4, sizeof(svn_prop_t)); + + { + svn_prop_t propchange; + propchange.name = apr_pstrdup(mtcc->pool, propname); + + if (propval) + propchange.value = svn_string_dup(propval, mtcc->pool); + else + propchange.value = NULL; + + APR_ARRAY_PUSH(op->prop_mods, svn_prop_t) = propchange; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_add_update_file(const char *relpath, + svn_stream_t *src_stream, + const svn_checksum_t *src_checksum, + svn_stream_t *base_stream, + const svn_checksum_t *base_checksum, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + mtcc_op_t *op; + svn_boolean_t created; + svn_node_kind_t kind; + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream); + + SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, + mtcc, scratch_pool)); + + if (kind != svn_node_file) + return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, + _("Can't update '%s' because it is not a file"), + relpath); + + SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE, + TRUE, mtcc->pool, scratch_pool)); + + if (!op + || (op->kind != OP_OPEN_FILE && op->kind != OP_ADD_FILE) + || (op->src_stream != NULL)) + { + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Can't update file at '%s'"), relpath); + } + + op->src_stream = src_stream; + op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool) + : NULL; + + op->base_stream = base_stream; + op->base_checksum = base_checksum ? svn_checksum_dup(base_checksum, + mtcc->pool) + : NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_check_path(svn_node_kind_t *kind, + const char *relpath, + svn_boolean_t check_repository, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + const char *origin_relpath; + svn_revnum_t origin_rev; + mtcc_op_t *op; + + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); + + if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc) + && !mtcc->root_op->performed_stat) + { + /* We know nothing about the root. Perhaps it is a file? */ + SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision, + kind, scratch_pool)); + + mtcc->root_op->performed_stat = TRUE; + if (*kind == svn_node_file) + { + mtcc->root_op->kind = OP_OPEN_FILE; + mtcc->root_op->children = NULL; + } + return SVN_NO_ERROR; + } + + SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE, + FALSE, mtcc->pool, scratch_pool)); + + if (!op || (check_repository && !op->performed_stat)) + { + SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, + relpath, TRUE, mtcc, + scratch_pool, scratch_pool)); + + if (!origin_relpath) + *kind = svn_node_none; + else + SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath, + origin_rev, kind, scratch_pool)); + + if (op && *kind == svn_node_dir) + { + if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) + op->performed_stat = TRUE; + else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) + return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, + _("Can't perform file operation " + "on '%s' as it is not a file"), + relpath); + } + else if (op && *kind == svn_node_file) + { + if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) + op->performed_stat = TRUE; + else if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) + return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't perform directory operation " + "on '%s' as it is not a directory"), + relpath); + } + else if (op && (op->kind == OP_OPEN_DIR || op->kind == OP_OPEN_FILE)) + { + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("Can't open '%s' as it does not exist"), + relpath); + } + + return SVN_NO_ERROR; + } + + /* op != NULL */ + if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) + { + *kind = svn_node_dir; + return SVN_NO_ERROR; + } + else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) + { + *kind = svn_node_file; + return SVN_NO_ERROR; + } + SVN_ERR_MALFUNCTION(); /* No other kinds defined as delete is filtered */ +} + +static svn_error_t * +commit_properties(const svn_delta_editor_t *editor, + const mtcc_op_t *op, + void *node_baton, + apr_pool_t *scratch_pool) +{ + int i; + apr_pool_t *iterpool; + + if (!op->prop_mods || op->prop_mods->nelts == 0) + return SVN_NO_ERROR; + + iterpool = svn_pool_create(scratch_pool); + for (i = 0; i < op->prop_mods->nelts; i++) + { + const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, svn_prop_t); + + svn_pool_clear(iterpool); + + if (op->kind == OP_ADD_DIR || op->kind == OP_OPEN_DIR) + SVN_ERR(editor->change_dir_prop(node_baton, mod->name, mod->value, + iterpool)); + else if (op->kind == OP_ADD_FILE || op->kind == OP_OPEN_FILE) + SVN_ERR(editor->change_file_prop(node_baton, mod->name, mod->value, + iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Handles updating a file to a delta editor and then closes it */ +static svn_error_t * +commit_file(const svn_delta_editor_t *editor, + mtcc_op_t *op, + void *file_baton, + const char *session_url, + const char *relpath, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *text_checksum = NULL; + svn_checksum_t *src_checksum = op->src_checksum; + SVN_ERR(commit_properties(editor, op, file_baton, scratch_pool)); + + if (op->src_stream) + { + const char *base_checksum = NULL; + apr_pool_t *txdelta_pool = scratch_pool; + svn_txdelta_window_handler_t window_handler; + void *handler_baton; + svn_stream_t *src_stream = op->src_stream; + + if (op->base_checksum && op->base_checksum->kind == svn_checksum_md5) + base_checksum = svn_checksum_to_cstring(op->base_checksum, scratch_pool); + + /* ### TODO: Future enhancement: Allocate in special pool and send + files after the true edit operation, like a wc commit */ + SVN_ERR(editor->apply_textdelta(file_baton, base_checksum, txdelta_pool, + &window_handler, &handler_baton)); + + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + + notify = svn_wc_create_notify_url( + svn_path_url_add_component2(session_url, relpath, + scratch_pool), + svn_wc_notify_commit_postfix_txdelta, + scratch_pool); + + notify->path = relpath; + notify->kind = svn_node_file; + + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); + } + + if (window_handler != svn_delta_noop_window_handler) + { + if (!src_checksum || src_checksum->kind != svn_checksum_md5) + src_stream = svn_stream_checksummed2(src_stream, &src_checksum, NULL, + svn_checksum_md5, + TRUE, scratch_pool); + + if (!op->base_stream) + SVN_ERR(svn_txdelta_send_stream(src_stream, + window_handler, handler_baton, NULL, + scratch_pool)); + else + SVN_ERR(svn_txdelta_run(op->base_stream, src_stream, + window_handler, handler_baton, + svn_checksum_md5, NULL, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); + } + + SVN_ERR(svn_stream_close(src_stream)); + if (op->base_stream) + SVN_ERR(svn_stream_close(op->base_stream)); + } + + if (src_checksum && src_checksum->kind == svn_checksum_md5) + text_checksum = svn_checksum_to_cstring(src_checksum, scratch_pool); + + return svn_error_trace(editor->close_file(file_baton, text_checksum, + scratch_pool)); +} + +/* Handles updating a directory to a delta editor and then closes it */ +static svn_error_t * +commit_directory(const svn_delta_editor_t *editor, + mtcc_op_t *op, + const char *relpath, + svn_revnum_t base_rev, + void *dir_baton, + const char *session_url, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + SVN_ERR(commit_properties(editor, op, dir_baton, scratch_pool)); + + if (op->children && op->children->nelts > 0) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + for (i = 0; i < op->children->nelts; i++) + { + mtcc_op_t *cop; + const char * child_relpath; + void *child_baton; + + cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); + + svn_pool_clear(iterpool); + + if (ctx->cancel_func) + SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); + + child_relpath = svn_relpath_join(relpath, cop->name, iterpool); + + switch (cop->kind) + { + case OP_DELETE: + SVN_ERR(editor->delete_entry(child_relpath, base_rev, + dir_baton, iterpool)); + break; + + case OP_ADD_DIR: + SVN_ERR(editor->add_directory(child_relpath, dir_baton, + cop->src_relpath + ? svn_path_url_add_component2( + session_url, + cop->src_relpath, + iterpool) + : NULL, + cop->src_rev, + iterpool, &child_baton)); + SVN_ERR(commit_directory(editor, cop, child_relpath, + SVN_INVALID_REVNUM, child_baton, + session_url, ctx, iterpool)); + break; + case OP_OPEN_DIR: + SVN_ERR(editor->open_directory(child_relpath, dir_baton, + base_rev, iterpool, &child_baton)); + SVN_ERR(commit_directory(editor, cop, child_relpath, + base_rev, child_baton, + session_url, ctx, iterpool)); + break; + + case OP_ADD_FILE: + SVN_ERR(editor->add_file(child_relpath, dir_baton, + cop->src_relpath + ? svn_path_url_add_component2( + session_url, + cop->src_relpath, + iterpool) + : NULL, + cop->src_rev, + iterpool, &child_baton)); + SVN_ERR(commit_file(editor, cop, child_baton, + session_url, child_relpath, ctx, iterpool)); + break; + case OP_OPEN_FILE: + SVN_ERR(editor->open_file(child_relpath, dir_baton, base_rev, + iterpool, &child_baton)); + SVN_ERR(commit_file(editor, cop, child_baton, + session_url, child_relpath, ctx, iterpool)); + break; + + default: + SVN_ERR_MALFUNCTION(); + } + } + } + + return svn_error_trace(editor->close_directory(dir_baton, scratch_pool)); +} + + +/* Helper function to recursively create svn_client_commit_item3_t items + to provide to the log message callback */ +static svn_error_t * +add_commit_items(mtcc_op_t *op, + const char *session_url, + const char *url, + apr_array_header_t *commit_items, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if ((op->kind != OP_OPEN_DIR && op->kind != OP_OPEN_FILE) + || (op->prop_mods && op->prop_mods->nelts) + || (op->src_stream)) + { + svn_client_commit_item3_t *item; + + item = svn_client_commit_item3_create(result_pool); + + item->path = NULL; + if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) + item->kind = svn_node_dir; + else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) + item->kind = svn_node_file; + else + item->kind = svn_node_unknown; + + item->url = apr_pstrdup(result_pool, url); + item->session_relpath = svn_uri_skip_ancestor(session_url, item->url, + result_pool); + + if (op->src_relpath) + { + item->copyfrom_url = svn_path_url_add_component2(session_url, + op->src_relpath, + result_pool); + item->copyfrom_rev = op->src_rev; + item->state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY; + } + else + item->copyfrom_rev = SVN_INVALID_REVNUM; + + if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) + item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; + else if (op->kind == OP_DELETE) + item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; + /* else item->state_flags = 0; */ + + if (op->prop_mods && op->prop_mods->nelts) + item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS; + + if (op->src_stream) + item->state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS; + + APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; + } + + if (op->children && op->children->nelts) + { + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + for (i = 0; i < op->children->nelts; i++) + { + mtcc_op_t *cop; + const char * child_url; + + cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); + + svn_pool_clear(iterpool); + + child_url = svn_path_url_add_component2(url, cop->name, iterpool); + + SVN_ERR(add_commit_items(cop, session_url, child_url, commit_items, + result_pool, iterpool)); + } + + svn_pool_destroy(iterpool); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_client__mtcc_commit(apr_hash_t *revprop_table, + svn_commit_callback2_t commit_callback, + void *commit_baton, + svn_client__mtcc_t *mtcc, + apr_pool_t *scratch_pool) +{ + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + apr_hash_t *commit_revprops; + svn_node_kind_t kind; + svn_error_t *err; + const char *session_url; + const char *log_msg; + + if (MTCC_UNMODIFIED(mtcc)) + { + /* No changes -> no revision. Easy out */ + svn_pool_destroy(mtcc->pool); + return SVN_NO_ERROR; + } + + SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, scratch_pool)); + + if (mtcc->root_op->kind != OP_OPEN_DIR) + { + const char *name; + + svn_uri_split(&session_url, &name, session_url, scratch_pool); + + if (*name) + { + SVN_ERR(mtcc_reparent(session_url, mtcc, scratch_pool)); + + SVN_ERR(svn_ra_reparent(mtcc->ra_session, session_url, scratch_pool)); + } + } + + /* Create new commit items and add them to the array. */ + if (SVN_CLIENT__HAS_LOG_MSG_FUNC(mtcc->ctx)) + { + svn_client_commit_item3_t *item; + const char *tmp_file; + apr_array_header_t *commit_items + = apr_array_make(scratch_pool, 32, sizeof(item)); + + SVN_ERR(add_commit_items(mtcc->root_op, session_url, session_url, + commit_items, scratch_pool, scratch_pool)); + + SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, + mtcc->ctx, scratch_pool)); + + if (! log_msg) + return SVN_NO_ERROR; + } + else + log_msg = ""; + + SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, + log_msg, mtcc->ctx, scratch_pool)); + + /* Ugly corner case: The ra session might have died while we were waiting + for the callback */ + + err = svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision, &kind, + scratch_pool); + + if (err) + { + svn_error_t *err2 = svn_client_open_ra_session2(&mtcc->ra_session, + session_url, + NULL, mtcc->ctx, + mtcc->pool, + scratch_pool); + + if (err2) + { + svn_pool_destroy(mtcc->pool); + return svn_error_trace(svn_error_compose_create(err, err2)); + } + svn_error_clear(err); + + SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", + mtcc->base_revision, &kind, scratch_pool)); + } + + if (kind != svn_node_dir) + return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't commit to '%s' because it " + "is not a directory"), + session_url); + + /* Beware that the editor object must not live longer than the MTCC. + Otherwise, txn objects etc. in EDITOR may live longer than their + respective FS objects. So, we can't use SCRATCH_POOL here. */ + SVN_ERR(svn_ra_get_commit_editor3(mtcc->ra_session, &editor, &edit_baton, + commit_revprops, + commit_callback, commit_baton, + NULL /* lock_tokens */, + FALSE /* keep_locks */, + mtcc->pool)); + + err = editor->open_root(edit_baton, mtcc->base_revision, scratch_pool, &root_baton); + + if (!err) + err = commit_directory(editor, mtcc->root_op, "", mtcc->base_revision, + root_baton, session_url, mtcc->ctx, scratch_pool); + + if (!err) + { + if (mtcc->ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(session_url, + svn_wc_notify_commit_finalizing, + scratch_pool); + mtcc->ctx->notify_func2(mtcc->ctx->notify_baton2, notify, + scratch_pool); + } + SVN_ERR(editor->close_edit(edit_baton, scratch_pool)); + } + else + err = svn_error_compose_create(err, + editor->abort_edit(edit_baton, scratch_pool)); + + svn_pool_destroy(mtcc->pool); + + return svn_error_trace(err); +} diff --git a/contrib/subversion/subversion/libsvn_client/patch.c b/contrib/subversion/subversion/libsvn_client/patch.c index b7fbf0614..6d8d0a463 100644 --- a/contrib/subversion/subversion/libsvn_client/patch.c +++ b/contrib/subversion/subversion/libsvn_client/patch.c @@ -48,6 +48,7 @@ #include "private/svn_dep_compat.h" #include "private/svn_string_private.h" #include "private/svn_subr_private.h" +#include "private/svn_sorts_private.h" typedef struct hunk_info_t { /* The hunk. */ @@ -208,9 +209,6 @@ typedef struct patch_target_t { /* 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; @@ -232,6 +230,10 @@ typedef struct patch_target_t { * (i.e. a new file was added on top locally deleted node). */ svn_boolean_t replaced; + /* Set if the target is supposed to be moved by the patch. + * This applies to --git diffs which carry "rename from/to" headers. */ + const char *move_target_abspath; + /* True if the target has the executable bit set. */ svn_boolean_t executable; @@ -321,7 +323,8 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords, const char *rev_str; const char *author; const char *url; - const char *root_url; + const char *repos_root_url; + const char *repos_relpath; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, &changed_date, @@ -330,15 +333,17 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords, 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, + SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, &repos_root_url, + NULL, wc_ctx, local_abspath, scratch_pool, scratch_pool)); + url = svn_path_url_add_component2(repos_root_url, repos_relpath, + scratch_pool); + SVN_ERR(svn_subst_build_keywords3(keywords, keywords_val->data, - rev_str, url, root_url, changed_date, + rev_str, url, repos_root_url, + changed_date, author, result_pool)); } @@ -481,6 +486,8 @@ resolve_target_path(patch_target_t *target, SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL, wc_ctx, target->local_abspath, result_pool, scratch_pool)); + /* ### BUG: moved_to_abspath contains the target where the op-root was + ### moved to... not the target itself! */ if (moved_to_abspath) { target->local_abspath = moved_to_abspath; @@ -942,6 +949,12 @@ choose_target_filename(const svn_patch_t *patch) if (strcmp(patch->new_filename, "/dev/null") == 0) return patch->old_filename; + /* If the patch renames the target, use the old name while + * applying hunks. The target will be renamed to the new name + * after hunks have been applied. */ + if (patch->operation == svn_diff_op_moved) + return patch->old_filename; + old = svn_path_component_count(patch->old_filename); new = svn_path_component_count(patch->new_filename); @@ -991,7 +1004,7 @@ init_patch_target(patch_target_t **patch_target, hi; hi = apr_hash_next(hi)) { - svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi); + svn_prop_patch_t *prop_patch = apr_hash_this_val(hi); if (! has_prop_changes) has_prop_changes = prop_patch->hunks->nelts > 0; else @@ -1021,6 +1034,7 @@ init_patch_target(patch_target_t **patch_target, SVN_ERR(resolve_target_path(target, choose_target_filename(patch), wcroot_abspath, strip_count, prop_changes_only, wc_ctx, result_pool, scratch_pool)); + *patch_target = target; if (! target->skipped) { const char *diff_header; @@ -1079,6 +1093,64 @@ init_patch_target(patch_target_t **patch_target, target->added = TRUE; else if (patch->operation == svn_diff_op_deleted) target->deleted = TRUE; + else if (patch->operation == svn_diff_op_moved) + { + const char *move_target_path; + const char *move_target_relpath; + svn_boolean_t under_root; + svn_node_kind_t kind_on_disk; + svn_node_kind_t wc_kind; + + move_target_path = svn_dirent_internal_style(patch->new_filename, + scratch_pool); + + if (strip_count > 0) + SVN_ERR(strip_path(&move_target_path, move_target_path, + strip_count, scratch_pool, scratch_pool)); + + if (svn_dirent_is_absolute(move_target_path)) + { + move_target_relpath = svn_dirent_is_child(wcroot_abspath, + move_target_path, + scratch_pool); + if (! move_target_relpath) + { + /* The move 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; + return SVN_NO_ERROR; + } + } + else + move_target_relpath = move_target_path; + + /* Make sure the move target path is secure to use. */ + SVN_ERR(svn_dirent_is_under_root(&under_root, + &target->move_target_abspath, + wcroot_abspath, + move_target_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; + } + + SVN_ERR(svn_io_check_path(target->move_target_abspath, + &kind_on_disk, scratch_pool)); + SVN_ERR(svn_wc_read_kind2(&wc_kind, wc_ctx, + target->move_target_abspath, + FALSE, FALSE, scratch_pool)); + if (kind_on_disk != svn_node_none || wc_kind != svn_node_none) + { + /* The move target path already exists on disk. Skip target. */ + target->skipped = TRUE; + target->move_target_abspath = NULL; + return SVN_NO_ERROR; + } + } if (! target->is_symlink) { @@ -1136,8 +1208,8 @@ init_patch_target(patch_target_t **patch_target, 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); + const char *prop_name = apr_hash_this_key(hi); + svn_prop_patch_t *prop_patch = apr_hash_this_val(hi); prop_patch_target_t *prop_target; SVN_ERR(init_prop_target(&prop_target, @@ -1150,7 +1222,6 @@ init_patch_target(patch_target_t **patch_target, } } - *patch_target = target; return SVN_NO_ERROR; } @@ -1510,7 +1581,8 @@ match_existing_target(svn_boolean_t *match, /* 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. + * line can be determined, set HI->REJECTED to TRUE. PREVIOUS_OFFSET + * is the offset at which the previous matching hunk was applied, or zero. * IGNORE_WHITESPACE tells whether whitespace should be considered when * matching. IS_PROP_HUNK indicates whether the hunk patches file content * or a property. @@ -1522,6 +1594,7 @@ 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_linenum_t previous_offset, svn_boolean_t ignore_whitespace, svn_boolean_t is_prop_hunk, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -1531,7 +1604,7 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, svn_linenum_t original_start; svn_boolean_t already_applied; - original_start = svn_diff_hunk_get_original_start(hunk); + original_start = svn_diff_hunk_get_original_start(hunk) + previous_offset; already_applied = FALSE; /* An original offset of zero means that this hunk wants to create @@ -1639,7 +1712,9 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, modified_start = svn_diff_hunk_get_modified_start(hunk); if (modified_start == 0) { - /* Patch wants to delete the file. */ + /* Patch wants to delete the file. + + ### locally_deleted is always false here? */ already_applied = target->locally_deleted; } else @@ -1660,27 +1735,85 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target, if (! already_applied) { - /* Scan the whole file again from the start. */ - SVN_ERR(seek_to_line(content, 1, scratch_pool)); + int i; + svn_linenum_t search_start = 1, search_end = 0; + svn_linenum_t matched_line2; + + /* Search for closest match before or after original + start. We have no backward search so search forwards + from the previous match (or start of file) to the + original start looking for the last match. Then + search forwards from the original start looking for a + better match. Finally search forwards from the start + of file to the previous hunk if that could result in + a better match. */ + + for (i = content->hunks->nelts; i > 0; --i) + { + const hunk_info_t *prev + = APR_ARRAY_IDX(content->hunks, i - 1, const hunk_info_t *); + if (!prev->rejected) + { + svn_linenum_t length; - /* Scan forward towards the hunk's line and look for a line - * where the hunk matches. */ + length = svn_diff_hunk_get_original_length(prev->hunk); + search_start = prev->matched_line + length; + break; + } + } + + /* Search from the previous match, or start of file, + towards the original location. */ + SVN_ERR(seek_to_line(content, search_start, scratch_pool)); 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) + /* If a match we only need to search forwards for a + better match, otherwise to the end of the file. */ + if (matched_line) + search_end = original_start + (original_start - matched_line); + + /* Search from original location, towards the end. */ + SVN_ERR(seek_to_line(content, original_start + 1, scratch_pool)); + SVN_ERR(scan_for_match(&matched_line2, content, hunk, + TRUE, search_end, fuzz, ignore_whitespace, + FALSE, cancel_func, cancel_baton, + scratch_pool)); + + /* Chose the forward match if it is closer than the + backward match or if there is no backward match. */ + if (matched_line2 + && (!matched_line + || (matched_line2 - original_start + < original_start - matched_line))) + matched_line = matched_line2; + + /* Search from before previous hunk if there could be a + better match. */ + if (search_start > 1 + && (!matched_line + || (matched_line > original_start + && (matched_line - original_start + > original_start - search_start)))) { - /* 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, + svn_linenum_t search_start2 = 1; + + if (matched_line + && matched_line - original_start < original_start) + search_start2 + = original_start - (matched_line - original_start) + 1; + + SVN_ERR(seek_to_line(content, search_start2, scratch_pool)); + SVN_ERR(scan_for_match(&matched_line2, content, hunk, FALSE, + search_start - 1, fuzz, + ignore_whitespace, FALSE, + cancel_func, cancel_baton, scratch_pool)); + if (matched_line2) + matched_line = matched_line2; } } } @@ -1725,7 +1858,7 @@ copy_lines_to_target(target_content_t *content, svn_linenum_t line, SVN_ERR(readline(content, &target_line, iterpool, iterpool)); if (! content->eof) target_line = apr_pstrcat(iterpool, target_line, content->eol_str, - (char *)NULL); + SVN_VA_NULL); len = strlen(target_line); SVN_ERR(content->write(content->write_baton, target_line, len, iterpool)); @@ -1949,7 +2082,7 @@ send_hunk_notification(const hunk_info_t *hi, notify->hunk_fuzz = hi->fuzz; notify->prop_name = prop_name; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); return SVN_NO_ERROR; } @@ -1963,6 +2096,7 @@ send_patch_notification(const patch_target_t *target, { svn_wc_notify_t *notify; svn_wc_notify_action_t action; + const char *notify_path; if (! ctx->notify_func2) return SVN_NO_ERROR; @@ -1971,14 +2105,18 @@ send_patch_notification(const patch_target_t *target, action = svn_wc_notify_skip; else if (target->deleted) action = svn_wc_notify_delete; - else if (target->added || target->replaced) + else if (target->added || target->replaced || target->move_target_abspath) 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); + if (target->move_target_abspath) + notify_path = target->move_target_abspath; + else + notify_path = target->local_abspath ? target->local_abspath + : target->local_relpath; + + notify = svn_wc_create_notify(notify_path, action, pool); notify->kind = svn_node_file; if (action == svn_wc_notify_skip) @@ -2006,7 +2144,7 @@ send_patch_notification(const patch_target_t *target, notify->prop_state = svn_wc_notify_state_changed; } - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); if (action == svn_wc_notify_patch) { @@ -2033,7 +2171,7 @@ send_patch_notification(const patch_target_t *target, { prop_patch_target_t *prop_target; - prop_target = svn__apr_hash_index_val(hash_index); + prop_target = apr_hash_this_val(hash_index); for (i = 0; i < prop_target->content->hunks->nelts; i++) { @@ -2054,15 +2192,16 @@ send_patch_notification(const patch_target_t *target, svn_pool_destroy(iterpool); } - return SVN_NO_ERROR; -} + if (target->move_target_abspath) + { + /* Notify about deletion of move source. */ + notify = svn_wc_create_notify(target->local_abspath, + svn_wc_notify_delete, pool); + notify->kind = svn_node_file; + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } -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); + return SVN_NO_ERROR; } /* Implements the callback for svn_sort__array. Puts hunks that match @@ -2124,8 +2263,6 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, 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) @@ -2135,6 +2272,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, int i; static const svn_linenum_t MAX_FUZZ = 2; apr_hash_index_t *hash_index; + svn_linenum_t previous_offset = 0; SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count, remove_tempfiles, result_pool, scratch_pool)); @@ -2144,19 +2282,6 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, 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++) @@ -2177,6 +2302,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, do { SVN_ERR(get_hunk_info(&hi, target, target->content, hunk, fuzz, + previous_offset, ignore_whitespace, FALSE /* is_prop_hunk */, cancel_func, cancel_baton, @@ -2185,6 +2311,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, } while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied); + if (hi->matched_line) + previous_offset + = hi->matched_line - svn_diff_hunk_get_original_start(hunk); + APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi; } @@ -2236,8 +2366,8 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *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); + prop_name = apr_hash_this_key(hash_index); + prop_patch = apr_hash_this_val(hash_index); if (! strcmp(prop_name, SVN_PROP_SPECIAL)) target->is_special = TRUE; @@ -2263,7 +2393,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, do { SVN_ERR(get_hunk_info(&hi, target, prop_target->content, - hunk, fuzz, + hunk, fuzz, 0, ignore_whitespace, TRUE /* is_prop_hunk */, cancel_func, cancel_baton, @@ -2283,7 +2413,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch, { prop_patch_target_t *prop_target; - prop_target = svn__apr_hash_index_val(hash_index); + prop_target = apr_hash_this_val(hash_index); for (i = 0; i < prop_target->content->hunks->nelts; i++) { @@ -2508,8 +2638,9 @@ create_missing_parents(patch_target_t *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, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, NULL /*props*/, + FALSE /* skip checks */, ctx->notify_func2, ctx->notify_baton2, iterpool)); } @@ -2625,7 +2756,10 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path, svn_subst_eol_style_native); SVN_ERR(svn_subst_copy_and_translate4( - target->patched_path, target->local_abspath, + target->patched_path, + target->move_target_abspath + ? target->move_target_abspath + : target->local_abspath, target->content->eol_str, repair_eol, target->content->keywords, TRUE /* expand */, FALSE /* special */, @@ -2639,15 +2773,39 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path, * 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, + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath, NULL /*props*/, + FALSE /* skip checks */, NULL, NULL, pool)); } /* Restore the target's executable bit if necessary. */ - SVN_ERR(svn_io_set_file_executable(target->local_abspath, + SVN_ERR(svn_io_set_file_executable(target->move_target_abspath + ? target->move_target_abspath + : target->local_abspath, target->executable, FALSE, pool)); + + if (target->move_target_abspath) + { + /* ### Copying the patched content to the move target location, + * performing the move in meta-data, and removing the file at + * the move source should be one atomic operation. */ + + /* ### Create missing parents. */ + + /* Perform the move in meta-data. */ + SVN_ERR(svn_wc__move2(ctx->wc_ctx, + target->local_abspath, + target->move_target_abspath, + TRUE, /* metadata_only */ + FALSE, /* allow_mixed_revisions */ + NULL, NULL, NULL, NULL, + pool)); + + /* Delete the patch target's old location from disk. */ + SVN_ERR(svn_io_remove_file2(target->local_abspath, FALSE, pool)); + } } } @@ -2694,7 +2852,7 @@ install_patched_prop_targets(patch_target_t *target, hi; hi = apr_hash_next(hi)) { - prop_patch_target_t *prop_target = svn__apr_hash_index_val(hi); + prop_patch_target_t *prop_target = apr_hash_this_val(hi); const svn_string_t *prop_val; svn_error_t *err; @@ -2730,10 +2888,11 @@ install_patched_prop_targets(patch_target_t *target, { 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, + SVN_ERR(svn_io_file_create_empty(target->local_abspath, + scratch_pool)); + SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath, NULL /*props*/, + FALSE /* skip checks */, /* suppress notification */ NULL, NULL, iterpool)); @@ -2869,10 +3028,13 @@ check_ancestor_delete(const char *deleted_target, { struct can_delete_baton_t cb; svn_error_t *err; + apr_array_header_t *ignores; apr_pool_t *iterpool = svn_pool_create(scratch_pool); const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool); + SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool)); + while (svn_dirent_is_child(apply_root, dir_abspath, iterpool)) { svn_pool_clear(iterpool); @@ -2882,7 +3044,7 @@ check_ancestor_delete(const char *deleted_target, cb.targets_info = targets_info; err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity, - TRUE, FALSE, FALSE, NULL, + TRUE, FALSE, FALSE, ignores, can_delete_callback, &cb, ctx->cancel_func, ctx->cancel_baton, iterpool); @@ -2986,14 +3148,23 @@ apply_patches(/* The path to the patch file. */ if (patch) { patch_target_t *target; + svn_boolean_t filtered = FALSE; 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) + + if (!target->skipped && patch_func) + { + SVN_ERR(patch_func(patch_baton, &filtered, + target->canon_path_from_patchfile, + target->patched_path, target->reject_path, + iterpool)); + } + + if (! filtered) { /* Save info we'll still need when we're done patching. */ patch_target_info_t *target_info = @@ -3009,6 +3180,7 @@ apply_patches(/* The path to the patch file. */ if (target->has_text_changes || target->added + || target->move_target_abspath || target->deleted) SVN_ERR(install_patched_target(target, abs_wc_path, ctx, dry_run, iterpool)); diff --git a/contrib/subversion/subversion/libsvn_client/prop_commands.c b/contrib/subversion/subversion/libsvn_client/prop_commands.c index 06c4d21dc..15629666c 100644 --- a/contrib/subversion/subversion/libsvn_client/prop_commands.c +++ b/contrib/subversion/subversion/libsvn_client/prop_commands.c @@ -206,6 +206,7 @@ propset_on_url(const char *propname, item = svn_client_commit_item3_create(pool); item->url = target; + item->kind = node_kind; item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items, @@ -240,6 +241,14 @@ propset_on_url(const char *propname, return svn_error_trace(err); } + if (ctx->notify_func2) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify_url(target, + svn_wc_notify_commit_finalizing, + pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); + } /* Close the edit. */ return editor->close_edit(edit_baton, pool); } @@ -485,11 +494,11 @@ svn_client_revprop_set2(const char *propname, const svn_string_t *unset = NULL; if (original_propval == NULL) - old_value_p = NULL; + old_value_p = NULL; else if (original_propval->data == NULL) - old_value_p = &unset; + old_value_p = &unset; else - old_value_p = &original_propval; + old_value_p = &original_propval; /* The actual RA call. */ SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname, @@ -512,45 +521,24 @@ svn_client_revprop_set2(const char *propname, notify->prop_name = propname; notify->revision = *set_rev; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } return SVN_NO_ERROR; } -/* Helper for the remote case of svn_client_propget. - * - * If PROPS is not null, then get the value of property PROPNAME in REVNUM, - using RA_LIB and SESSION. Store the value ('svn_string_t *') in PROPS, - under the path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *'). - * - * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a - * depth-first ordered array of svn_prop_inherited_item_t * structures - * representing the PROPNAME properties inherited by the target. If - * INHERITABLE_PROPS in not null and no inheritable properties are found, - * then set *INHERITED_PROPS to an empty array. - * - * Recurse according to DEPTH, similarly to svn_client_propget3(). - * - * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE". - * Yes, caller passes this; it makes the recursion more efficient :-). - * - * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary - * work in SCRATCH_POOL. The two pools can be the same; recursive - * calls may use a different SCRATCH_POOL, however. - */ -static svn_error_t * -remote_propget(apr_hash_t *props, - apr_array_header_t **inherited_props, - const char *propname, - const char *target_prefix, - const char *target_relative, - svn_node_kind_t kind, - svn_revnum_t revnum, - svn_ra_session_t *ra_session, - svn_depth_t depth, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +svn_error_t * +svn_client__remote_propget(apr_hash_t *props, + apr_array_header_t **inherited_props, + const char *propname, + const char *target_prefix, + const char *target_relative, + svn_node_kind_t kind, + svn_revnum_t revnum, + svn_ra_session_t *ra_session, + svn_depth_t depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_hash_t *dirents; apr_hash_t *prop_hash = NULL; @@ -587,6 +575,9 @@ remote_propget(apr_hash_t *props, if (inherited_props) { const char *repos_root_url; + int i; + apr_array_header_t *final_iprops = + apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *)); /* We will filter out all but PROPNAME later, making a final copy in RESULT_POOL, so pass SCRATCH_POOL for all pools. */ @@ -599,15 +590,8 @@ remote_propget(apr_hash_t *props, repos_root_url, scratch_pool, scratch_pool)); - } - - /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */ - if (inherited_props) - { - int i; - apr_array_header_t *final_iprops = - apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *)); + /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */ for (i = 0; i < (*inherited_props)->nelts; i++) { svn_prop_inherited_item_t *iprop = @@ -650,8 +634,8 @@ remote_propget(apr_hash_t *props, hi; hi = apr_hash_next(hi)) { - const char *this_name = svn__apr_hash_index_key(hi); - svn_dirent_t *this_ent = svn__apr_hash_index_val(hi); + const char *this_name = apr_hash_this_key(hi); + svn_dirent_t *this_ent = apr_hash_this_val(hi); const char *new_target_relative; svn_depth_t depth_below_here = depth; @@ -666,15 +650,15 @@ remote_propget(apr_hash_t *props, new_target_relative = svn_relpath_join(target_relative, this_name, iterpool); - SVN_ERR(remote_propget(props, NULL, - propname, - target_prefix, - new_target_relative, - this_ent->kind, - revnum, - ra_session, - depth_below_here, - result_pool, iterpool)); + SVN_ERR(svn_client__remote_propget(props, NULL, + propname, + target_prefix, + new_target_relative, + this_ent->kind, + revnum, + ra_session, + depth_below_here, + result_pool, iterpool)); } svn_pool_destroy(iterpool); @@ -704,7 +688,7 @@ recursive_propget_receiver(void *baton, { apr_hash_index_t *hi = apr_hash_first(scratch_pool, props); svn_hash_sets(b->props, apr_pstrdup(b->pool, local_abspath), - svn_string_dup(svn__apr_hash_index_val(hi), b->pool)); + svn_string_dup(apr_hash_this_val(hi), b->pool)); } return SVN_NO_ERROR; @@ -882,13 +866,9 @@ svn_client_propget5(apr_hash_t **props, if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind) || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) { - svn_revnum_t origin_rev; const char *repos_relpath; const char *repos_root_url; - const char *repos_uuid; const char *local_abspath; - const char *copy_root_abspath; - svn_boolean_t is_copy; /* Avoid assertion on the next line when somebody accidentally asks for a working copy revision on a URL */ @@ -901,12 +881,10 @@ svn_client_propget5(apr_hash_t **props, if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) { - SVN_ERR(svn_wc__node_get_origin(&is_copy, - &origin_rev, + SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &repos_relpath, &repos_root_url, - &repos_uuid, - ©_root_abspath, + NULL, NULL, NULL, ctx->wc_ctx, local_abspath, FALSE, /* scan_deleted */ @@ -970,7 +948,8 @@ svn_client_propget5(apr_hash_t **props, if (!local_explicit_props) *props = apr_hash_make(result_pool); - SVN_ERR(remote_propget(!local_explicit_props ? *props : NULL, + SVN_ERR(svn_client__remote_propget( + !local_explicit_props ? *props : NULL, !local_iprops ? inherited_props : NULL, propname, loc->url, "", kind, loc->rev, ra_session, @@ -1134,8 +1113,8 @@ remote_proplist(const char *target_prefix, 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 *name = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(hi); svn_prop_kind_t prop_kind; prop_kind = svn_property_kind2(name); @@ -1160,8 +1139,8 @@ remote_proplist(const char *target_prefix, hi; hi = apr_hash_next(hi)) { - const char *this_name = svn__apr_hash_index_key(hi); - svn_dirent_t *this_ent = svn__apr_hash_index_val(hi); + const char *this_name = apr_hash_this_key(hi); + svn_dirent_t *this_ent = apr_hash_this_val(hi); const char *new_target_relative; if (cancel_func) @@ -1291,12 +1270,9 @@ get_remote_props(const char *path_or_url, if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind) || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) { - svn_revnum_t origin_rev; const char *repos_relpath; const char *repos_root_url; - const char *repos_uuid; const char *local_abspath; - const char *copy_root_abspath; svn_boolean_t is_copy; /* Avoid assertion on the next line when somebody accidentally asks for @@ -1311,11 +1287,10 @@ get_remote_props(const char *path_or_url, if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) { SVN_ERR(svn_wc__node_get_origin(&is_copy, - &origin_rev, + NULL, &repos_relpath, &repos_root_url, - &repos_uuid, - ©_root_abspath, + NULL, NULL, NULL, ctx->wc_ctx, local_abspath, FALSE, /* scan_deleted */ diff --git a/contrib/subversion/subversion/libsvn_client/ra.c b/contrib/subversion/subversion/libsvn_client/ra.c index a0d4ceab5..f98b4b070 100644 --- a/contrib/subversion/subversion/libsvn_client/ra.c +++ b/contrib/subversion/subversion/libsvn_client/ra.c @@ -42,6 +42,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" #include "private/svn_client_private.h" +#include "private/svn_sorts_private.h" /* This is the baton that we pass svn_ra_open3(), and is associated with @@ -70,6 +71,8 @@ typedef struct callback_baton_t /* A client context. */ svn_client_ctx_t *ctx; + /* Last progress reported by progress callback. */ + apr_off_t last_progress; } callback_baton_t; @@ -287,6 +290,31 @@ get_client_string(void *baton, return SVN_NO_ERROR; } +/* Implements svn_ra_progress_notify_func_t. Accumulates progress information + * for different RA sessions and reports total progress to caller. */ +static void +progress_func(apr_off_t progress, + apr_off_t total, + void *baton, + apr_pool_t *pool) +{ + callback_baton_t *b = baton; + svn_client_ctx_t *public_ctx = b->ctx; + svn_client__private_ctx_t *private_ctx = + svn_client__get_private_ctx(public_ctx); + + private_ctx->total_progress += (progress - b->last_progress); + b->last_progress = progress; + + if (public_ctx->progress_func) + { + /* All RA implementations currently provide -1 for total. So it doesn't + make sense to develop some complex logic to combine total across all + RA sessions. */ + public_ctx->progress_func(private_ctx->total_progress, -1, + public_ctx->progress_baton, pool); + } +} #define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO: Make configurable. */ @@ -320,12 +348,15 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session, cbtable->invalidate_wc_props = (write_dav_props && read_dav_props) ? invalidate_wc_props : NULL; cbtable->auth_baton = ctx->auth_baton; /* new-style */ - cbtable->progress_func = ctx->progress_func; - cbtable->progress_baton = ctx->progress_baton; + cbtable->progress_func = progress_func; + cbtable->progress_baton = cb; cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL; cbtable->get_client_string = get_client_string; if (base_dir_abspath) cbtable->get_wc_contents = get_wc_contents; + cbtable->check_tunnel_func = ctx->check_tunnel_func; + cbtable->open_tunnel_func = ctx->open_tunnel_func; + cbtable->tunnel_baton = ctx->tunnel_baton; cb->commit_items = commit_items; cb->ctx = ctx; @@ -403,7 +434,7 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session, svn_wc_create_notify_url(corrected, svn_wc_notify_url_redirect, scratch_pool); - (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } /* Our caller will want to know what our final corrected URL was. */ @@ -614,8 +645,7 @@ svn_client__repos_location_segments(apr_array_header_t **segments, pool); SVN_ERR(svn_error_compose_create( err, svn_ra_reparent(ra_session, old_session_url, pool))); - qsort((*segments)->elts, (*segments)->nelts, - (*segments)->elt_size, compare_segments); + svn_sort__array(*segments, compare_segments); return SVN_NO_ERROR; } @@ -627,6 +657,9 @@ svn_client__repos_location_segments(apr_array_header_t **segments, * END_REVNUM must be valid revision numbers except that END_REVNUM may * be SVN_INVALID_REVNUM if END_URL is NULL. * + * YOUNGEST_REV is the already retrieved youngest revision of the ra session, + * but can be SVN_INVALID_REVNUM if the value is not already retrieved. + * * RA_SESSION is an open RA session parented at URL. */ static svn_error_t * @@ -637,6 +670,7 @@ repos_locations(const char **start_url, svn_revnum_t peg_revnum, svn_revnum_t start_revnum, svn_revnum_t end_revnum, + svn_revnum_t youngest_rev, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -644,9 +678,9 @@ repos_locations(const char **start_url, apr_array_header_t *revs; apr_hash_t *rev_locs; - SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM); - SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM); - SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL); + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revnum)); + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revnum)); + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_revnum) || end_url == NULL); /* Avoid a network request in the common easy case. */ if (start_revnum == peg_revnum @@ -661,6 +695,27 @@ repos_locations(const char **start_url, SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool)); + /* Handle another common case: The repository root can't move */ + if (! strcmp(repos_url, url)) + { + if (! SVN_IS_VALID_REVNUM(youngest_rev)) + SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest_rev, + scratch_pool)); + + if (start_revnum > youngest_rev + || (SVN_IS_VALID_REVNUM(end_revnum) && (end_revnum > youngest_rev))) + return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), + (start_revnum > youngest_rev) + ? start_revnum : end_revnum); + + if (start_url) + *start_url = apr_pstrdup(result_pool, repos_url); + if (end_url) + *end_url = apr_pstrdup(result_pool, repos_url); + return SVN_NO_ERROR; + } + revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t)); APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum; if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM) @@ -716,7 +771,7 @@ svn_client__repos_location(svn_client__pathrev_t **op_loc_p, peg_loc->url, scratch_pool)); err = repos_locations(&op_url, NULL, ra_session, peg_loc->url, peg_loc->rev, - op_revnum, SVN_INVALID_REVNUM, + op_revnum, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, result_pool, scratch_pool); SVN_ERR(svn_error_compose_create( err, svn_ra_reparent(ra_session, old_session_url, scratch_pool))); @@ -776,7 +831,7 @@ svn_client__repos_locations(const char **start_url, svn_boolean_t is_copy; SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath, - &repos_root_url, NULL, NULL, + &repos_root_url, NULL, NULL, NULL, ctx->wc_ctx, local_abspath_or_url, FALSE, subpool, subpool)); @@ -856,7 +911,7 @@ svn_client__repos_locations(const char **start_url, SVN_ERR(repos_locations(start_url, end_url, ra_session, url, peg_revnum, - start_revnum, end_revnum, + start_revnum, end_revnum, youngest_rev, pool, subpool)); svn_pool_destroy(subpool); return SVN_NO_ERROR; @@ -888,9 +943,9 @@ svn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, remembering the youngest matching location. */ for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); - apr_ssize_t path_len = svn__apr_hash_index_klen(hi); - svn_rangelist_t *ranges1 = svn__apr_hash_index_val(hi); + const char *path = apr_hash_this_key(hi); + apr_ssize_t path_len = apr_hash_this_key_len(hi); + svn_rangelist_t *ranges1 = apr_hash_this_val(hi); svn_rangelist_t *ranges2, *common; ranges2 = apr_hash_get(history2, path, path_len); @@ -992,48 +1047,6 @@ svn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, return SVN_NO_ERROR; } -svn_error_t * -svn_client__youngest_common_ancestor(const char **ancestor_url, - svn_revnum_t *ancestor_rev, - const char *path_or_url1, - const svn_opt_revision_t *revision1, - const char *path_or_url2, - const svn_opt_revision_t *revision2, - svn_client_ctx_t *ctx, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - apr_pool_t *sesspool = svn_pool_create(scratch_pool); - svn_ra_session_t *session; - svn_client__pathrev_t *loc1, *loc2, *ancestor; - - /* Resolve the two locations */ - SVN_ERR(svn_client__ra_session_from_path2(&session, &loc1, - path_or_url1, NULL, - revision1, revision1, - ctx, sesspool)); - SVN_ERR(svn_client__resolve_rev_and_url(&loc2, session, - path_or_url2, revision2, revision2, - ctx, scratch_pool)); - - SVN_ERR(svn_client__get_youngest_common_ancestor( - &ancestor, loc1, loc2, session, ctx, result_pool, scratch_pool)); - - if (ancestor) - { - *ancestor_url = ancestor->url; - *ancestor_rev = ancestor->rev; - } - else - { - *ancestor_url = NULL; - *ancestor_rev = SVN_INVALID_REVNUM; - } - svn_pool_destroy(sesspool); - return SVN_NO_ERROR; -} - - struct ra_ev2_baton { /* The working copy context, from the client context. */ svn_wc_context_t *wc_ctx; @@ -1080,6 +1093,7 @@ svn_client__ra_provide_base(svn_stream_t **contents, /* The pristine contents refer to the BASE, or to the pristine of a copy/move to this location. Fetch the correct revision. */ SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, + NULL, reb->wc_ctx, local_abspath, FALSE, scratch_pool, scratch_pool)); } @@ -1124,6 +1138,7 @@ svn_client__ra_provide_props(apr_hash_t **props, /* The pristine props refer to the BASE, or to the pristine props of a copy/move to this location. Fetch the correct revision. */ SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, + NULL, reb->wc_ctx, local_abspath, FALSE, scratch_pool, scratch_pool)); } diff --git a/contrib/subversion/subversion/libsvn_client/relocate.c b/contrib/subversion/subversion/libsvn_client/relocate.c index ed8d09cc0..dcd9017c7 100644 --- a/contrib/subversion/subversion/libsvn_client/relocate.c +++ b/contrib/subversion/subversion/libsvn_client/relocate.c @@ -127,85 +127,6 @@ validator_func(void *baton, return SVN_NO_ERROR; } - -/* Examing the array of svn_wc_external_item2_t's EXT_DESC (parsed - from the svn:externals property set on LOCAL_ABSPATH) and determine - if the external working copies described by such should be - relocated as a side-effect of the relocation of their parent - working copy (from OLD_PARENT_REPOS_ROOT_URL to - NEW_PARENT_REPOS_ROOT_URL). If so, attempt said relocation. */ -static svn_error_t * -relocate_externals(const char *local_abspath, - apr_array_header_t *ext_desc, - const char *old_parent_repos_root_url, - const char *new_parent_repos_root_url, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - apr_pool_t *iterpool; - int i; - - /* Parse an externals definition into an array of external items. */ - - iterpool = svn_pool_create(scratch_pool); - - for (i = 0; i < ext_desc->nelts; i++) - { - svn_wc_external_item2_t *ext_item = - APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *); - const char *target_repos_root_url; - const char *target_abspath; - svn_error_t *err; - - svn_pool_clear(iterpool); - - /* If this external isn't pulled in via a relative URL, ignore - it. There's no sense in relocating a working copy only to - have the next 'svn update' try to point it back to another - location. */ - if (! ((strncmp("../", ext_item->url, 3) == 0) || - (strncmp("^/", ext_item->url, 2) == 0))) - continue; - - /* If the external working copy's not-yet-relocated repos root - URL matches the primary working copy's pre-relocated - repository root URL, try to relocate that external, too. - You might wonder why this check is needed, given that we're - already limiting ourselves to externals pulled via URLs - relative to their primary working copy. Well, it's because - you can use "../" to "crawl up" above one repository's URL - space and down into another one. */ - SVN_ERR(svn_dirent_get_absolute(&target_abspath, - svn_dirent_join(local_abspath, - ext_item->target_dir, - iterpool), - iterpool)); - err = svn_client_get_repos_root(&target_repos_root_url, NULL /* uuid */, - target_abspath, ctx, iterpool, iterpool); - - /* Ignore externals that aren't present in the working copy. - * This can happen if an external is deleted from disk accidentally, - * or if an external is configured on a locally added directory. */ - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) - { - svn_error_clear(err); - continue; - } - else - SVN_ERR(err); - - if (strcmp(target_repos_root_url, old_parent_repos_root_url) == 0) - SVN_ERR(svn_client_relocate2(target_abspath, - old_parent_repos_root_url, - new_parent_repos_root_url, - FALSE, ctx, iterpool)); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - svn_error_t * svn_client_relocate2(const char *wcroot_dir, const char *from_prefix, @@ -256,10 +177,9 @@ svn_client_relocate2(const char *wcroot_dir, /* Relocate externals, too (if any). */ - SVN_ERR(svn_wc__externals_gather_definitions(&externals_hash, NULL, - ctx->wc_ctx, local_abspath, - svn_depth_infinity, - pool, pool)); + SVN_ERR(svn_wc__externals_defined_below(&externals_hash, + ctx->wc_ctx, local_abspath, + pool, pool)); if (! apr_hash_count(externals_hash)) return SVN_NO_ERROR; @@ -269,18 +189,39 @@ svn_client_relocate2(const char *wcroot_dir, hi != NULL; hi = apr_hash_next(hi)) { - const char *this_abspath = svn__apr_hash_index_key(hi); - const char *value = svn__apr_hash_index_val(hi); - apr_array_header_t *ext_desc; + svn_node_kind_t kind; + const char *this_abspath = apr_hash_this_key(hi); svn_pool_clear(iterpool); - SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath, - value, FALSE, - iterpool)); - if (ext_desc->nelts) - SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url, - new_repos_root_url, ctx, iterpool)); + SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, + ctx->wc_ctx, + local_abspath, this_abspath, + FALSE, iterpool, iterpool)); + + if (kind == svn_node_dir) + { + const char *this_repos_root_url; + svn_error_t *err; + + err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */, + this_abspath, ctx, iterpool, iterpool); + + /* Ignore externals that aren't present in the working copy. + * This can happen if an external is deleted from disk accidentally, + * or if an external is configured on a locally added directory. */ + if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + { + svn_error_clear(err); + continue; + } + SVN_ERR(err); + + if (strcmp(old_repos_root_url, this_repos_root_url) == 0) + SVN_ERR(svn_client_relocate2(this_abspath, from_prefix, to_prefix, + FALSE /* ignore_externals */, + ctx, iterpool)); + } } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/libsvn_client/repos_diff.c b/contrib/subversion/subversion/libsvn_client/repos_diff.c index 6a7725f59..58fe8aaa1 100644 --- a/contrib/subversion/subversion/libsvn_client/repos_diff.c +++ b/contrib/subversion/subversion/libsvn_client/repos_diff.c @@ -328,7 +328,7 @@ get_file_from_ra(struct file_baton *fb, fb->pool, scratch_pool)); fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum, - svn_checksum_md5, TRUE, scratch_pool); + svn_checksum_md5, TRUE, fb->pool); /* Retrieve the file and its properties */ SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session, @@ -389,6 +389,10 @@ remove_non_prop_changes(apr_hash_t *pristine_props, { int i; + /* For added nodes, there is nothing to filter. */ + if (apr_hash_count(pristine_props) == 0) + return; + for (i = 0; i < changes->nelts; i++) { svn_prop_t *change = &APR_ARRAY_IDX(changes, i, svn_prop_t); @@ -581,8 +585,8 @@ diff_deleted_dir(const char *path, hi = apr_hash_next(hi)) { const char *child_path; - const char *name = svn__apr_hash_index_key(hi); - svn_dirent_t *dirent = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_dirent_t *dirent = apr_hash_this_val(hi); svn_pool_clear(iterpool); @@ -1162,7 +1166,7 @@ change_file_prop(void *file_baton, propchange = apr_array_push(fb->propchanges); propchange->name = apr_pstrdup(fb->pool, name); - propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; + propchange->value = svn_string_dup(value, fb->pool); return SVN_NO_ERROR; } @@ -1192,7 +1196,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); return SVN_NO_ERROR; } @@ -1365,6 +1369,7 @@ svn_client__get_diff_editor2(const svn_delta_editor_t **editor, eb->ra_session = ra_session; eb->revision = revision; + eb->target_revision = SVN_INVALID_REVNUM; eb->empty_file = NULL; eb->empty_hash = apr_hash_make(eb->pool); eb->text_deltas = text_deltas; diff --git a/contrib/subversion/subversion/libsvn_client/resolved.c b/contrib/subversion/subversion/libsvn_client/resolved.c index 049637109..8b94707ec 100644 --- a/contrib/subversion/subversion/libsvn_client/resolved.c +++ b/contrib/subversion/subversion/libsvn_client/resolved.c @@ -27,8 +27,6 @@ /*** Includes. ***/ -#include - #include "svn_types.h" #include "svn_wc.h" #include "svn_client.h" @@ -39,6 +37,7 @@ #include "svn_hash.h" #include "svn_sorts.h" #include "client.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -59,8 +58,7 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain, *conflicts_remain = FALSE; SVN_ERR(svn_hash_keys(&array, conflicted_paths, scratch_pool)); - qsort(array->elts, array->nelts, array->elt_size, - svn_sort_compare_paths); + svn_sort__array(array, svn_sort_compare_paths); for (i = 0; i < array->nelts; i++) { @@ -79,7 +77,7 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain, ctx->notify_func2, ctx->notify_baton2, iterpool)); - if (conflicts_remain) + if (conflicts_remain && !*conflicts_remain) { svn_error_t *err; svn_boolean_t text_c, prop_c, tree_c; diff --git a/contrib/subversion/subversion/libsvn_client/revert.c b/contrib/subversion/subversion/libsvn_client/revert.c index 681e39c07..d827014eb 100644 --- a/contrib/subversion/subversion/libsvn_client/revert.c +++ b/contrib/subversion/subversion/libsvn_client/revert.c @@ -49,6 +49,8 @@ struct revert_with_write_lock_baton { svn_depth_t depth; svn_boolean_t use_commit_times; const apr_array_header_t *changelists; + svn_boolean_t clear_changelists; + svn_boolean_t metadata_only; svn_client_ctx_t *ctx; }; @@ -78,11 +80,13 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) struct revert_with_write_lock_baton *b = baton; svn_error_t *err; - err = svn_wc_revert4(b->ctx->wc_ctx, + err = svn_wc_revert5(b->ctx->wc_ctx, b->local_abspath, b->depth, b->use_commit_times, b->changelists, + b->clear_changelists, + b->metadata_only, b->ctx->cancel_func, b->ctx->cancel_baton, b->ctx->notify_func2, b->ctx->notify_baton2, scratch_pool); @@ -96,11 +100,18 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { if (b->ctx->notify_func2) - (*b->ctx->notify_func2)( - b->ctx->notify_baton2, - svn_wc_create_notify(b->local_abspath, svn_wc_notify_skip, - scratch_pool), - scratch_pool); + { + svn_wc_notify_t *notify; + + notify = svn_wc_create_notify(b->local_abspath, + svn_wc_notify_skip, + scratch_pool); + + notify->err = err; + + b->ctx->notify_func2(b->ctx->notify_baton2, + notify, scratch_pool); + } svn_error_clear(err); } else @@ -112,13 +123,15 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) svn_error_t * -svn_client_revert2(const apr_array_header_t *paths, +svn_client_revert3(const apr_array_header_t *paths, svn_depth_t depth, const apr_array_header_t *changelists, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_client_ctx_t *ctx, apr_pool_t *pool) { - apr_pool_t *subpool; + apr_pool_t *iterpool; svn_error_t *err = SVN_NO_ERROR; int i; svn_config_t *cfg; @@ -145,7 +158,7 @@ svn_client_revert2(const apr_array_header_t *paths, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); - subpool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); for (i = 0; i < paths->nelts; i++) { @@ -153,14 +166,14 @@ svn_client_revert2(const apr_array_header_t *paths, const char *local_abspath, *lock_target; svn_boolean_t wc_root; - svn_pool_clear(subpool); + svn_pool_clear(iterpool); /* See if we've been asked to cancel this operation. */ if ((ctx->cancel_func) && ((err = ctx->cancel_func(ctx->cancel_baton)))) goto errorful; - err = svn_dirent_get_absolute(&local_abspath, path, pool); + err = svn_dirent_get_absolute(&local_abspath, path, iterpool); if (err) goto errorful; @@ -168,15 +181,18 @@ svn_client_revert2(const apr_array_header_t *paths, baton.depth = depth; baton.use_commit_times = use_commit_times; baton.changelists = changelists; + baton.clear_changelists = clear_changelists; + baton.metadata_only = metadata_only; baton.ctx = ctx; - err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, pool); + err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, iterpool); if (err) goto errorful; lock_target = wc_root ? local_abspath : svn_dirent_dirname(local_abspath, pool); err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx, - lock_target, FALSE, pool, pool); + lock_target, FALSE, + iterpool, iterpool); if (err) goto errorful; } @@ -192,10 +208,10 @@ svn_client_revert2(const apr_array_header_t *paths, if (paths->nelts == 1) sleep_path = APR_ARRAY_IDX(paths, 0, const char *); - svn_io_sleep_for_timestamps(sleep_path, subpool); + svn_io_sleep_for_timestamps(sleep_path, iterpool); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return svn_error_trace(err); } diff --git a/contrib/subversion/subversion/libsvn_client/revisions.c b/contrib/subversion/subversion/libsvn_client/revisions.c index ec255c1e1..e61e7d475 100644 --- a/contrib/subversion/subversion/libsvn_client/revisions.c +++ b/contrib/subversion/subversion/libsvn_client/revisions.c @@ -92,6 +92,7 @@ svn_client__get_revision_number(svn_revnum_t *revnum, goto invalid_rev_arg; err = svn_wc__node_get_origin(NULL, revnum, NULL, NULL, NULL, NULL, + NULL, wc_ctx, local_abspath, TRUE, scratch_pool, scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_client/status.c b/contrib/subversion/subversion/libsvn_client/status.c index e581d37a4..a70165867 100644 --- a/contrib/subversion/subversion/libsvn_client/status.c +++ b/contrib/subversion/subversion/libsvn_client/status.c @@ -29,7 +29,9 @@ #include #include +#include "svn_private_config.h" #include "svn_pools.h" +#include "svn_sorts.h" #include "client.h" #include "svn_path.h" @@ -39,9 +41,9 @@ #include "svn_error.h" #include "svn_hash.h" -#include "svn_private_config.h" -#include "private/svn_wc_private.h" #include "private/svn_client_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_wc_private.h" /*** Getting update information ***/ @@ -195,8 +197,7 @@ reporter_finish_report(void *report_baton, apr_pool_t *pool) server doesn't support lock discovery, we'll just not do locky stuff. */ err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool); - if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) - || (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))) + if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); err = SVN_NO_ERROR; @@ -245,27 +246,32 @@ do_external_status(svn_client_ctx_t *ctx, apr_hash_t *external_map, svn_depth_t depth, svn_boolean_t get_all, - svn_boolean_t update, + svn_boolean_t check_out_of_date, + svn_boolean_t check_working_copy, svn_boolean_t no_ignore, + const apr_array_header_t *changelists, const char *anchor_abspath, const char *anchor_relpath, svn_client_status_func_t status_func, void *status_baton, apr_pool_t *scratch_pool) { - apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_array_header_t *externals; + int i; + + externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically, + scratch_pool); /* Loop over the hash of new values (we don't care about the old ones). This is a mapping of versioned directories to property values. */ - for (hi = apr_hash_first(scratch_pool, external_map); - hi; - hi = apr_hash_next(hi)) + for (i = 0; i < externals->nelts; i++) { svn_node_kind_t external_kind; - const char *local_abspath = svn__apr_hash_index_key(hi); - const char *defining_abspath = svn__apr_hash_index_val(hi); + svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t); + const char *local_abspath = item.key; + const char *defining_abspath = item.value; svn_node_kind_t kind; svn_opt_revision_t opt_rev; const char *status_path; @@ -309,9 +315,12 @@ do_external_status(svn_client_ctx_t *ctx, } /* And then do the status. */ - SVN_ERR(svn_client_status5(NULL, ctx, status_path, &opt_rev, depth, - get_all, update, no_ignore, FALSE, FALSE, - NULL, status_func, status_baton, + SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth, + get_all, check_out_of_date, + check_working_copy, no_ignore, + FALSE /* ignore_exernals */, + FALSE /* depth_as_sticky */, + changelists, status_func, status_baton, iterpool)); } @@ -325,13 +334,14 @@ do_external_status(svn_client_ctx_t *ctx, svn_error_t * -svn_client_status5(svn_revnum_t *result_rev, +svn_client_status6(svn_revnum_t *result_rev, svn_client_ctx_t *ctx, const char *path, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t get_all, - svn_boolean_t update, + svn_boolean_t check_out_of_date, + svn_boolean_t check_working_copy, svn_boolean_t no_ignore, svn_boolean_t ignore_externals, svn_boolean_t depth_as_sticky, @@ -348,6 +358,11 @@ svn_client_status5(svn_revnum_t *result_rev, svn_error_t *err; apr_hash_t *changelist_hash = NULL; + /* Override invalid combinations of the check_out_of_date and + check_working_copy flags. */ + if (!check_out_of_date) + check_working_copy = TRUE; + if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s' is not a local path"), path); @@ -366,7 +381,7 @@ svn_client_status5(svn_revnum_t *result_rev, SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool)); - if (update) + if (check_out_of_date) { /* The status editor only works on directories, so get the ancestor if necessary */ @@ -434,7 +449,7 @@ svn_client_status5(svn_revnum_t *result_rev, /* If we want to know about out-of-dateness, we crawl the working copy and let the RA layer drive the editor for real. Otherwise, we just close the edit. :-) */ - if (update) + if (check_out_of_date) { svn_ra_session_t *ra_session; const char *URL; @@ -464,14 +479,14 @@ svn_client_status5(svn_revnum_t *result_rev, SVN_RA_CAPABILITY_DEPTH, pool)); SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton, - &edit_revision, ctx->wc_ctx, - dir_abspath, target_basename, - depth, get_all, - no_ignore, depth_as_sticky, - server_supports_depth, - ignores, tweak_status, &sb, - ctx->cancel_func, ctx->cancel_baton, - pool, pool)); + &edit_revision, ctx->wc_ctx, + dir_abspath, target_basename, + depth, get_all, check_working_copy, + no_ignore, depth_as_sticky, + server_supports_depth, + ignores, tweak_status, &sb, + ctx->cancel_func, ctx->cancel_baton, + pool, pool)); /* Verify that URL exists in HEAD. If it doesn't, this can save @@ -562,7 +577,7 @@ svn_client_status5(svn_revnum_t *result_rev, = svn_wc_create_notify(target_abspath, svn_wc_notify_status_completed, pool); notify->revision = edit_revision; - (ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ @@ -590,14 +605,7 @@ svn_client_status5(svn_revnum_t *result_rev, SVN_ERR(err); } - /* If there are svn:externals set, we don't want those to show up as - unversioned or unrecognized, so patch up the hash. If caller wants - all the statuses, we will change unversioned status items that - are interesting to an svn:externals property to - svn_wc_status_unversioned, otherwise we'll just remove the status - item altogether. - - We only descend into an external if depth is svn_depth_infinity or + /* We only descend into an external if depth is svn_depth_infinity or svn_depth_unknown. However, there are conceivable behaviors that would involve descending under other circumstances; thus, we pass depth anyway, so the code will DTRT if we change the conditional @@ -613,7 +621,8 @@ svn_client_status5(svn_revnum_t *result_rev, SVN_ERR(do_external_status(ctx, external_map, depth, get_all, - update, no_ignore, + check_out_of_date, check_working_copy, + no_ignore, changelists, sb.anchor_abspath, sb.anchor_relpath, status_func, status_baton, pool)); } diff --git a/contrib/subversion/subversion/libsvn_client/switch.c b/contrib/subversion/subversion/libsvn_client/switch.c index cd39cad93..37ab05c11 100644 --- a/contrib/subversion/subversion/libsvn_client/switch.c +++ b/contrib/subversion/subversion/libsvn_client/switch.c @@ -231,8 +231,6 @@ switch_internal(svn_revnum_t *result_rev, yca = NULL; /* Not versioned */ else { - /* ### It would be nice if this function could reuse the existing - ra session instead of opening two for its own use. */ SVN_ERR(svn_client__get_youngest_common_ancestor( &yca, switch_loc, target_base_loc, ra_session, ctx, pool, pool)); @@ -340,8 +338,8 @@ switch_internal(svn_revnum_t *result_rev, *timestamp_sleep = TRUE; /* Drive the reporter structure, describing the revisions within - PATH. When we call reporter->finish_report, the update_editor - will be driven by svn_repos_dir_delta2. */ + LOCAL_ABSPATH. When this calls reporter->finish_report, the + reporter will drive the switch_editor. */ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), @@ -367,7 +365,7 @@ switch_internal(svn_revnum_t *result_rev, new_depths, switch_loc->repos_root_url, local_abspath, - depth, timestamp_sleep, + depth, timestamp_sleep, ra_session, ctx, pool)); } @@ -382,7 +380,7 @@ switch_internal(svn_revnum_t *result_rev, = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ diff --git a/contrib/subversion/subversion/libsvn_client/update.c b/contrib/subversion/subversion/libsvn_client/update.c index 0b006cc33..3895aa29d 100644 --- a/contrib/subversion/subversion/libsvn_client/update.c +++ b/contrib/subversion/subversion/libsvn_client/update.c @@ -196,10 +196,16 @@ record_conflict(svn_wc_conflict_result_t **result, Add the paths of any conflict victims to CONFLICTED_PATHS, if that is not null. + + Use RA_SESSION_P to run the update if it is not NULL. If it is then + open a new ra session and place it in RA_SESSION_P. This allows + repeated calls to update_internal to reuse the same session. */ static svn_error_t * update_internal(svn_revnum_t *result_rev, + svn_boolean_t *timestamp_sleep, apr_hash_t *conflicted_paths, + svn_ra_session_t **ra_session_p, const char *local_abspath, const char *anchor_abspath, const svn_opt_revision_t *revision, @@ -208,10 +214,10 @@ update_internal(svn_revnum_t *result_rev, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t adds_as_modification, - svn_boolean_t *timestamp_sleep, svn_boolean_t notify_summary, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const svn_delta_editor_t *update_editor; void *update_edit_baton; @@ -229,7 +235,7 @@ update_internal(svn_revnum_t *result_rev, const char *diff3_cmd; apr_hash_t *wcroot_iprops; svn_opt_revision_t opt_rev; - svn_ra_session_t *ra_session; + svn_ra_session_t *ra_session = *ra_session_p; const char *preserved_exts_str; apr_array_header_t *preserved_exts; struct svn_client__dirent_fetcher_baton_t dfb; @@ -248,7 +254,7 @@ update_internal(svn_revnum_t *result_rev, depth_is_sticky = FALSE; if (strcmp(local_abspath, anchor_abspath)) - target = svn_dirent_basename(local_abspath, pool); + target = svn_dirent_basename(local_abspath, scratch_pool); else target = ""; @@ -256,8 +262,8 @@ update_internal(svn_revnum_t *result_rev, SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, ctx->wc_ctx, anchor_abspath, - TRUE, FALSE, - pool, pool)); + TRUE /* ignore_enoent */, + scratch_pool, scratch_pool)); /* It does not make sense to update conflict victims. */ if (repos_relpath) @@ -266,11 +272,11 @@ update_internal(svn_revnum_t *result_rev, svn_boolean_t text_conflicted, prop_conflicted; anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath, - pool); + scratch_pool); err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, NULL, - ctx->wc_ctx, local_abspath, pool); + ctx->wc_ctx, local_abspath, scratch_pool); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); @@ -293,9 +299,9 @@ update_internal(svn_revnum_t *result_rev, target_conflicted ? svn_wc_notify_skip_conflicted : svn_wc_notify_update_skip_working_only, - pool); + scratch_pool); - ctx->notify_func2(ctx->notify_baton2, nt, pool); + ctx->notify_func2(ctx->notify_baton2, nt, scratch_pool); } return SVN_NO_ERROR; } @@ -312,32 +318,33 @@ update_internal(svn_revnum_t *result_rev, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, - pool)); + scratch_pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, - TRUE, TRUE, pool)); + TRUE, TRUE, scratch_pool)); if (target_kind == svn_node_dir) { SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, - pool)); + scratch_pool)); } } /* check whether the "clean c/o" optimization is applicable */ - SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, pool)); + SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, + scratch_pool)); /* 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, pool)); + SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, @@ -349,7 +356,7 @@ update_internal(svn_revnum_t *result_rev, 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, pool) + ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool) : NULL; /* Let everyone know we're starting a real update (unless we're @@ -358,19 +365,52 @@ update_internal(svn_revnum_t *result_rev, { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, - pool); + 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; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } - /* Open an RA session for the URL */ - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, - anchor_url, - anchor_abspath, NULL, TRUE, - TRUE, ctx, pool, pool)); + /* Try to reuse the RA session by reparenting it to the anchor_url. + * This code is probably overly cautious since we only use this + * currently when parents are missing and so all the anchor_urls + * have to be in the same repo. */ + if (ra_session) + { + svn_error_t *err = svn_ra_reparent(ra_session, anchor_url, scratch_pool); + if (err) + { + if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) + { + /* session changed repos, can't reuse it */ + svn_error_clear(err); + ra_session = NULL; + } + else + { + return svn_error_trace(err); + } + } + else + { + corrected_url = NULL; + } + } + + /* Open an RA session for the URL if one isn't already available */ + if (!ra_session) + { + SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, + anchor_url, + anchor_abspath, NULL, + TRUE /* write_dav_props */, + TRUE /* read_dav_props */, + ctx, + result_pool, scratch_pool)); + *ra_session_p = ra_session; + } /* If we got a corrected URL from the RA subsystem, we'll need to relocate our working copy first. */ @@ -380,12 +420,13 @@ update_internal(svn_revnum_t *result_rev, /* To relocate everything inside our repository we need the old and new repos root. */ - SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, pool)); + SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, + scratch_pool)); /* svn_client_relocate2() will check the uuid */ SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url, new_repos_root_url, ignore_externals, - ctx, pool)); + ctx, scratch_pool)); /* Store updated repository root for externals */ repos_root_url = new_repos_root_url; @@ -406,10 +447,10 @@ update_internal(svn_revnum_t *result_rev, to take a URL as easily as a local path? */ SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, local_abspath, ra_session, &opt_rev, - pool)); + scratch_pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, - SVN_RA_CAPABILITY_DEPTH, pool)); + SVN_RA_CAPABILITY_DEPTH, scratch_pool)); dfb.ra_session = ra_session; dfb.target_revision = revnum; @@ -417,7 +458,7 @@ update_internal(svn_revnum_t *result_rev, SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath, revnum, depth, ra_session, - ctx, pool, pool)); + ctx, scratch_pool, scratch_pool)); /* Fetch the update editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision later on. */ @@ -436,7 +477,7 @@ update_internal(svn_revnum_t *result_rev, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, - pool, pool)); + 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. */ @@ -447,15 +488,16 @@ update_internal(svn_revnum_t *result_rev, : svn_depth_unknown), FALSE /* send_copyfrom_args */, FALSE /* ignore_ancestry */, - update_editor, update_edit_baton, pool, pool)); + update_editor, update_edit_baton, + scratch_pool, scratch_pool)); /* Past this point, we assume the WC is going to be modified so we will * need to sleep for timestamps. */ *timestamp_sleep = TRUE; /* Drive the reporter structure, describing the revisions within - PATH. When we call reporter->finish_report, the - update_editor will be driven by svn_repos_dir_delta2. */ + LOCAL_ABSPATH. When this calls reporter->finish_report, the + reporter will drive the update_editor. */ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), @@ -463,7 +505,7 @@ update_internal(svn_revnum_t *result_rev, use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, - pool)); + scratch_pool)); /* We handle externals after the update is complete, so that handling external items (and any errors therefrom) doesn't delay @@ -476,13 +518,14 @@ update_internal(svn_revnum_t *result_rev, SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, - depth, pool, pool)); + depth, + scratch_pool, scratch_pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, repos_root_url, local_abspath, - depth, timestamp_sleep, - ctx, pool)); + depth, timestamp_sleep, ra_session, + ctx, scratch_pool)); } /* Let everyone know we're finished here (unless we're asked not to). */ @@ -490,13 +533,13 @@ update_internal(svn_revnum_t *result_rev, { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, - pool); + 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, pool); + ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); } /* If the caller wants the result revision, give it to them. */ @@ -508,6 +551,7 @@ update_internal(svn_revnum_t *result_rev, svn_error_t * svn_client__update_internal(svn_revnum_t *result_rev, + svn_boolean_t *timestamp_sleep, const char *local_abspath, const svn_opt_revision_t *revision, svn_depth_t depth, @@ -517,7 +561,7 @@ svn_client__update_internal(svn_revnum_t *result_rev, svn_boolean_t adds_as_modification, svn_boolean_t make_parents, svn_boolean_t innerupdate, - svn_boolean_t *timestamp_sleep, + svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -536,16 +580,21 @@ svn_client__update_internal(svn_revnum_t *result_rev, const char *parent_abspath = local_abspath; apr_array_header_t *missing_parents = apr_array_make(pool, 4, sizeof(const char *)); + apr_pool_t *iterpool; + + iterpool = svn_pool_create(pool); while (1) { + svn_pool_clear(iterpool); + /* Try to lock. If we can't lock because our target (or its parent) isn't a working copy, we'll try to walk up the tree to find a working copy, remembering this path's parent as one we need to flesh out. */ err = svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, parent_abspath, !innerupdate, - pool, pool); + pool, iterpool); if (!err) break; if ((err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) @@ -567,12 +616,14 @@ svn_client__update_internal(svn_revnum_t *result_rev, const char *missing_parent = APR_ARRAY_IDX(missing_parents, i, const char *); - err = update_internal(result_rev, conflicted_paths, - missing_parent, anchor_abspath, - &peg_revision, svn_depth_empty, FALSE, - ignore_externals, allow_unver_obstructions, - adds_as_modification, timestamp_sleep, - FALSE, ctx, pool); + svn_pool_clear(iterpool); + + err = update_internal(result_rev, timestamp_sleep, conflicted_paths, + &ra_session, missing_parent, + anchor_abspath, &peg_revision, svn_depth_empty, + FALSE, ignore_externals, + allow_unver_obstructions, adds_as_modification, + FALSE, ctx, pool, iterpool); if (err) goto cleanup; anchor_abspath = missing_parent; @@ -583,6 +634,8 @@ svn_client__update_internal(svn_revnum_t *result_rev, peg_revision.kind = svn_opt_revision_number; peg_revision.value.number = *result_rev; } + + svn_pool_destroy(iterpool); } else { @@ -592,16 +645,17 @@ svn_client__update_internal(svn_revnum_t *result_rev, anchor_abspath = lockroot_abspath; } - err = update_internal(result_rev, conflicted_paths, + err = update_internal(result_rev, timestamp_sleep, conflicted_paths, + &ra_session, local_abspath, anchor_abspath, &peg_revision, depth, depth_is_sticky, ignore_externals, allow_unver_obstructions, - adds_as_modification, timestamp_sleep, - TRUE, ctx, pool); + adds_as_modification, + TRUE, ctx, pool, pool); /* Give the conflict resolver callback the opportunity to * resolve any conflicts that were raised. */ - if (! err && ctx->conflict_func2) + if (! err && ctx->conflict_func2 && apr_hash_count(conflicted_paths)) { err = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool); } @@ -633,6 +687,7 @@ svn_client_update4(apr_array_header_t **result_revs, const char *path = NULL; svn_boolean_t sleep = FALSE; svn_error_t *err = SVN_NO_ERROR; + svn_boolean_t found_valid_target = FALSE; if (result_revs) *result_revs = apr_array_make(pool, paths->nelts, sizeof(svn_revnum_t)); @@ -664,14 +719,13 @@ svn_client_update4(apr_array_header_t **result_revs, err = svn_dirent_get_absolute(&local_abspath, path, iterpool); if (err) goto cleanup; - err = svn_client__update_internal(&result_rev, local_abspath, + err = svn_client__update_internal(&result_rev, &sleep, local_abspath, revision, depth, depth_is_sticky, ignore_externals, allow_unver_obstructions, adds_as_modification, make_parents, - FALSE, &sleep, - ctx, + FALSE, NULL, ctx, iterpool); if (err) @@ -691,15 +745,21 @@ svn_client_update4(apr_array_header_t **result_revs, notify = svn_wc_create_notify(path, svn_wc_notify_skip, iterpool); - (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool); + ctx->notify_func2(ctx->notify_baton2, notify, iterpool); } } + else + found_valid_target = TRUE; + if (result_revs) APR_ARRAY_PUSH(*result_revs, svn_revnum_t) = result_rev; } svn_pool_destroy(iterpool); cleanup: + if (!err && !found_valid_target) + return svn_error_create(SVN_ERR_WC_NOT_WORKING_COPY, NULL, + _("None of the targets are working copies")); if (sleep) { const char *wcroot_abspath; diff --git a/contrib/subversion/subversion/libsvn_client/upgrade.c b/contrib/subversion/subversion/libsvn_client/upgrade.c index 7a696196d..5677b1cf5 100644 --- a/contrib/subversion/subversion/libsvn_client/upgrade.c +++ b/contrib/subversion/subversion/libsvn_client/upgrade.c @@ -83,11 +83,13 @@ fetch_repos_info(const char **repos_root, } /* Forward definition. Upgrades svn:externals properties in the working copy - LOCAL_ABSPATH to the WC-NG storage. + LOCAL_ABSPATH to the WC-NG storage. INFO_BATON will be used to fetch + repository info using fetch_repos_info() function if needed. */ static svn_error_t * upgrade_externals_from_properties(svn_client_ctx_t *ctx, const char *local_abspath, + struct repos_info_baton *info_baton, apr_pool_t *scratch_pool); svn_error_t * @@ -138,7 +140,7 @@ svn_client_upgrade(const char *path, svn_pool_clear(iterpool); - ext_abspath = svn__apr_hash_index_key(hi); + ext_abspath = apr_hash_this_key(hi); SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, local_abspath, @@ -172,7 +174,7 @@ svn_client_upgrade(const char *path, (There is no way to detect the difference from libsvn_client :( ) */ SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, - scratch_pool)); + &info_baton, scratch_pool)); } return SVN_NO_ERROR; } @@ -180,6 +182,7 @@ svn_client_upgrade(const char *path, static svn_error_t * upgrade_externals_from_properties(svn_client_ctx_t *ctx, const char *local_abspath, + struct repos_info_baton *info_baton, apr_pool_t *scratch_pool) { apr_hash_index_t *hi; @@ -187,7 +190,6 @@ upgrade_externals_from_properties(svn_client_ctx_t *ctx, 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 @@ -209,8 +211,8 @@ upgrade_externals_from_properties(svn_client_ctx_t *ctx, 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); + const char *externals_parent = apr_hash_this_key(hi); + svn_string_t *external_desc = apr_hash_this_val(hi); apr_array_header_t *externals_p; svn_error_t *err; @@ -345,7 +347,7 @@ upgrade_externals_from_properties(svn_client_ctx_t *ctx, { err = fetch_repos_info(&repos_root_url, &repos_uuid, - &info_baton, + info_baton, resolved_url, scratch_pool, scratch_pool); if (err) diff --git a/contrib/subversion/subversion/libsvn_client/util.c b/contrib/subversion/subversion/libsvn_client/util.c index 06855e7e7..248412b04 100644 --- a/contrib/subversion/subversion/libsvn_client/util.c +++ b/contrib/subversion/subversion/libsvn_client/util.c @@ -138,7 +138,13 @@ svn_client__pathrev_fspath(const svn_client__pathrev_t *pathrev, svn_client_commit_item3_t * svn_client_commit_item3_create(apr_pool_t *pool) { - return apr_pcalloc(pool, sizeof(svn_client_commit_item3_t)); + svn_client_commit_item3_t *item = apr_pcalloc(pool, sizeof(*item)); + + item->revision = SVN_INVALID_REVNUM; + item->copyfrom_rev = SVN_INVALID_REVNUM; + item->kind = svn_node_unknown; + + return item; } svn_client_commit_item3_t * @@ -195,7 +201,6 @@ svn_client__wc_node_get_base(svn_client__pathrev_t **base_p, NULL, wc_ctx, wc_abspath, TRUE /* ignore_enoent */, - TRUE /* show_hidden */, result_pool, scratch_pool)); if ((*base_p)->repos_root_url && relpath) { @@ -225,7 +230,8 @@ svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, &relpath, &(*origin_p)->repos_root_url, &(*origin_p)->repos_uuid, - NULL, ctx->wc_ctx, wc_abspath, + NULL, NULL, + ctx->wc_ctx, wc_abspath, FALSE /* scan_deleted */, result_pool, scratch_pool)); if ((*origin_p)->repos_root_url && relpath) diff --git a/contrib/subversion/subversion/libsvn_delta/compat.c b/contrib/subversion/subversion/libsvn_delta/compat.c index 470efa2a6..dfa97437b 100644 --- a/contrib/subversion/subversion/libsvn_delta/compat.c +++ b/contrib/subversion/subversion/libsvn_delta/compat.c @@ -36,6 +36,8 @@ #include "svn_private_config.h" #include "private/svn_delta_private.h" +#include "private/svn_sorts_private.h" +#include "svn_private_config.h" struct file_rev_handler_wrapper_baton { @@ -188,6 +190,7 @@ struct change_node apr_hash_t *props; /* new/final set of props to apply */ + svn_boolean_t contents_changed; /* the file contents changed */ const char *contents_abspath; /* file containing new fulltext */ svn_checksum_t *checksum; /* checksum of new fulltext */ @@ -292,7 +295,7 @@ get_children(struct ev2_edit_baton *eb, for (hi = apr_hash_first(pool, eb->changes); hi; hi = apr_hash_next(hi)) { - const char *repos_relpath = svn__apr_hash_index_key(hi); + const char *repos_relpath = apr_hash_this_key(hi); const char *child; /* Find potential children. */ @@ -347,17 +350,26 @@ process_actions(struct ev2_edit_baton *eb, return SVN_NO_ERROR; } - if (change->contents_abspath != NULL) + if (change->contents_changed) { /* We can only set text on files. */ /* ### validate we aren't overwriting KIND? */ kind = svn_node_file; - /* ### the checksum might be in CHANGE->CHECKSUM */ - SVN_ERR(svn_io_file_checksum2(&checksum, change->contents_abspath, - svn_checksum_sha1, scratch_pool)); - SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath, - scratch_pool, scratch_pool)); + if (change->contents_abspath) + { + /* ### the checksum might be in CHANGE->CHECKSUM */ + SVN_ERR(svn_io_file_checksum2(&checksum, change->contents_abspath, + svn_checksum_sha1, scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath, + scratch_pool, scratch_pool)); + } + else + { + contents = svn_stream_empty(scratch_pool); + checksum = svn_checksum_empty_checksum(svn_checksum_sha1, + scratch_pool); + } } if (change->props != NULL) @@ -399,7 +411,7 @@ process_actions(struct ev2_edit_baton *eb, else { /* If this file was added, but apply_txdelta() was not - called (ie. no CONTENTS_ABSPATH), then we're adding + called (i.e., CONTENTS_CHANGED is FALSE), then we're adding an empty file. */ if (change->contents_abspath == NULL) { @@ -440,8 +452,8 @@ process_actions(struct ev2_edit_baton *eb, change->changing, NULL, props)); else SVN_ERR(svn_editor_alter_file(eb->editor, repos_relpath, - change->changing, props, - checksum, contents)); + change->changing, + checksum, contents, props)); } return SVN_NO_ERROR; @@ -789,6 +801,26 @@ window_handler(svn_txdelta_window_t *window, void *baton) return svn_error_trace(err); } +/* Lazy-open handler for getting a read-only stream of the delta base. */ +static svn_error_t * +open_delta_base(svn_stream_t **stream, void *baton, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + const char *const delta_base = baton; + return svn_stream_open_readonly(stream, delta_base, + result_pool, scratch_pool); +} + +/* Lazy-open handler for opening a stream for the delta result. */ +static svn_error_t * +open_delta_target(svn_stream_t **stream, void *baton, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + const char **delta_target = baton; + return svn_stream_open_unique(stream, delta_target, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool); +} static svn_error_t * ev2_apply_textdelta(void *file_baton, @@ -802,10 +834,9 @@ ev2_apply_textdelta(void *file_baton, struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb)); struct change_node *change; svn_stream_t *target; - /* ### fix this. for now, we know this has a "short" lifetime. */ - apr_pool_t *scratch_pool = handler_pool; change = locate_change(fb->eb, fb->path); + SVN_ERR_ASSERT(!change->contents_changed); SVN_ERR_ASSERT(change->contents_abspath == NULL); SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing) || change->changing == fb->base_revision); @@ -814,12 +845,14 @@ ev2_apply_textdelta(void *file_baton, if (! fb->delta_base) hb->source = svn_stream_empty(handler_pool); else - SVN_ERR(svn_stream_open_readonly(&hb->source, fb->delta_base, handler_pool, - scratch_pool)); + hb->source = svn_stream_lazyopen_create(open_delta_base, + (char*)fb->delta_base, + FALSE, handler_pool); - SVN_ERR(svn_stream_open_unique(&target, &change->contents_abspath, NULL, - svn_io_file_del_on_pool_cleanup, - fb->eb->edit_pool, scratch_pool)); + change->contents_changed = TRUE; + target = svn_stream_lazyopen_create(open_delta_target, + &change->contents_abspath, + FALSE, fb->eb->edit_pool); svn_txdelta_apply(hb->source, target, NULL, NULL, @@ -1106,6 +1139,7 @@ add_file_cb(void *baton, change->kind = svn_node_file; change->deleting = replaces_rev; change->props = svn_prop_hash_dup(props, eb->edit_pool); + change->contents_changed = TRUE; change->contents_abspath = tmp_filename; change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool); @@ -1183,9 +1217,9 @@ 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_hash_t *props, apr_pool_t *scratch_pool) { struct editor_baton *eb = baton; @@ -1199,12 +1233,12 @@ alter_file_cb(void *baton, if (contents) { /* We may need to re-checksum these contents */ - if (!(checksum && checksum->kind == svn_checksum_md5)) + if (checksum && checksum->kind == svn_checksum_md5) + md5_checksum = (svn_checksum_t *)checksum; + else contents = svn_stream_checksummed2(contents, &md5_checksum, NULL, svn_checksum_md5, TRUE, scratch_pool); - else - md5_checksum = (svn_checksum_t *)checksum; /* Spool the contents to a tempfile, and provide that to the driver. */ SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL, @@ -1223,6 +1257,7 @@ alter_file_cb(void *baton, change->props = svn_prop_hash_dup(props, eb->edit_pool); if (contents != NULL) { + change->contents_changed = TRUE; change->contents_abspath = tmp_filename; change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool); } @@ -1235,8 +1270,8 @@ static svn_error_t * alter_symlink_cb(void *baton, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const char *target, + apr_hash_t *props, apr_pool_t *scratch_pool) { /* ### should we verify the kind is truly a symlink? */ @@ -1331,17 +1366,6 @@ move_cb(void *baton, 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) -{ - SVN__NOT_IMPLEMENTED(); -} - - static int count_components(const char *relpath) { @@ -1640,7 +1664,7 @@ apply_change(void **dir_baton, /* Make this an FS path by prepending "/" */ if (copyfrom_url[0] != '/') copyfrom_url = apr_pstrcat(scratch_pool, "/", - copyfrom_url, NULL); + copyfrom_url, SVN_VA_NULL); } copyfrom_rev = change->copyfrom_rev; @@ -1673,7 +1697,7 @@ apply_change(void **dir_baton, else SVN_ERR(drive_ev1_props(eb, relpath, change, file_baton, scratch_pool)); - if (change->contents_abspath) + if (change->contents_changed && change->contents_abspath) { svn_txdelta_window_handler_t handler; void *handler_baton; @@ -1889,7 +1913,6 @@ svn_delta__editor_from_delta(svn_editor_t **editor_p, delete_cb, copy_cb, move_cb, - rotate_cb, complete_cb, abort_cb }; diff --git a/contrib/subversion/subversion/libsvn_delta/compose_delta.c b/contrib/subversion/subversion/libsvn_delta/compose_delta.c index 7b96438f7..6d757f223 100644 --- a/contrib/subversion/subversion/libsvn_delta/compose_delta.c +++ b/contrib/subversion/subversion/libsvn_delta/compose_delta.c @@ -648,15 +648,18 @@ copy_source_ops(apr_size_t offset, apr_size_t limit, { const svn_txdelta_op_t *const op = &window->ops[op_ndx]; const apr_size_t *const off = &ndx->offs[op_ndx]; - apr_size_t fix_offset; - apr_size_t fix_limit; - + const apr_size_t fix_offset = (offset > off[0] ? offset - off[0] : 0); + const apr_size_t fix_limit = (off[0] >= limit ? 0 + : (off[1] > limit ? off[1] - limit : 0)); + + /* Ideally, we'd do this check before assigning fix_offset and + fix_limit; but then we couldn't make them const whilst still + adhering to C90 rules. Instead, we're going to assume that a + smart optimizing compiler will reorder this check before the + local variable initialization. */ if (off[0] >= limit) break; - fix_offset = (offset > off[0] ? offset - off[0] : 0); - fix_limit = (off[1] > limit ? off[1] - limit : 0); - /* It would be extremely weird if the fixed-up op had zero length. */ assert(fix_offset + fix_limit < op->length); @@ -701,23 +704,22 @@ copy_source_ops(apr_size_t offset, apr_size_t limit, apr_size_t tgt_off = target_offset; assert(ptn_length > ptn_overlap); - /* ### FIXME: ptn_overlap is unsigned, so the if() condition - below is always true! Either it should be '> 0', or the - code block should be unconditional. See also r842362. */ - if (ptn_overlap >= 0) - { - /* Issue second subrange in the pattern. */ - const apr_size_t length = - MIN(op->length - fix_off - fix_limit, - ptn_length - ptn_overlap); - copy_source_ops(op->offset + ptn_overlap, - op->offset + ptn_overlap + length, - tgt_off, - op_ndx, - build_baton, window, ndx, pool); - fix_off += length; - tgt_off += length; - } + /* Unconditionally issue the second subrange of the + pattern. This is always correct, since the outer + condition already verifies that there is an overlap + in the target copy. */ + { + const apr_size_t length = + MIN(op->length - fix_off - fix_limit, + ptn_length - ptn_overlap); + copy_source_ops(op->offset + ptn_overlap, + op->offset + ptn_overlap + length, + tgt_off, + op_ndx, + build_baton, window, ndx, pool); + fix_off += length; + tgt_off += length; + } assert(fix_off + fix_limit <= op->length); if (ptn_overlap > 0 diff --git a/contrib/subversion/subversion/libsvn_delta/debug_editor.c b/contrib/subversion/subversion/libsvn_delta/debug_editor.c index 7c2cdec8e..8ca7b2040 100644 --- a/contrib/subversion/subversion/libsvn_delta/debug_editor.c +++ b/contrib/subversion/subversion/libsvn_delta/debug_editor.c @@ -33,6 +33,7 @@ struct edit_baton int indent_level; svn_stream_t *out; + const char *prefix; }; struct dir_baton @@ -52,8 +53,7 @@ write_indent(struct edit_baton *eb, apr_pool_t *pool) { int i; - /* This is DBG_FLAG from ../libsvn_subr/debug.c */ - SVN_ERR(svn_stream_puts(eb->out, "DBG:")); + SVN_ERR(svn_stream_puts(eb->out, eb->prefix)); for (i = 0; i < eb->indent_level; ++i) SVN_ERR(svn_stream_puts(eb->out, " ")); @@ -346,8 +346,8 @@ change_file_prop(void *file_baton, struct edit_baton *eb = fb->edit_baton; SVN_ERR(write_indent(eb, pool)); - SVN_ERR(svn_stream_printf(eb->out, pool, "change_file_prop : %s\n", - name)); + SVN_ERR(svn_stream_printf(eb->out, pool, "change_file_prop : %s -> %s\n", + name, value ? value->data : "")); SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_file_baton, name, @@ -367,7 +367,8 @@ change_dir_prop(void *dir_baton, struct edit_baton *eb = db->edit_baton; SVN_ERR(write_indent(eb, pool)); - SVN_ERR(svn_stream_printf(eb->out, pool, "change_dir_prop : %s\n", name)); + SVN_ERR(svn_stream_printf(eb->out, pool, "change_dir_prop : %s -> %s\n", + name, value ? value->data : "")); SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton, name, @@ -391,19 +392,34 @@ close_edit(void *edit_baton, return SVN_NO_ERROR; } +static svn_error_t * +abort_edit(void *edit_baton, + apr_pool_t *pool) +{ + struct edit_baton *eb = edit_baton; + + SVN_ERR(write_indent(eb, pool)); + SVN_ERR(svn_stream_printf(eb->out, pool, "abort_edit\n")); + + SVN_ERR(eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool)); + + return SVN_NO_ERROR; +} + svn_error_t * svn_delta__get_debug_editor(const svn_delta_editor_t **editor, void **edit_baton, const svn_delta_editor_t *wrapped_editor, void *wrapped_edit_baton, + const char *prefix, apr_pool_t *pool) { - svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool); + svn_delta_editor_t *tree_editor = apr_palloc(pool, sizeof(*tree_editor)); struct edit_baton *eb = apr_palloc(pool, sizeof(*eb)); apr_file_t *errfp; svn_stream_t *out; - apr_status_t apr_err = apr_file_open_stderr(&errfp, pool); + apr_status_t apr_err = apr_file_open_stdout(&errfp, pool); if (apr_err) return svn_error_wrap_apr(apr_err, "Problem opening stderr"); @@ -424,11 +440,14 @@ svn_delta__get_debug_editor(const svn_delta_editor_t **editor, 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->out = out; eb->indent_level = 0; + /* This is DBG_FLAG from ../libsvn_subr/debug.c */ + eb->prefix = apr_pstrcat(pool, "DBG: ", prefix, SVN_VA_NULL); *editor = tree_editor; *edit_baton = eb; diff --git a/contrib/subversion/subversion/libsvn_delta/debug_editor.h b/contrib/subversion/subversion/libsvn_delta/debug_editor.h index 2b031afa2..63c90d32f 100644 --- a/contrib/subversion/subversion/libsvn_delta/debug_editor.h +++ b/contrib/subversion/subversion/libsvn_delta/debug_editor.h @@ -32,14 +32,19 @@ extern "C" { /* Return a debug editor that wraps @a wrapped_editor. * * The debug editor simply prints an indication of what callbacks are being - * called to @c stderr, and is only intended for use in debugging subversion + * called to @c stdout, and is only intended for use in debugging subversion * editors. + * + * @a prefix, if non-null, is printed between "DBG: " and each indication. + * + * Note: Our test suite generally ignores stdout lines starting with "DBG:". */ svn_error_t * svn_delta__get_debug_editor(const svn_delta_editor_t **editor, void **edit_baton, const svn_delta_editor_t *wrapped_editor, void *wrapped_baton, + const char *prefix, apr_pool_t *pool); #ifdef __cplusplus diff --git a/contrib/subversion/subversion/libsvn_delta/editor.c b/contrib/subversion/subversion/libsvn_delta/editor.c index 1dc94b231..1c5e298fe 100644 --- a/contrib/subversion/subversion/libsvn_delta/editor.c +++ b/contrib/subversion/subversion/libsvn_delta/editor.c @@ -390,16 +390,6 @@ svn_editor_setcb_move(svn_editor_t *editor, } -svn_error_t * -svn_editor_setcb_rotate(svn_editor_t *editor, - svn_editor_cb_rotate_t callback, - apr_pool_t *scratch_pool) -{ - editor->funcs.cb_rotate = callback; - return SVN_NO_ERROR; -} - - svn_error_t * svn_editor_setcb_complete(svn_editor_t *editor, svn_editor_cb_complete_t callback, @@ -437,7 +427,6 @@ svn_editor_setcb_many(svn_editor_t *editor, COPY_CALLBACK(cb_delete); COPY_CALLBACK(cb_copy); COPY_CALLBACK(cb_move); - COPY_CALLBACK(cb_rotate); COPY_CALLBACK(cb_complete); COPY_CALLBACK(cb_abort); @@ -683,9 +672,9 @@ svn_error_t * svn_editor_alter_file(svn_editor_t *editor, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const svn_checksum_t *checksum, - svn_stream_t *contents) + svn_stream_t *contents, + apr_hash_t *props) { svn_error_t *err = SVN_NO_ERROR; @@ -705,8 +694,8 @@ svn_editor_alter_file(svn_editor_t *editor, { START_CALLBACK(editor); err = editor->funcs.cb_alter_file(editor->baton, - relpath, revision, props, - checksum, contents, + relpath, revision, + checksum, contents, props, editor->scratch_pool); END_CALLBACK(editor); } @@ -723,8 +712,8 @@ svn_error_t * svn_editor_alter_symlink(svn_editor_t *editor, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, - const char *target) + const char *target, + apr_hash_t *props) { svn_error_t *err = SVN_NO_ERROR; @@ -740,8 +729,8 @@ svn_editor_alter_symlink(svn_editor_t *editor, { START_CALLBACK(editor); err = editor->funcs.cb_alter_symlink(editor->baton, - relpath, revision, props, - target, + relpath, revision, + target, props, editor->scratch_pool); END_CALLBACK(editor); } @@ -861,56 +850,6 @@ svn_editor_move(svn_editor_t *editor, } -svn_error_t * -svn_editor_rotate(svn_editor_t *editor, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions) -{ - svn_error_t *err = SVN_NO_ERROR; - - SHOULD_NOT_BE_FINISHED(editor); -#ifdef ENABLE_ORDERING_CHECK - { - int i; - for (i = 0; i < relpaths->nelts; i++) - { - const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); - - SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); - SHOULD_NOT_BE_COMPLETED(editor, relpath); - VERIFY_PARENT_MAY_EXIST(editor, relpath); - CHILD_DELETIONS_ALLOWED(editor, relpath); - } - } -#endif - - SVN_ERR(check_cancel(editor)); - - if (editor->funcs.cb_rotate) - { - START_CALLBACK(editor); - err = editor->funcs.cb_rotate(editor->baton, relpaths, revisions, - editor->scratch_pool); - END_CALLBACK(editor); - } - -#ifdef ENABLE_ORDERING_CHECK - { - int i; - for (i = 0; i < relpaths->nelts; i++) - { - const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); - MARK_ALLOW_ALTER(editor, relpath); - MARK_PARENT_STABLE(editor, relpath); - } - } -#endif - - svn_pool_clear(editor->scratch_pool); - return svn_error_trace(err); -} - - svn_error_t * svn_editor_complete(svn_editor_t *editor) { diff --git a/contrib/subversion/subversion/libsvn_delta/libsvn_delta.pc.in b/contrib/subversion/subversion/libsvn_delta/libsvn_delta.pc.in new file mode 100644 index 000000000..b96e6ab3c --- /dev/null +++ b/contrib/subversion/subversion/libsvn_delta/libsvn_delta.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_delta +Description: Subversion Delta Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_subr +Libs: -L${libdir} -lsvn_delta @SVN_ZLIB_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_delta/path_driver.c b/contrib/subversion/subversion/libsvn_delta/path_driver.c index 62e703a46..c1f3e07b8 100644 --- a/contrib/subversion/subversion/libsvn_delta/path_driver.c +++ b/contrib/subversion/subversion/libsvn_delta/path_driver.c @@ -32,6 +32,7 @@ #include "svn_path.h" #include "svn_sorts.h" #include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" /*** Helper functions. ***/ @@ -157,8 +158,7 @@ svn_delta_path_driver2(const svn_delta_editor_t *editor, if (sort_paths && paths->nelts > 1) { apr_array_header_t *sorted = apr_array_copy(subpool, paths); - qsort(sorted->elts, sorted->nelts, sorted->elt_size, - svn_sort_compare_paths); + svn_sort__array(sorted, svn_sort_compare_paths); paths = sorted; } @@ -187,7 +187,7 @@ svn_delta_path_driver2(const svn_delta_editor_t *editor, driving the editor. */ for (; i < paths->nelts; i++) { - const char *pdir, *bname; + const char *pdir; const char *common = ""; size_t common_len; @@ -224,9 +224,10 @@ svn_delta_path_driver2(const svn_delta_editor_t *editor, /*** Step C - Open any directories between the common ancestor and the parent of the current path. ***/ if (*path == '/') - svn_fspath__split(&pdir, &bname, path, iterpool); + pdir = svn_fspath__dirname(path, iterpool); else - svn_relpath_split(&pdir, &bname, path, iterpool); + pdir = svn_relpath_dirname(path, iterpool); + if (strlen(pdir) > common_len) { const char *piece = pdir + common_len + 1; diff --git a/contrib/subversion/subversion/libsvn_delta/svndiff.c b/contrib/subversion/subversion/libsvn_delta/svndiff.c index dadf252fe..070c638a7 100644 --- a/contrib/subversion/subversion/libsvn_delta/svndiff.c +++ b/contrib/subversion/subversion/libsvn_delta/svndiff.c @@ -29,21 +29,12 @@ #include "delta.h" #include "svn_pools.h" #include "svn_private_config.h" -#include #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 +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "private/svn_dep_compat.h" /* ----- Text delta to svndiff ----- */ @@ -58,139 +49,31 @@ struct encoder_baton { 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) +#define MAX_INSTRUCTION_LEN (2*SVN__MAX_ENCODED_UINT_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; + unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p; - p = encode_int(buf, val); + SVN_ERR_ASSERT_NO_RETURN(val >= 0); + p = svn__encode_uint(buf, (apr_uint64_t)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 headers[4 + 5 * SVN__MAX_ENCODED_UINT_LEN + + MAX_INSTRUCTION_LEN]; unsigned char ibuf[MAX_INSTRUCTION_LEN]; unsigned char *header_current; apr_size_t header_len; @@ -226,16 +109,17 @@ send_simple_insertion_window(svn_txdelta_window_t *window, else { ibuf[0] = (0x2 << 6); - ip_len = encode_int(ibuf + 1, window->tview_len) - ibuf; + ip_len = svn__encode_uint(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 = svn__encode_uint(header_current, + (apr_uint64_t)window->sview_offset); + header_current = svn__encode_uint(header_current, window->sview_len); + header_current = svn__encode_uint(header_current, window->tview_len); header_current[0] = (unsigned char)ip_len; /* 1 instruction */ - header_current = encode_int(&header_current[1], len); + header_current = svn__encode_uint(&header_current[1], len); /* append instructions (1 to a handful of bytes) */ for (i = 0; i < ip_len; ++i) @@ -319,9 +203,9 @@ window_handler(svn_txdelta_window_t *window, void *baton) if (op->length >> 6 == 0) *ip++ |= (unsigned char)op->length; else - ip = encode_int(ip + 1, op->length); + ip = svn__encode_uint(ip + 1, op->length); if (op->action_code != svn_txdelta_new) - ip = encode_int(ip, op->offset); + ip = svn__encode_uint(ip, op->offset); svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf); } @@ -331,20 +215,20 @@ window_handler(svn_txdelta_window_t *window, void *baton) append_encoded_int(header, window->tview_len); if (eb->version == 1) { - SVN_ERR(zlib_encode(instructions->data, instructions->len, - i1, eb->compression_level)); + SVN_ERR(svn__compress(instructions, 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; + svn_stringbuf_t *compressed = svn_stringbuf_create_empty(pool); + svn_stringbuf_t *original = svn_stringbuf_create_empty(pool); + original->data = (char *)window->new_data->data; /* won't be modified */ + original->len = window->new_data->len; + original->blocksize = window->new_data->len + 1; + + SVN_ERR(svn__compress(original, compressed, eb->compression_level)); + newdata = svn_stringbuf__morph_into_string(compressed); } else newdata = window->new_data; @@ -453,128 +337,32 @@ struct decode_baton }; -/* 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. */ +/* Wrapper aroung svn__deencode_uint taking a file size as *VAL. */ 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; - } - } + apr_uint64_t temp = 0; + const unsigned char *result = svn__decode_uint(&temp, p, end); + *val = (svn_filesize_t)temp; - return NULL; + return result; } - /* 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; - } - } + apr_uint64_t temp = 0; + const unsigned char *result = svn__decode_uint(&temp, p, end); + if (temp > APR_SIZE_MAX) + return NULL; - 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; + *val = (apr_size_t)temp; + return result; } /* Decode an instruction into OP, returning a pointer to the text @@ -695,6 +483,21 @@ count_and_verify_instructions(int *ninst, return SVN_NO_ERROR; } +static svn_error_t * +zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out, + apr_size_t limit) +{ + /* construct a fake string buffer as parameter to svn__decompress. + This is fine as that function never writes to it. */ + svn_stringbuf_t compressed; + compressed.pool = NULL; + compressed.data = (char *)in; + compressed.len = inLen; + compressed.blocksize = inLen + 1; + + return svn__decompress(&compressed, out, limit); +} + /* 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; @@ -775,6 +578,10 @@ decode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset, return SVN_NO_ERROR; } +static const char SVNDIFF_V0[] = { 'S', 'V', 'N', 0 }; +static const char SVNDIFF_V1[] = { 'S', 'V', 'N', 1 }; +#define SVNDIFF_HEADER_SIZE (sizeof(SVNDIFF_V0)) + static svn_error_t * write_handler(void *baton, const char *buffer, @@ -787,14 +594,14 @@ write_handler(void *baton, apr_size_t buflen = *len; /* Chew up four bytes at the beginning for the header. */ - if (db->header_bytes < 4) + if (db->header_bytes < SVNDIFF_HEADER_SIZE) { - apr_size_t nheader = 4 - db->header_bytes; + apr_size_t nheader = SVNDIFF_HEADER_SIZE - db->header_bytes; if (nheader > buflen) nheader = buflen; - if (memcmp(buffer, "SVN\0" + db->header_bytes, nheader) == 0) + if (memcmp(buffer, SVNDIFF_V0 + db->header_bytes, nheader) == 0) db->version = 0; - else if (memcmp(buffer, "SVN\1" + db->header_bytes, nheader) == 0) + else if (memcmp(buffer, SVNDIFF_V1 + db->header_bytes, nheader) == 0) db->version = 1; else return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL, @@ -851,7 +658,7 @@ write_handler(void *baton, 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 || + newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || inslen > MAX_INSTRUCTION_SECTION_LEN) return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, _("Svndiff contains a too-large window")); @@ -908,7 +715,7 @@ write_handler(void *baton, 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) + if (db->buffer->len > 5 * SVN__MAX_ENCODED_UINT_LEN) return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, _("Svndiff contains a too-large window header")); @@ -989,7 +796,7 @@ 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)); + SVN_ERR(svn_stream_read_full(stream, &c, &len)); if (len == 0) return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, _("Unexpected end of svndiff input")); @@ -997,9 +804,12 @@ read_one_byte(unsigned char *byte, svn_stream_t *stream) return SVN_NO_ERROR; } -/* Read and decode one integer from STREAM into *SIZE. */ +/* Read and decode one integer from STREAM into *SIZE. + Increment *BYTE_COUNTER by the number of chars we have read. */ static svn_error_t * -read_one_size(apr_size_t *size, svn_stream_t *stream) +read_one_size(apr_size_t *size, + apr_size_t *byte_counter, + svn_stream_t *stream) { unsigned char c; @@ -1007,6 +817,7 @@ read_one_size(apr_size_t *size, svn_stream_t *stream) while (1) { SVN_ERR(read_one_byte(&c, stream)); + ++*byte_counter; *size = (*size << 7) | (c & 0x7f); if (!(c & 0x80)) break; @@ -1018,30 +829,33 @@ read_one_size(apr_size_t *size, svn_stream_t *stream) 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) + apr_size_t *inslen, apr_size_t *newlen, + apr_size_t *header_len) { unsigned char c; /* Read the source view offset by hand, since it's not an apr_size_t. */ + *header_len = 0; *sview_offset = 0; while (1) { SVN_ERR(read_one_byte(&c, stream)); + ++*header_len; *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)); + SVN_ERR(read_one_size(sview_len, header_len, stream)); + SVN_ERR(read_one_size(tview_len, header_len, stream)); + SVN_ERR(read_one_size(inslen, header_len, stream)); + SVN_ERR(read_one_size(newlen, header_len, 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 || + *newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || *inslen > MAX_INSTRUCTION_SECTION_LEN) return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, _("Svndiff contains a too-large window")); @@ -1063,14 +877,14 @@ svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window, apr_pool_t *pool) { svn_filesize_t sview_offset; - apr_size_t sview_len, tview_len, inslen, newlen, len; + apr_size_t sview_len, tview_len, inslen, newlen, len, header_len; unsigned char *buf; SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, - &inslen, &newlen)); + &inslen, &newlen, &header_len)); len = inslen + newlen; buf = apr_palloc(pool, len); - SVN_ERR(svn_stream_read(stream, (char*)buf, &len)); + SVN_ERR(svn_stream_read_full(stream, (char*)buf, &len)); if (len < inslen + newlen) return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, _("Unexpected end of svndiff input")); @@ -1087,29 +901,28 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file, { 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_size_t sview_len, tview_len, inslen, newlen, header_len; apr_off_t offset; SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, - &inslen, &newlen)); + &inslen, &newlen, &header_len)); 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) +svn_txdelta__read_raw_window_len(apr_size_t *window_len, + svn_stream_t *stream, + apr_pool_t *pool) { - return zlib_encode(in->data, in->len, out, compression_level); -} + svn_filesize_t sview_offset; + apr_size_t sview_len, tview_len, inslen, newlen, header_len; -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); + SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, + &inslen, &newlen, &header_len)); + + *window_len = inslen + newlen + header_len; + return SVN_NO_ERROR; } + diff --git a/contrib/subversion/subversion/libsvn_delta/text_delta.c b/contrib/subversion/subversion/libsvn_delta/text_delta.c index be2c434e8..04eca8afb 100644 --- a/contrib/subversion/subversion/libsvn_delta/text_delta.c +++ b/contrib/subversion/subversion/libsvn_delta/text_delta.c @@ -366,14 +366,14 @@ txdelta_next_window(svn_txdelta_window_t **window, /* Read the source stream. */ if (b->more_source) { - SVN_ERR(svn_stream_read(b->source, b->buf, &source_len)); + SVN_ERR(svn_stream_read_full(b->source, b->buf, &source_len)); b->more_source = (source_len == SVN_DELTA_WINDOW_SIZE); } else source_len = 0; /* Read the target stream. */ - SVN_ERR(svn_stream_read(b->target, b->buf + source_len, &target_len)); + SVN_ERR(svn_stream_read_full(b->target, b->buf + source_len, &target_len)); b->pos += source_len; if (target_len == 0) @@ -522,7 +522,7 @@ tpush_write_handler(void *baton, const char *data, apr_size_t *len) if (tb->source_len == 0 && !tb->source_done) { tb->source_len = SVN_DELTA_WINDOW_SIZE; - SVN_ERR(svn_stream_read(tb->source, tb->buf, &tb->source_len)); + SVN_ERR(svn_stream_read_full(tb->source, tb->buf, &tb->source_len)); if (tb->source_len < SVN_DELTA_WINDOW_SIZE) tb->source_done = TRUE; } @@ -623,68 +623,31 @@ size_buffer(char **buf, apr_size_t *buf_size, return SVN_NO_ERROR; } -/* Copy LEN bytes from SOURCE to TARGET, optimizing for the case where LEN - * is often very small. Return a pointer to the first byte after the copied - * target range, unlike standard memcpy(), as a potential further - * optimization for the caller. - * - * memcpy() is hard to tune for a wide range of buffer lengths. Therefore, - * it is often tuned for high throughput on large buffers and relatively - * low latency for mid-sized buffers (tens of bytes). However, the overhead - * for very small buffers (<10 bytes) is still high. Even passing the - * parameters, for instance, may take as long as copying 3 bytes. - * - * Because short copy sequences seem to be a common case, at least in - * "format 2" FSFS repositories, we copy them directly. Larger buffer sizes - * aren't hurt measurably by the exta 'if' clause. */ -static APR_INLINE char * -fast_memcpy(char *target, const char *source, apr_size_t len) -{ - if (len > 7) - { - memcpy(target, source, len); - target += len; - } - else - { - /* memcpy is not exactly fast for small block sizes. - * Since they are common, let's run optimized code for them. */ - const char *end = source + len; - for (; source != end; source++) - *(target++) = *source; - } - - return target; -} - /* Copy LEN bytes from SOURCE to TARGET. Unlike memmove() or memcpy(), * create repeating patterns if the source and target ranges overlap. * Return a pointer to the first byte after the copied target range. */ static APR_INLINE char * patterning_copy(char *target, const char *source, apr_size_t len) { - const char *end = source + len; - - /* On many machines, we can do "chunky" copies. */ - -#if SVN_UNALIGNED_ACCESS_IS_OK - - if (end + sizeof(apr_uint32_t) <= target) + /* If the source and target overlap, repeat the overlapping pattern + in the target buffer. Always copy from the source buffer because + presumably it will be in the L1 cache after the first iteration + and doing this should avoid pipeline stalls due to write/read + dependencies. */ + const apr_size_t overlap = target - source; + while (len > overlap) { - /* Source and target are at least 4 bytes apart, so we can copy in - * 4-byte chunks. */ - for (; source + sizeof(apr_uint32_t) <= end; - source += sizeof(apr_uint32_t), - target += sizeof(apr_uint32_t)) - *(apr_uint32_t *)(target) = *(apr_uint32_t *)(source); + memcpy(target, source, overlap); + target += overlap; + len -= overlap; } -#endif - - /* fall through to byte-wise copy (either for the below-chunk-size tail - * or the whole copy) */ - for (; source != end; source++) - *(target++) = *source; + /* Copy any remaining source pattern. */ + if (len) + { + memcpy(target, source, len); + target += len; + } return target; } @@ -697,6 +660,11 @@ svn_txdelta_apply_instructions(svn_txdelta_window_t *window, const svn_txdelta_op_t *op; apr_size_t tpos = 0; + /* Nothing to do for empty buffers. + * This check allows for NULL TBUF in that case. */ + if (*tlen == 0) + return; + for (op = window->ops; op < window->ops + window->num_ops; op++) { const apr_size_t buf_len = (op->length < *tlen - tpos @@ -711,7 +679,7 @@ svn_txdelta_apply_instructions(svn_txdelta_window_t *window, /* Copy from source area. */ assert(sbuf); assert(op->offset + op->length <= window->sview_len); - fast_memcpy(tbuf + tpos, sbuf + op->offset, buf_len); + memcpy(tbuf + tpos, sbuf + op->offset, buf_len); break; case svn_txdelta_target: @@ -728,9 +696,9 @@ svn_txdelta_apply_instructions(svn_txdelta_window_t *window, case svn_txdelta_new: /* Copy from window new area. */ assert(op->offset + op->length <= window->new_data->len); - fast_memcpy(tbuf + tpos, - window->new_data->data + op->offset, - buf_len); + memcpy(tbuf + tpos, + window->new_data->data + op->offset, + buf_len); break; default: @@ -747,20 +715,6 @@ svn_txdelta_apply_instructions(svn_txdelta_window_t *window, *tlen = tpos; } -/* This is a private interlibrary compatibility wrapper. */ -void -svn_txdelta__apply_instructions(svn_txdelta_window_t *window, - const char *sbuf, char *tbuf, - apr_size_t *tlen); -void -svn_txdelta__apply_instructions(svn_txdelta_window_t *window, - const char *sbuf, char *tbuf, - apr_size_t *tlen) -{ - svn_txdelta_apply_instructions(window, sbuf, tbuf, tlen); -} - - /* Apply WINDOW to the streams given by APPL. */ static svn_error_t * apply_window(svn_txdelta_window_t *window, void *baton) @@ -819,7 +773,7 @@ apply_window(svn_txdelta_window_t *window, void *baton) if (ab->sbuf_len < window->sview_len) { len = window->sview_len - ab->sbuf_len; - err = svn_stream_read(ab->source, ab->sbuf + ab->sbuf_len, &len); + err = svn_stream_read_full(ab->source, ab->sbuf + ab->sbuf_len, &len); if (err == SVN_NO_ERROR && len != window->sview_len - ab->sbuf_len) err = svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL, "Delta source ended unexpectedly"); @@ -836,13 +790,7 @@ apply_window(svn_txdelta_window_t *window, void *baton) /* Write out the output. */ - /* ### We've also considered just adding two (optionally null) - arguments to svn_stream_create(): read_checksum and - write_checksum. Then instead of every caller updating an md5 - context when it calls svn_stream_write() or svn_stream_read(), - streams would do it automatically, and verify the checksum in - svn_stream_closed(). But this might be overkill for issue #689; - so for now we just update the context here. */ + /* Just update the context here. */ if (ab->result_digest) apr_md5_update(&(ab->md5_context), ab->tbuf, len); @@ -936,7 +884,7 @@ svn_error_t *svn_txdelta_send_stream(svn_stream_t *stream, { apr_size_t read_len = SVN__STREAM_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, read_buf, &read_len)); + SVN_ERR(svn_stream_read_full(stream, read_buf, &read_len)); if (read_len == 0) break; diff --git a/contrib/subversion/subversion/libsvn_delta/xdelta.c b/contrib/subversion/subversion/libsvn_delta/xdelta.c index 2075a512b..2e5bb266a 100644 --- a/contrib/subversion/subversion/libsvn_delta/xdelta.c +++ b/contrib/subversion/subversion/libsvn_delta/xdelta.c @@ -29,6 +29,7 @@ #include "svn_hash.h" #include "svn_delta.h" +#include "private/svn_string_private.h" #include "delta.h" /* This is pseudo-adler32. It is adler32 without the prime modulus. @@ -43,6 +44,15 @@ */ #define MATCH_BLOCKSIZE 64 +/* Size of the checksum presence FLAGS array in BLOCKS_T. With standard + MATCH_BLOCKSIZE and SVN_DELTA_WINDOW_SIZE, 32k entries is about 20x + the number of checksums that actually occur, i.e. we expect a >95% + probability that non-matching checksums get already detected by checking + against the FLAGS array. + Must be a power of 2. + */ +#define FLAGS_COUNT (32 * 1024) + /* "no" / "invalid" / "unused" value for positions within the delta windows */ #define NO_POSITION ((apr_uint32_t)-1) @@ -104,7 +114,7 @@ struct block (our delta window size much much smaller then 4GB). That reduces the hash table size by 50% from 32to 16KB and makes it easier to fit into the CPU's L1 cache. */ - apr_uint32_t pos; /* NO_POSITION -> block is not used */ + apr_uint32_t pos; /* NO_POSITION -> block is not used */ }; /* A hash table, using open addressing, of the blocks of the source. */ @@ -117,8 +127,19 @@ struct blocks hte same width as the block position index, (struct block).pos. */ apr_uint32_t max; + /* Source buffer that the positions in SLOTS refer to. */ const char* data; + + /* Bit array indicating whether there may be a matching slot for a given + adler32 checksum. Since FLAGS has much more entries than SLOTS, this + will indicate most cases of non-matching checksums with a "0" bit, i.e. + as "known not to have a match". + The mapping of adler32 checksum bits is [0..2][16..27] (LSB -> MSB), + i.e. address the byte by the multiplicative part of adler32 and address + the bits in that byte by the additive part of adler32. */ + char flags[FLAGS_COUNT / 8]; + /* The vector of blocks. A pos value of NO_POSITION represents an unused slot. */ struct block *slots; @@ -135,6 +156,15 @@ static apr_uint32_t hash_func(apr_uint32_t sum) return sum ^ (sum >> 12); } +/* Return the offset in BLOCKS.FLAGS for the adler32 SUM. */ +static apr_uint32_t hash_flags(apr_uint32_t sum) +{ + /* The upper half of SUM has a wider value range than the lower 16 bit. + Also, we want to a different folding than HASH_FUNC to minimize + correlation between different hash levels. */ + return (sum >> 16) & ((FLAGS_COUNT / 8) - 1); +} + /* Insert a block with the checksum ADLERSUM at position POS in the source data into the table BLOCKS. Ignore true duplicates, i.e. blocks with actually the same content. */ @@ -152,6 +182,7 @@ add_block(struct blocks *blocks, apr_uint32_t adlersum, apr_uint32_t pos) blocks->slots[h].adlersum = adlersum; blocks->slots[h].pos = pos; + blocks->flags[hash_flags(adlersum)] |= 1 << (adlersum & 7); } /* Find a block in BLOCKS with the checksum ADLERSUM and matching the content @@ -216,6 +247,9 @@ init_blocks_table(const char *data, blocks->slots[i].pos = NO_POSITION; } + /* No checksum entries in SLOTS, yet => reset all checksum flags. */ + memset(blocks->flags, 0, sizeof(blocks->flags)); + /* If there is an odd block at the end of the buffer, we will not use that shorter block for deltification (only indirectly as an extension of some previous block). */ @@ -223,73 +257,6 @@ init_blocks_table(const char *data, add_block(blocks, init_adler32(data + i), i); } -/* Return the lowest position at which A and B differ. If no difference - * can be found in the first MAX_LEN characters, MAX_LEN will be returned. - */ -static apr_size_t -match_length(const char *a, const char *b, apr_size_t max_len) -{ - apr_size_t pos = 0; - -#if SVN_UNALIGNED_ACCESS_IS_OK - - /* Chunky processing is so much faster ... - * - * We can't make this work on architectures that require aligned access - * because A and B will probably have different alignment. So, skipping - * the first few chars until alignment is reached is not an option. - */ - for (; pos + sizeof(apr_size_t) <= max_len; pos += sizeof(apr_size_t)) - if (*(const apr_size_t*)(a + pos) != *(const apr_size_t*)(b + pos)) - break; - -#endif - - for (; pos < max_len; ++pos) - if (a[pos] != b[pos]) - break; - - return pos; -} - -/* Return the number of bytes before A and B that don't differ. If no - * difference can be found in the first MAX_LEN characters, MAX_LEN will - * be returned. Please note that A-MAX_LEN and B-MAX_LEN must both be - * valid addresses. - */ -static apr_size_t -reverse_match_length(const char *a, const char *b, apr_size_t max_len) -{ - apr_size_t pos = 0; - -#if SVN_UNALIGNED_ACCESS_IS_OK - - /* Chunky processing is so much faster ... - * - * We can't make this work on architectures that require aligned access - * because A and B will probably have different alignment. So, skipping - * the first few chars until alignment is reached is not an option. - */ - for (pos = sizeof(apr_size_t); pos <= max_len; pos += sizeof(apr_size_t)) - if (*(const apr_size_t*)(a - pos) != *(const apr_size_t*)(b - pos)) - break; - - pos -= sizeof(apr_size_t); - -#endif - - /* If we find a mismatch at -pos, pos-1 characters matched. - */ - while (++pos <= max_len) - if (a[0-pos] != b[0-pos]) - return pos - 1; - - /* No mismatch found -> at least MAX_LEN matching chars. - */ - return max_len; -} - - /* Try to find a match for the target data B in BLOCKS, and then extend the match as long as data in A and B at the match position continues to match. We set the position in A we ended up in (in @@ -323,9 +290,9 @@ find_match(const struct blocks *blocks, max_delta = asize - apos - MATCH_BLOCKSIZE < bsize - bpos - MATCH_BLOCKSIZE ? asize - apos - MATCH_BLOCKSIZE : bsize - bpos - MATCH_BLOCKSIZE; - delta = match_length(a + apos + MATCH_BLOCKSIZE, - b + bpos + MATCH_BLOCKSIZE, - max_delta); + delta = svn_cstring__match_length(a + apos + MATCH_BLOCKSIZE, + b + bpos + MATCH_BLOCKSIZE, + max_delta); /* See if we can extend backwards (max MATCH_BLOCKSIZE-1 steps because A's content has been sampled only every MATCH_BLOCKSIZE positions). */ @@ -362,7 +329,8 @@ store_delta_trailer(svn_txdelta__ops_baton_t *build_baton, if (max_len == 0) return; - end_match = reverse_match_length(a + asize, b + bsize, max_len); + end_match = svn_cstring__reverse_match_length(a + asize, b + bsize, + max_len); if (end_match <= 4) end_match = 0; @@ -409,12 +377,12 @@ compute_delta(svn_txdelta__ops_baton_t *build_baton, { struct blocks blocks; apr_uint32_t rolling; - apr_size_t lo = 0, pending_insert_start = 0; + apr_size_t lo = 0, pending_insert_start = 0, upper; /* Optimization: directly compare window starts. If more than 4 * bytes match, we can immediately create a matching windows. * Shorter sequences result in a net data increase. */ - lo = match_length(a, b, asize > bsize ? bsize : asize); + lo = svn_cstring__match_length(a, b, asize > bsize ? bsize : asize); if ((lo > 4) || (lo == bsize)) { svn_txdelta__insert_op(build_baton, svn_txdelta_source, @@ -432,19 +400,32 @@ compute_delta(svn_txdelta__ops_baton_t *build_baton, return; } + upper = bsize - MATCH_BLOCKSIZE; /* this is now known to be >= LO */ + /* Initialize the matches table. */ init_blocks_table(a, asize, &blocks, pool); /* Initialize our rolling checksum. */ rolling = init_adler32(b + lo); - while (lo < bsize) + while (lo < upper) { - apr_size_t matchlen = 0; + apr_size_t matchlen; apr_size_t apos; - if (lo + MATCH_BLOCKSIZE <= bsize) - matchlen = find_match(&blocks, rolling, a, asize, b, bsize, - &lo, &apos, pending_insert_start); + /* Quickly skip positions whose respective ROLLING checksums + definitely do not match any SLOT in BLOCKS. */ + while (!(blocks.flags[hash_flags(rolling)] & (1 << (rolling & 7))) + && lo < upper) + { + rolling = adler32_replace(rolling, b[lo], b[lo+MATCH_BLOCKSIZE]); + lo++; + } + + /* LO is still <= UPPER, i.e. the following lookup is legal: + Closely check whether we've got a match for the current location. + Due to the above pre-filter, chances are that we find one. */ + matchlen = find_match(&blocks, rolling, a, asize, b, bsize, + &lo, &apos, pending_insert_start); /* If we didn't find a real match, insert the byte at the target position into the pending insert. */ @@ -468,7 +449,8 @@ compute_delta(svn_txdelta__ops_baton_t *build_baton, { /* the match borders on the previous op. Maybe, we found a * match that is better than / overlapping the previous one. */ - apr_size_t len = reverse_match_length(a + apos, b + lo, apos < lo ? apos : lo); + apr_size_t len = svn_cstring__reverse_match_length + (a + apos, b + lo, apos < lo ? apos : lo); if (len > 0) { len = svn_txdelta__remove_copy(build_baton, len); diff --git a/contrib/subversion/subversion/libsvn_diff/binary_diff.c b/contrib/subversion/subversion/libsvn_diff/binary_diff.c new file mode 100644 index 000000000..035794dbe --- /dev/null +++ b/contrib/subversion/subversion/libsvn_diff/binary_diff.c @@ -0,0 +1,221 @@ +/* + * binary_diff.c: handling of git like binary 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 + +#include "svn_pools.h" +#include "svn_error.h" +#include "svn_diff.h" +#include "svn_types.h" + +/* Copies the data from ORIGINAL_STREAM to a temporary file, returning both + the original and compressed size. */ +static svn_error_t * +create_compressed(apr_file_t **result, + svn_filesize_t *full_size, + svn_filesize_t *compressed_size, + svn_stream_t *original_stream, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *compressed; + svn_filesize_t bytes_read = 0; + apr_finfo_t finfo; + apr_size_t rd; + + SVN_ERR(svn_io_open_uniquely_named(result, NULL, NULL, "diffgz", + NULL, svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + + compressed = svn_stream_compressed( + svn_stream_from_aprfile2(*result, TRUE, scratch_pool), + scratch_pool); + + if (original_stream) + do + { + char buffer[SVN_STREAM_CHUNK_SIZE]; + rd = sizeof(buffer); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + SVN_ERR(svn_stream_read_full(original_stream, buffer, &rd)); + + bytes_read += rd; + SVN_ERR(svn_stream_write(compressed, buffer, &rd)); + } + while(rd == SVN_STREAM_CHUNK_SIZE); + else + { + apr_size_t zero = 0; + SVN_ERR(svn_stream_write(compressed, NULL, &zero)); + } + + SVN_ERR(svn_stream_close(compressed)); /* Flush compression */ + + *full_size = bytes_read; + SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, *result, scratch_pool)); + *compressed_size = finfo.size; + + return SVN_NO_ERROR; +} + +#define GIT_BASE85_CHUNKSIZE 52 + +/* Git Base-85 table for write_literal */ +static const char b85str[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "!#$%&()*+-;<=>?@^_`{|}~"; + +/* Git length encoding table for write_literal */ +static const char b85lenstr[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + +/* Writes out a git-like literal output of the compressed data in + COMPRESSED_DATA to OUTPUT_STREAM, describing that its normal length is + UNCOMPRESSED_SIZE. */ +static svn_error_t * +write_literal(svn_filesize_t uncompressed_size, + svn_stream_t *compressed_data, + svn_stream_t *output_stream, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + apr_size_t rd; + SVN_ERR(svn_stream_seek(compressed_data, NULL)); /* Seek to start */ + + SVN_ERR(svn_stream_printf(output_stream, scratch_pool, + "literal %" SVN_FILESIZE_T_FMT APR_EOL_STR, + uncompressed_size)); + + do + { + char chunk[GIT_BASE85_CHUNKSIZE]; + const unsigned char *next; + apr_size_t left; + + rd = sizeof(chunk); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + SVN_ERR(svn_stream_read_full(compressed_data, chunk, &rd)); + + { + apr_size_t one = 1; + SVN_ERR(svn_stream_write(output_stream, &b85lenstr[rd-1], &one)); + } + + left = rd; + next = (void*)chunk; + while (left) + { + char five[5]; + unsigned info = 0; + int n; + apr_size_t five_sz; + + /* Push 4 bytes into the 32 bit info, when available */ + for (n = 24; n >= 0 && left; n -= 8, next++, left--) + { + info |= (*next) << n; + } + + /* Write out info as base85 */ + for (n = 4; n >= 0; n--) + { + five[n] = b85str[info % 85]; + info /= 85; + } + + five_sz = 5; + SVN_ERR(svn_stream_write(output_stream, five, &five_sz)); + } + + SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); + } + while (rd == GIT_BASE85_CHUNKSIZE); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_diff_output_binary(svn_stream_t *output_stream, + svn_stream_t *original, + svn_stream_t *latest, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + apr_file_t *original_apr; + svn_filesize_t original_full; + svn_filesize_t original_deflated; + apr_file_t *latest_apr; + svn_filesize_t latest_full; + svn_filesize_t latest_deflated; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_compressed(&original_apr, &original_full, &original_deflated, + original, cancel_func, cancel_baton, + scratch_pool, subpool)); + svn_pool_clear(subpool); + + SVN_ERR(create_compressed(&latest_apr, &latest_full, &latest_deflated, + latest, cancel_func, cancel_baton, + scratch_pool, subpool)); + svn_pool_clear(subpool); + + SVN_ERR(svn_stream_puts(output_stream, "GIT binary patch" APR_EOL_STR)); + + /* ### git would first calculate if a git-delta latest->original would be + shorter than the zipped data. For now lets assume that it is not + and just dump the literal data */ + SVN_ERR(write_literal(latest_full, + svn_stream_from_aprfile2(latest_apr, FALSE, subpool), + output_stream, + cancel_func, cancel_baton, + scratch_pool)); + svn_pool_clear(subpool); + SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); + + /* ### git would first calculate if a git-delta original->latest would be + shorter than the zipped data. For now lets assume that it is not + and just dump the literal data */ + SVN_ERR(write_literal(original_full, + svn_stream_from_aprfile2(original_apr, FALSE, subpool), + output_stream, + cancel_func, cancel_baton, + scratch_pool)); + svn_pool_destroy(subpool); + + SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_diff/deprecated.c b/contrib/subversion/subversion/libsvn_diff/deprecated.c index 891ad5fa8..b8dc097aa 100644 --- a/contrib/subversion/subversion/libsvn_diff/deprecated.c +++ b/contrib/subversion/subversion/libsvn_diff/deprecated.c @@ -143,6 +143,34 @@ wrap_diff_fns(svn_diff_fns2_t **diff_fns2, /*** From diff_file.c ***/ + +svn_error_t * +svn_diff_file_output_unified3(svn_stream_t *output_stream, + svn_diff_t *diff, + const char *original_path, + const char *modified_path, + const char *original_header, + const char *modified_header, + const char *header_encoding, + const char *relative_to_dir, + svn_boolean_t show_c_function, + apr_pool_t *pool) +{ + return svn_error_trace( + svn_diff_file_output_unified4(output_stream, + diff, + original_path, + modified_path, + original_header, + modified_header, + header_encoding, + relative_to_dir, + show_c_function, + -1 /* context_size */, + NULL, NULL, /* cancel */ + pool)); +} + svn_error_t * svn_diff_file_output_unified2(svn_stream_t *output_stream, svn_diff_t *diff, @@ -243,6 +271,31 @@ svn_diff_file_output_merge(svn_stream_t *output_stream, pool); } +svn_error_t * +svn_diff_file_output_merge2(svn_stream_t *output_stream, + svn_diff_t *diff, + const char *original_path, + const char *modified_path, + const char *latest_path, + const char *conflict_original, + const char *conflict_modified, + const char *conflict_latest, + const char *conflict_separator, + svn_diff_conflict_display_style_t conflict_style, + apr_pool_t *pool) +{ + return svn_error_trace(svn_diff_file_output_merge3(output_stream, + diff, original_path, + modified_path, + latest_path, + conflict_original, + conflict_modified, + conflict_latest, + conflict_separator, + conflict_style, + NULL, NULL, /* cancel */ + pool)); +} /*** From diff.c ***/ svn_error_t * @@ -287,3 +340,125 @@ svn_diff_diff4(svn_diff_t **diff, wrap_diff_fns(&diff_fns2, &fwb, vtable, diff_baton, pool); return svn_diff_diff4_2(diff, fwb, diff_fns2, pool); } + +/*** From util.c ***/ +svn_error_t * +svn_diff_output(svn_diff_t *diff, + void *output_baton, + const svn_diff_output_fns_t *output_fns) +{ + return svn_error_trace(svn_diff_output2(diff, output_baton, output_fns, + NULL, NULL /* cancel */)); +} + +/*** From diff_memory.c ***/ +svn_error_t * +svn_diff_mem_string_output_merge(svn_stream_t *output_stream, + svn_diff_t *diff, + const svn_string_t *original, + const svn_string_t *modified, + const svn_string_t *latest, + const char *conflict_original, + const char *conflict_modified, + const char *conflict_latest, + const char *conflict_separator, + svn_boolean_t display_original_in_conflict, + svn_boolean_t display_resolved_conflicts, + apr_pool_t *pool) +{ + svn_diff_conflict_display_style_t style = + svn_diff_conflict_display_modified_latest; + + if (display_resolved_conflicts) + style = svn_diff_conflict_display_resolved_modified_latest; + + if (display_original_in_conflict) + style = svn_diff_conflict_display_modified_original_latest; + + return svn_diff_mem_string_output_merge2(output_stream, + diff, + original, + modified, + latest, + conflict_original, + conflict_modified, + conflict_latest, + conflict_separator, + style, + pool); +} + +svn_error_t * +svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, + svn_diff_t *diff, + const svn_string_t *original, + const svn_string_t *modified, + const svn_string_t *latest, + const char *conflict_original, + const char *conflict_modified, + const char *conflict_latest, + const char *conflict_separator, + svn_diff_conflict_display_style_t style, + apr_pool_t *pool) +{ + return svn_error_trace(svn_diff_mem_string_output_merge3(output_stream, diff, + original, + modified, latest, + conflict_original, + conflict_modified, + conflict_latest, + conflict_separator, + style, + /* no cancelation */ + NULL, NULL, + pool)); +} + +svn_error_t * +svn_diff_mem_string_output_unified(svn_stream_t *output_stream, + svn_diff_t *diff, + const char *original_header, + const char *modified_header, + const char *header_encoding, + const svn_string_t *original, + const svn_string_t *modified, + apr_pool_t *pool) +{ + return svn_error_trace(svn_diff_mem_string_output_unified2(output_stream, + diff, + TRUE, + NULL, + original_header, + modified_header, + header_encoding, + original, + modified, + pool)); +} + +svn_error_t * +svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, + svn_diff_t *diff, + svn_boolean_t with_diff_header, + const char *hunk_delimiter, + const char *original_header, + const char *modified_header, + const char *header_encoding, + const svn_string_t *original, + const svn_string_t *modified, + apr_pool_t *pool) +{ + return svn_error_trace(svn_diff_mem_string_output_unified3(output_stream, + diff, + with_diff_header, + hunk_delimiter, + original_header, + modified_header, + header_encoding, + original, + modified, + -1 /* context */, + /* cancel */ + NULL, NULL, + pool)); +} diff --git a/contrib/subversion/subversion/libsvn_diff/diff4.c b/contrib/subversion/subversion/libsvn_diff/diff4.c index 9f3cb8cd2..1ecbb38e8 100644 --- a/contrib/subversion/subversion/libsvn_diff/diff4.c +++ b/contrib/subversion/subversion/libsvn_diff/diff4.c @@ -270,7 +270,7 @@ svn_diff_diff4_2(svn_diff_t **diff, } /* Get the lcs for common ancestor - original - * Do reverse adjustements + * Do reverse adjustments */ lcs_adjust = svn_diff__lcs(position_list[3], position_list[2], token_counts[3], token_counts[2], diff --git a/contrib/subversion/subversion/libsvn_diff/diff_file.c b/contrib/subversion/subversion/libsvn_diff/diff_file.c index 830552ac4..f54522e7c 100644 --- a/contrib/subversion/subversion/libsvn_diff/diff_file.c +++ b/contrib/subversion/subversion/libsvn_diff/diff_file.c @@ -31,6 +31,8 @@ #include #include +#include + #include "svn_error.h" #include "svn_diff.h" #include "svn_types.h" @@ -137,16 +139,16 @@ datasource_to_index(svn_diff_datasource_e datasource) * *LENGTH. The actual bytes read are stored in *LENGTH on return. */ static APR_INLINE svn_error_t * -read_chunk(apr_file_t *file, const char *path, +read_chunk(apr_file_t *file, char *buffer, apr_off_t length, - apr_off_t offset, apr_pool_t *pool) + apr_off_t offset, apr_pool_t *scratch_pool) { /* XXX: The final offset may not be the one we asked for. * XXX: Check. */ - SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool)); return svn_io_file_read_full2(file, buffer, (apr_size_t) length, - NULL, NULL, pool); + NULL, NULL, scratch_pool); } @@ -286,7 +288,7 @@ increment_chunk(struct file_info *file, apr_pool_t *pool) file->chunk++; length = file->chunk == last_chunk ? offset_in_chunk(file->size) : CHUNK_SIZE; - SVN_ERR(read_chunk(file->file, file->path, file->buffer, + SVN_ERR(read_chunk(file->file, file->buffer, length, chunk_to_offset(file->chunk), pool)); file->endp = file->buffer + length; @@ -313,7 +315,7 @@ decrement_chunk(struct file_info *file, apr_pool_t *pool) { /* Read previous chunk and reset pointers. */ file->chunk--; - SVN_ERR(read_chunk(file->file, file->path, file->buffer, + SVN_ERR(read_chunk(file->file, file->buffer, CHUNK_SIZE, chunk_to_offset(file->chunk), pool)); file->endp = file->buffer + CHUNK_SIZE; @@ -542,7 +544,6 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], int suffix_lines_to_keep = SUFFIX_LINES_TO_KEEP; svn_boolean_t is_match; apr_off_t lines = 0; - svn_boolean_t had_cr; svn_boolean_t had_nl; apr_size_t i; @@ -573,7 +574,7 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], /* There is at least more than 1 chunk, so allocate full chunk size buffer */ file_for_suffix[i].buffer = apr_palloc(pool, CHUNK_SIZE); - SVN_ERR(read_chunk(file_for_suffix[i].file, file_for_suffix[i].path, + SVN_ERR(read_chunk(file_for_suffix[i].file, file_for_suffix[i].buffer, length[i], chunk_to_offset(file_for_suffix[i].chunk), pool)); @@ -646,11 +647,10 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], min_curp[0] += suffix_min_offset0; /* Scan quickly by reading with machine-word granularity. */ - for (i = 0, can_read_word = TRUE; i < file_len; i++) - can_read_word = can_read_word - && ( (file_for_suffix[i].curp + 1 - - sizeof(apr_uintptr_t)) - > min_curp[i]); + for (i = 0, can_read_word = TRUE; can_read_word && i < file_len; i++) + can_read_word = ((file_for_suffix[i].curp + 1 - sizeof(apr_uintptr_t)) + > min_curp[i]); + while (can_read_word) { apr_uintptr_t chunk; @@ -664,9 +664,8 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], if (contains_eol(chunk)) break; - for (i = 1, is_match = TRUE; i < file_len; i++) - is_match = is_match - && ( chunk + for (i = 1, is_match = TRUE; is_match && i < file_len; i++) + is_match = (chunk == *(const apr_uintptr_t *) (file_for_suffix[i].curp + 1 - sizeof(apr_uintptr_t))); @@ -685,7 +684,6 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], /* We skipped some bytes, so there are no closing EOLs */ had_nl = FALSE; - had_cr = FALSE; } /* The > min_curp[i] check leaves at least one final byte for checking @@ -712,7 +710,7 @@ find_identical_suffix(apr_off_t *suffix_lines, struct file_info file[], one file reaches its end. */ do { - had_cr = FALSE; + svn_boolean_t had_cr = FALSE; while (!is_one_at_eof(file_for_suffix, file_len) && *file_for_suffix[0].curp != '\n' && *file_for_suffix[0].curp != '\r') @@ -803,7 +801,7 @@ datasources_open(void *baton, file->size = finfo[i].size; length[i] = finfo[i].size > CHUNK_SIZE ? CHUNK_SIZE : finfo[i].size; file->buffer = apr_palloc(file_baton->pool, (apr_size_t) length[i]); - SVN_ERR(read_chunk(file->file, file->path, file->buffer, + SVN_ERR(read_chunk(file->file, file->buffer, length[i], 0, file_baton->pool)); file->endp = file->buffer + length[i]; file->curp = file->buffer; @@ -969,9 +967,9 @@ datasource_get_next_token(apr_uint32_t *hash, void **token, void *baton, function. When changing things here, make sure the whitespace settings are - applied, or we mught not reach the exact suffix boundary as token + applied, or we might not reach the exact suffix boundary as token boundary. */ - SVN_ERR(read_chunk(file->file, file->path, + SVN_ERR(read_chunk(file->file, curp, length, chunk_to_offset(file->chunk), file_baton->pool)); @@ -1113,7 +1111,6 @@ token_compare(void *baton, void *token1, void *token2, int *compare) COMPARE_CHUNK_SIZE : raw_length[i]; SVN_ERR(read_chunk(file[i]->file, - file[i]->path, bufp[i], length[i], offset[i], file_baton->pool)); offset[i] += length[i]; @@ -1196,13 +1193,18 @@ static const apr_getopt_option_t diff_options[] = /* ### For compatibility; we don't support the argument to -u, because * ### we don't have optional argument support. */ { "unified", 'u', 0, NULL }, + { "context", 'U', 1, NULL }, { NULL, 0, 0, NULL } }; svn_diff_file_options_t * svn_diff_file_options_create(apr_pool_t *pool) { - return apr_pcalloc(pool, sizeof(svn_diff_file_options_t)); + svn_diff_file_options_t * opts = apr_pcalloc(pool, sizeof(*opts)); + + opts->context_size = SVN_DIFF__UNIFIED_CONTEXT_SIZE; + + return opts; } /* A baton for use with opt_parsing_error_func(). */ @@ -1248,7 +1250,7 @@ svn_diff_file_options_parse(svn_diff_file_options_t *options, opt_parsing_error_baton.pool = pool; argv[0] = ""; - memcpy((void *) (argv + 1), args->elts, sizeof(char*) * args->nelts); + memcpy(argv + 1, args->elts, sizeof(char*) * args->nelts); argv[args->nelts + 1] = NULL; apr_getopt_init(&os, pool, args->nelts + 1, argv); @@ -1291,6 +1293,9 @@ svn_diff_file_options_parse(svn_diff_file_options_t *options, case 'p': options->show_c_function = TRUE; break; + case 'U': + SVN_ERR(svn_cstring_atoi(&options->context_size, opt_arg)); + break; default: break; } @@ -1410,6 +1415,8 @@ typedef struct svn_diff__file_output_baton_t /* Extra context for the current hunk. */ char hunk_extra_context[SVN_DIFF__EXTRA_CONTEXT_LENGTH + 1]; + int context_size; + apr_pool_t *pool; } svn_diff__file_output_baton_t; @@ -1615,7 +1622,7 @@ output_unified_flush_hunk(svn_diff__file_output_baton_t *baton) } target_line = baton->hunk_start[0] + baton->hunk_length[0] - + SVN_DIFF__UNIFIED_CONTEXT_SIZE; + + baton->context_size; /* Add trailing context to the hunk */ SVN_ERR(output_unified_diff_range(baton, 0 /* original */, @@ -1666,8 +1673,8 @@ output_unified_diff_modified(void *baton, apr_off_t prev_context_end; svn_boolean_t init_hunk = FALSE; - if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE) - context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE; + if (original_start > output_baton->context_size) + context_prefix_length = output_baton->context_size; else context_prefix_length = original_start; @@ -1677,7 +1684,7 @@ output_unified_diff_modified(void *baton, { prev_context_end = output_baton->hunk_start[0] + output_baton->hunk_length[0] - + SVN_DIFF__UNIFIED_CONTEXT_SIZE; + + output_baton->context_size; } else { @@ -1815,7 +1822,7 @@ static const svn_diff_output_fns_t svn_diff__file_output_unified_vtable = }; svn_error_t * -svn_diff_file_output_unified3(svn_stream_t *output_stream, +svn_diff_file_output_unified4(svn_stream_t *output_stream, svn_diff_t *diff, const char *original_path, const char *modified_path, @@ -1824,6 +1831,9 @@ svn_diff_file_output_unified3(svn_stream_t *output_stream, const char *header_encoding, const char *relative_to_dir, svn_boolean_t show_c_function, + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { if (svn_diff_contains_diffs(diff)) @@ -1840,6 +1850,8 @@ svn_diff_file_output_unified3(svn_stream_t *output_stream, baton.hunk = svn_stringbuf_create_empty(pool); baton.show_c_function = show_c_function; baton.extra_context = svn_stringbuf_create_empty(pool); + baton.context_size = (context_size >= 0) ? context_size + : SVN_DIFF__UNIFIED_CONTEXT_SIZE; if (show_c_function) { @@ -1918,8 +1930,9 @@ svn_diff_file_output_unified3(svn_stream_t *output_stream, original_header, modified_header, pool)); - SVN_ERR(svn_diff_output(diff, &baton, - &svn_diff__file_output_unified_vtable)); + SVN_ERR(svn_diff_output2(diff, &baton, + &svn_diff__file_output_unified_vtable, + cancel_func, cancel_baton)); SVN_ERR(output_unified_flush_hunk(&baton)); for (i = 0; i < 2; i++) @@ -1939,8 +1952,9 @@ svn_diff_file_output_unified3(svn_stream_t *output_stream, *pointers! */ typedef struct context_saver_t { svn_stream_t *stream; - const char *data[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; - apr_size_t len[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; + int context_size; + const char **data; /* const char *data[context_size] */ + apr_size_t *len; /* apr_size_t len[context_size] */ apr_size_t next_slot; apr_size_t total_written; } context_saver_t; @@ -1952,10 +1966,14 @@ context_saver_stream_write(void *baton, apr_size_t *len) { context_saver_t *cs = baton; - cs->data[cs->next_slot] = data; - cs->len[cs->next_slot] = *len; - cs->next_slot = (cs->next_slot + 1) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; - cs->total_written++; + + if (cs->context_size > 0) + { + cs->data[cs->next_slot] = data; + cs->len[cs->next_slot] = *len; + cs->next_slot = (cs->next_slot + 1) % cs->context_size; + cs->total_written++; + } return SVN_NO_ERROR; } @@ -1980,6 +1998,11 @@ typedef struct svn_diff3__file_output_baton_t const char *marker_eol; svn_diff_conflict_display_style_t conflict_style; + int context_size; + + /* cancel support */ + svn_cancel_func_t cancel_func; + void *cancel_baton; /* The rest of the fields are for svn_diff_conflict_display_only_conflicts only. Note that for @@ -1999,9 +2022,9 @@ flush_context_saver(context_saver_t *cs, svn_stream_t *output_stream) { int i; - for (i = 0; i < SVN_DIFF__UNIFIED_CONTEXT_SIZE; i++) + for (i = 0; i < cs->context_size; i++) { - apr_size_t slot = (i + cs->next_slot) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; + apr_size_t slot = (i + cs->next_slot) % cs->context_size; if (cs->data[slot]) { apr_size_t len = cs->len[slot]; @@ -2016,6 +2039,8 @@ make_context_saver(svn_diff3__file_output_baton_t *fob) { context_saver_t *cs; + assert(fob->context_size > 0); /* Or nothing to save */ + svn_pool_clear(fob->pool); cs = apr_pcalloc(fob->pool, sizeof(*cs)); cs->stream = svn_stream_empty(fob->pool); @@ -2023,10 +2048,13 @@ make_context_saver(svn_diff3__file_output_baton_t *fob) svn_stream_set_write(cs->stream, context_saver_stream_write); fob->context_saver = cs; fob->output_stream = cs->stream; + cs->context_size = fob->context_size; + cs->data = apr_pcalloc(fob->pool, sizeof(*cs->data) * cs->context_size); + cs->len = apr_pcalloc(fob->pool, sizeof(*cs->len) * cs->context_size); } -/* A stream which prints SVN_DIFF__UNIFIED_CONTEXT_SIZE lines to +/* A stream which prints LINES_TO_PRINT (based on context size) lines to BATON->REAL_OUTPUT_STREAM, and then changes BATON->OUTPUT_STREAM to a context_saver; used for *trailing* context. */ @@ -2061,7 +2089,7 @@ make_trailing_context_printer(svn_diff3__file_output_baton_t *btn) svn_pool_clear(btn->pool); tcp = apr_pcalloc(btn->pool, sizeof(*tcp)); - tcp->lines_to_print = SVN_DIFF__UNIFIED_CONTEXT_SIZE; + tcp->lines_to_print = btn->context_size; tcp->fob = btn; s = svn_stream_empty(btn->pool); svn_stream_set_baton(s, tcp); @@ -2191,7 +2219,25 @@ static const svn_diff_output_fns_t svn_diff3__file_output_vtable = output_conflict }; +static svn_error_t * +output_conflict_with_context_marker(svn_diff3__file_output_baton_t *btn, + const char *label, + apr_off_t start, + apr_off_t length) +{ + if (length == 1) + SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, + "%s (%" APR_OFF_T_FMT ")", + label, start + 1)); + else + SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, + "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")", + label, start + 1, length)); + SVN_ERR(output_marker_eol(btn)); + + return SVN_NO_ERROR; +} static svn_error_t * output_conflict_with_context(svn_diff3__file_output_baton_t *btn, @@ -2206,7 +2252,7 @@ output_conflict_with_context(svn_diff3__file_output_baton_t *btn, trailing context)? If so, flush it. */ if (btn->output_stream == btn->context_saver->stream) { - if (btn->context_saver->total_written > SVN_DIFF__UNIFIED_CONTEXT_SIZE) + if (btn->context_saver->total_written > btn->context_size) SVN_ERR(svn_stream_puts(btn->real_output_stream, "@@\n")); SVN_ERR(flush_context_saver(btn->context_saver, btn->real_output_stream)); } @@ -2215,34 +2261,19 @@ output_conflict_with_context(svn_diff3__file_output_baton_t *btn, btn->output_stream = btn->real_output_stream; /* Output the conflict itself. */ - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (modified_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->conflict_modified, - modified_start + 1, modified_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->conflict_modified, + modified_start, modified_length)); SVN_ERR(output_hunk(btn, 1/*modified*/, modified_start, modified_length)); - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (original_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->conflict_original, - original_start + 1, original_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->conflict_original, + original_start, original_length)); SVN_ERR(output_hunk(btn, 0/*original*/, original_start, original_length)); SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, "%s%s", btn->conflict_separator, btn->marker_eol)); SVN_ERR(output_hunk(btn, 2/*latest*/, latest_start, latest_length)); - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (latest_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->conflict_latest, - latest_start + 1, latest_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->conflict_latest, + latest_start, latest_length)); /* Go into print-trailing-context mode instead. */ make_trailing_context_printer(btn); @@ -2271,8 +2302,10 @@ output_conflict(void *baton, if (style == svn_diff_conflict_display_resolved_modified_latest) { if (diff) - return svn_diff_output(diff, baton, - &svn_diff3__file_output_vtable); + return svn_diff_output2(diff, baton, + &svn_diff3__file_output_vtable, + file_baton->cancel_func, + file_baton->cancel_baton); else style = svn_diff_conflict_display_modified_latest; } @@ -2315,7 +2348,7 @@ output_conflict(void *baton, } svn_error_t * -svn_diff_file_output_merge2(svn_stream_t *output_stream, +svn_diff_file_output_merge3(svn_stream_t *output_stream, svn_diff_t *diff, const char *original_path, const char *modified_path, @@ -2325,7 +2358,9 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, const char *conflict_latest, const char *conflict_separator, svn_diff_conflict_display_style_t style, - apr_pool_t *pool) + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { svn_diff3__file_output_baton_t baton; apr_file_t *file[3]; @@ -2338,9 +2373,10 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, (style == svn_diff_conflict_display_only_conflicts); memset(&baton, 0, sizeof(baton)); + baton.context_size = SVN_DIFF__UNIFIED_CONTEXT_SIZE; if (conflicts_only) { - baton.pool = svn_pool_create(pool); + baton.pool = svn_pool_create(scratch_pool); make_context_saver(&baton); baton.real_output_stream = output_stream; } @@ -2351,22 +2387,22 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, baton.path[2] = latest_path; SVN_ERR(svn_utf_cstring_from_utf8(&baton.conflict_modified, conflict_modified ? conflict_modified - : apr_psprintf(pool, "<<<<<<< %s", + : apr_psprintf(scratch_pool, "<<<<<<< %s", modified_path), - pool)); + scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&baton.conflict_original, conflict_original ? conflict_original - : apr_psprintf(pool, "||||||| %s", + : apr_psprintf(scratch_pool, "||||||| %s", original_path), - pool)); + scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&baton.conflict_separator, conflict_separator ? conflict_separator - : "=======", pool)); + : "=======", scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&baton.conflict_latest, conflict_latest ? conflict_latest - : apr_psprintf(pool, ">>>>>>> %s", + : apr_psprintf(scratch_pool, ">>>>>>> %s", latest_path), - pool)); + scratch_pool)); baton.conflict_style = style; @@ -2377,7 +2413,7 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, SVN_ERR(map_or_read_file(&file[idx], MMAP_T_ARG(mm[idx]) &baton.buffer[idx], &size, - baton.path[idx], pool)); + baton.path[idx], scratch_pool)); baton.curp[idx] = baton.buffer[idx]; baton.endp[idx] = baton.buffer[idx]; @@ -2395,8 +2431,12 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, eol = APR_EOL_STR; baton.marker_eol = eol; - SVN_ERR(svn_diff_output(diff, &baton, - &svn_diff3__file_output_vtable)); + baton.cancel_func = cancel_func; + baton.cancel_baton = cancel_baton; + + SVN_ERR(svn_diff_output2(diff, &baton, + &svn_diff3__file_output_vtable, + cancel_func, cancel_baton)); for (idx = 0; idx < 3; idx++) { @@ -2414,7 +2454,7 @@ svn_diff_file_output_merge2(svn_stream_t *output_stream, if (file[idx]) { - SVN_ERR(svn_io_file_close(file[idx], pool)); + SVN_ERR(svn_io_file_close(file[idx], scratch_pool)); } } diff --git a/contrib/subversion/subversion/libsvn_diff/diff_memory.c b/contrib/subversion/subversion/libsvn_diff/diff_memory.c index 00f4c7fd4..d9d800d71 100644 --- a/contrib/subversion/subversion/libsvn_diff/diff_memory.c +++ b/contrib/subversion/subversion/libsvn_diff/diff_memory.c @@ -356,6 +356,8 @@ typedef struct unified_output_baton_t source_tokens_t sources[2]; /* 0 == original; 1 == modified */ apr_off_t current_token[2]; /* current token per source */ + int context_size; + /* Cached markers, in header_encoding, indexed using unified_output_e */ const char *prefix_str[3]; @@ -461,7 +463,7 @@ output_unified_flush_hunk(output_baton_t *baton, /* Write the trailing context */ target_token = baton->hunk_start[0] + baton->hunk_length[0] - + SVN_DIFF__UNIFIED_CONTEXT_SIZE; + + baton->context_size; SVN_ERR(output_unified_token_range(baton, 0 /*original*/, unified_output_context, target_token)); @@ -515,8 +517,8 @@ output_unified_diff_modified(void *baton, apr_off_t prev_context_end; svn_boolean_t init_hunk = FALSE; - if (original_start > SVN_DIFF__UNIFIED_CONTEXT_SIZE) - context_prefix_length = SVN_DIFF__UNIFIED_CONTEXT_SIZE; + if (original_start > output_baton->context_size) + context_prefix_length = output_baton->context_size; else context_prefix_length = original_start; @@ -526,7 +528,7 @@ output_unified_diff_modified(void *baton, { prev_context_end = output_baton->hunk_start[0] + output_baton->hunk_length[0] - + SVN_DIFF__UNIFIED_CONTEXT_SIZE; + + output_baton->context_size; } else { @@ -608,7 +610,7 @@ static const svn_diff_output_fns_t mem_output_unified_vtable = svn_error_t * -svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, +svn_diff_mem_string_output_unified3(svn_stream_t *output_stream, svn_diff_t *diff, svn_boolean_t with_diff_header, const char *hunk_delimiter, @@ -617,7 +619,10 @@ svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, const char *header_encoding, const svn_string_t *original, const svn_string_t *modified, - apr_pool_t *pool) + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { if (svn_diff_contains_diffs(diff)) @@ -626,37 +631,40 @@ svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, memset(&baton, 0, sizeof(baton)); baton.output_stream = output_stream; - baton.pool = svn_pool_create(pool); + baton.pool = svn_pool_create(scratch_pool); baton.header_encoding = header_encoding; - baton.hunk = svn_stringbuf_create_empty(pool); + baton.hunk = svn_stringbuf_create_empty(scratch_pool); baton.hunk_delimiter = hunk_delimiter; baton.no_newline_string = (hunk_delimiter == NULL || strcmp(hunk_delimiter, "##") != 0) ? APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_FILE APR_EOL_STR : APR_EOL_STR SVN_DIFF__NO_NEWLINE_AT_END_OF_PROPERTY APR_EOL_STR; + baton.context_size = context_size >= 0 ? context_size + : SVN_DIFF__UNIFIED_CONTEXT_SIZE; SVN_ERR(svn_utf_cstring_from_utf8_ex2 (&(baton.prefix_str[unified_output_context]), " ", - header_encoding, pool)); + header_encoding, scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8_ex2 (&(baton.prefix_str[unified_output_delete]), "-", - header_encoding, pool)); + header_encoding, scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8_ex2 (&(baton.prefix_str[unified_output_insert]), "+", - header_encoding, pool)); + header_encoding, scratch_pool)); - fill_source_tokens(&baton.sources[0], original, pool); - fill_source_tokens(&baton.sources[1], modified, pool); + fill_source_tokens(&baton.sources[0], original, scratch_pool); + fill_source_tokens(&baton.sources[1], modified, scratch_pool); if (with_diff_header) { SVN_ERR(svn_diff__unidiff_write_header( output_stream, header_encoding, - original_header, modified_header, pool)); + original_header, modified_header, scratch_pool)); } - SVN_ERR(svn_diff_output(diff, &baton, - &mem_output_unified_vtable)); + SVN_ERR(svn_diff_output2(diff, &baton, + &mem_output_unified_vtable, + cancel_func, cancel_baton)); SVN_ERR(output_unified_flush_hunk(&baton, hunk_delimiter)); @@ -666,28 +674,6 @@ svn_diff_mem_string_output_unified2(svn_stream_t *output_stream, return SVN_NO_ERROR; } -svn_error_t * -svn_diff_mem_string_output_unified(svn_stream_t *output_stream, - svn_diff_t *diff, - const char *original_header, - const char *modified_header, - const char *header_encoding, - const svn_string_t *original, - const svn_string_t *modified, - apr_pool_t *pool) -{ - SVN_ERR(svn_diff_mem_string_output_unified2(output_stream, - diff, - TRUE, - NULL, - original_header, - modified_header, - header_encoding, - original, - modified, - pool)); - return SVN_NO_ERROR; -} @@ -698,8 +684,9 @@ svn_diff_mem_string_output_unified(svn_stream_t *output_stream, *pointers! */ typedef struct context_saver_t { svn_stream_t *stream; - const char *data[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; - apr_size_t len[SVN_DIFF__UNIFIED_CONTEXT_SIZE]; + int context_size; + const char **data; /* const char *data[context_size] */ + apr_size_t *len; /* apr_size_t len[context_size] */ apr_size_t next_slot; apr_size_t total_written; } context_saver_t; @@ -713,7 +700,7 @@ context_saver_stream_write(void *baton, context_saver_t *cs = baton; cs->data[cs->next_slot] = data; cs->len[cs->next_slot] = *len; - cs->next_slot = (cs->next_slot + 1) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; + cs->next_slot = (cs->next_slot + 1) % cs->context_size; cs->total_written++; return SVN_NO_ERROR; } @@ -733,6 +720,11 @@ typedef struct merge_output_baton_t const char *marker_eol; svn_diff_conflict_display_style_t conflict_style; + int context_size; + + /* cancel support */ + svn_cancel_func_t cancel_func; + void *cancel_baton; /* The rest of the fields are for svn_diff_conflict_display_only_conflicts only. Note that for @@ -753,9 +745,9 @@ flush_context_saver(context_saver_t *cs, svn_stream_t *output_stream) { int i; - for (i = 0; i < SVN_DIFF__UNIFIED_CONTEXT_SIZE; i++) + for (i = 0; i < cs->context_size; i++) { - apr_size_t slot = (i + cs->next_slot) % SVN_DIFF__UNIFIED_CONTEXT_SIZE; + apr_size_t slot = (i + cs->next_slot) % cs->context_size; if (cs->data[slot]) { apr_size_t len = cs->len[slot]; @@ -771,6 +763,8 @@ make_context_saver(merge_output_baton_t *mob) { context_saver_t *cs; + assert(mob->context_size > 0); /* Or nothing to save */ + svn_pool_clear(mob->pool); cs = apr_pcalloc(mob->pool, sizeof(*cs)); cs->stream = svn_stream_empty(mob->pool); @@ -778,10 +772,13 @@ make_context_saver(merge_output_baton_t *mob) svn_stream_set_write(cs->stream, context_saver_stream_write); mob->context_saver = cs; mob->output_stream = cs->stream; + cs->context_size = mob->context_size; + cs->data = apr_pcalloc(mob->pool, sizeof(*cs->data) * cs->context_size); + cs->len = apr_pcalloc(mob->pool, sizeof(*cs->len) * cs->context_size); } -/* A stream which prints SVN_DIFF__UNIFIED_CONTEXT_SIZE lines to +/* A stream which prints LINES_TO_PRINT (based on context_size) lines to BATON->REAL_OUTPUT_STREAM, and then changes BATON->OUTPUT_STREAM to a context_saver; used for *trailing* context. */ @@ -815,7 +812,7 @@ make_trailing_context_printer(merge_output_baton_t *btn) svn_pool_clear(btn->pool); tcp = apr_pcalloc(btn->pool, sizeof(*tcp)); - tcp->lines_to_print = SVN_DIFF__UNIFIED_CONTEXT_SIZE; + tcp->lines_to_print = btn->context_size; tcp->mob = btn; s = svn_stream_empty(btn->pool); svn_stream_set_baton(s, tcp); @@ -913,7 +910,8 @@ output_conflict(void *baton, if (style == svn_diff_conflict_display_resolved_modified_latest) { if (diff) - return svn_diff_output(diff, baton, &merge_output_vtable); + return svn_diff_output2(diff, baton, &merge_output_vtable, + btn->cancel_func, btn->cancel_baton); else style = svn_diff_conflict_display_modified_latest; } @@ -949,6 +947,25 @@ output_conflict(void *baton, return SVN_NO_ERROR; } +static svn_error_t * +output_conflict_with_context_marker(merge_output_baton_t *btn, + const char *label, + apr_off_t start, + apr_off_t length) +{ + if (length == 1) + SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, + "%s (%" APR_OFF_T_FMT ")", + label, start + 1)); + else + SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, + "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")", + label, start + 1, length)); + + SVN_ERR(output_marker_eol(btn)); + + return SVN_NO_ERROR; +} static svn_error_t * output_conflict_with_context(void *baton, @@ -966,7 +983,7 @@ output_conflict_with_context(void *baton, trailing context)? If so, flush it. */ if (btn->output_stream == btn->context_saver->stream) { - if (btn->context_saver->total_written > SVN_DIFF__UNIFIED_CONTEXT_SIZE) + if (btn->context_saver->total_written > btn->context_size) SVN_ERR(svn_stream_puts(btn->real_output_stream, "@@\n")); SVN_ERR(flush_context_saver(btn->context_saver, btn->real_output_stream)); } @@ -975,36 +992,24 @@ output_conflict_with_context(void *baton, btn->output_stream = btn->real_output_stream; /* Output the conflict itself. */ - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (modified_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->markers[1], - modified_start + 1, modified_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[1], + modified_start, + modified_length)); SVN_ERR(output_merge_token_range(NULL, btn, 1/*modified*/, modified_start, modified_length)); - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (original_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->markers[0], - original_start + 1, original_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[0], + original_start, + original_length)); SVN_ERR(output_merge_token_range(NULL, btn, 0/*original*/, original_start, original_length)); SVN_ERR(output_merge_marker(btn, 2/*separator*/)); SVN_ERR(output_merge_token_range(NULL, btn, 2/*latest*/, latest_start, latest_length)); - SVN_ERR(svn_stream_printf(btn->output_stream, btn->pool, - (latest_length == 1 - ? "%s (%" APR_OFF_T_FMT ")" - : "%s (%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT ")"), - btn->markers[3], - latest_start + 1, latest_length)); - SVN_ERR(output_marker_eol(btn)); + SVN_ERR(output_conflict_with_context_marker(btn, btn->markers[3], + latest_start, + latest_length)); /* Go into print-trailing-context mode instead. */ make_trailing_context_printer(btn); @@ -1049,7 +1054,7 @@ detect_eol(svn_string_t *token) } svn_error_t * -svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, +svn_diff_mem_string_output_merge3(svn_stream_t *output_stream, svn_diff_t *diff, const svn_string_t *original, const svn_string_t *modified, @@ -1059,7 +1064,9 @@ svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, const char *conflict_latest, const char *conflict_separator, svn_diff_conflict_display_style_t style, - apr_pool_t *pool) + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { merge_output_baton_t btn; const char *eol; @@ -1069,19 +1076,20 @@ svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, ? &merge_only_conflicts_output_vtable : &merge_output_vtable; memset(&btn, 0, sizeof(btn)); + btn.context_size = SVN_DIFF__UNIFIED_CONTEXT_SIZE; if (conflicts_only) { - btn.pool = svn_pool_create(pool); + btn.pool = svn_pool_create(scratch_pool); make_context_saver(&btn); btn.real_output_stream = output_stream; } else btn.output_stream = output_stream; - fill_source_tokens(&(btn.sources[0]), original, pool); - fill_source_tokens(&(btn.sources[1]), modified, pool); - fill_source_tokens(&(btn.sources[2]), latest, pool); + fill_source_tokens(&(btn.sources[0]), original, scratch_pool); + fill_source_tokens(&(btn.sources[1]), modified, scratch_pool); + fill_source_tokens(&(btn.sources[2]), latest, scratch_pool); btn.conflict_style = style; @@ -1095,67 +1103,33 @@ svn_diff_mem_string_output_merge2(svn_stream_t *output_stream, eol = APR_EOL_STR; /* use the platform default */ btn.marker_eol = eol; + btn.cancel_func = cancel_func; + btn.cancel_baton = cancel_baton; SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[1], conflict_modified ? conflict_modified : "<<<<<<< (modified)", - pool)); + scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[0], conflict_original ? conflict_original : "||||||| (original)", - pool)); + scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[2], conflict_separator ? conflict_separator : "=======", - pool)); + scratch_pool)); SVN_ERR(svn_utf_cstring_from_utf8(&btn.markers[3], conflict_latest ? conflict_latest : ">>>>>>> (latest)", - pool)); + scratch_pool)); - SVN_ERR(svn_diff_output(diff, &btn, vtable)); + SVN_ERR(svn_diff_output2(diff, &btn, vtable, cancel_func, cancel_baton)); if (conflicts_only) svn_pool_destroy(btn.pool); return SVN_NO_ERROR; } - -svn_error_t * -svn_diff_mem_string_output_merge(svn_stream_t *output_stream, - svn_diff_t *diff, - const svn_string_t *original, - const svn_string_t *modified, - const svn_string_t *latest, - const char *conflict_original, - const char *conflict_modified, - const char *conflict_latest, - const char *conflict_separator, - svn_boolean_t display_original_in_conflict, - svn_boolean_t display_resolved_conflicts, - apr_pool_t *pool) -{ - svn_diff_conflict_display_style_t style = - svn_diff_conflict_display_modified_latest; - - if (display_resolved_conflicts) - style = svn_diff_conflict_display_resolved_modified_latest; - - if (display_original_in_conflict) - style = svn_diff_conflict_display_modified_original_latest; - - return svn_diff_mem_string_output_merge2(output_stream, - diff, - original, - modified, - latest, - conflict_original, - conflict_modified, - conflict_latest, - conflict_separator, - style, - pool); -} diff --git a/contrib/subversion/subversion/libsvn_diff/lcs.c b/contrib/subversion/subversion/libsvn_diff/lcs.c index 8087a92f5..b420187d4 100644 --- a/contrib/subversion/subversion/libsvn_diff/lcs.c +++ b/contrib/subversion/subversion/libsvn_diff/lcs.c @@ -345,7 +345,7 @@ svn_diff__lcs(svn_diff__position_t *position_list1, /* pointer to tail (ring) */ { svn_diff__snake(fp + k, token_counts, &lcs_freelist, pool); } - /* for k > 0, deletions are free */ + /* for k > 0, deletions are free */ for (k = (d > 0 ? d : 0) + p; k >= 0; k--) { svn_diff__snake(fp + k, token_counts, &lcs_freelist, pool); diff --git a/contrib/subversion/subversion/libsvn_diff/libsvn_diff.pc.in b/contrib/subversion/subversion/libsvn_diff/libsvn_diff.pc.in new file mode 100644 index 000000000..eed9d886a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_diff/libsvn_diff.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_diff +Description: Subversion Diff Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_subr +Libs: -L${libdir} -lsvn_diff @SVN_ZLIB_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_diff/parse-diff.c b/contrib/subversion/subversion/libsvn_diff/parse-diff.c index e269ef925..3f794b8b1 100644 --- a/contrib/subversion/subversion/libsvn_diff/parse-diff.c +++ b/contrib/subversion/subversion/libsvn_diff/parse-diff.c @@ -35,9 +35,12 @@ #include "svn_utf.h" #include "svn_dirent_uri.h" #include "svn_diff.h" +#include "svn_ctype.h" +#include "svn_mergeinfo.h" #include "private/svn_eol_private.h" #include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" /* Helper macro for readability */ #define starts_with(str, start) \ @@ -385,7 +388,6 @@ svn_diff_hunk_readline_diff_text(svn_diff_hunk_t *hunk, 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; @@ -415,33 +417,10 @@ svn_diff_hunk_readline_diff_text(svn_diff_hunk_t *hunk, 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] = '+'; - } + if (line->data[0] == '+') + line->data[0] = '-'; + else if (line->data[0] == '-') + line->data[0] = '+'; } *stringbuf = line; @@ -471,6 +450,147 @@ parse_prop_name(const char **prop_name, const char *header, return SVN_NO_ERROR; } + +/* A helper function to parse svn:mergeinfo diffs. + * + * These diffs use a special pretty-print format, for instance: + * + * Added: svn:mergeinfo + * ## -0,0 +0,1 ## + * Merged /trunk:r2-3 + * + * The hunk header has the following format: + * ## -0,NUMBER_OF_REVERSE_MERGES +0,NUMBER_OF_FORWARD_MERGES ## + * + * At this point, the number of reverse merges has already been + * parsed into HUNK->ORIGINAL_LENGTH, and the number of forward + * merges has been parsed into HUNK->MODIFIED_LENGTH. + * + * The header is followed by a list of mergeinfo, one path per line. + * This function parses such lines. Lines describing reverse merges + * appear first, and then all lines describing forward merges appear. + * + * Parts of the line are affected by i18n. The words 'Merged' + * and 'Reverse-merged' can appear in any language and at any + * position within the line. We can only assume that a leading + * '/' starts the merge source path, the path is followed by + * ":r", which in turn is followed by a mergeinfo revision range, + * which is terminated by whitespace or end-of-string. + * + * If the current line meets the above criteria and we're able + * to parse valid mergeinfo from it, the resulting mergeinfo + * is added to patch->mergeinfo or patch->reverse_mergeinfo, + * and we proceed to the next line. + */ +static svn_error_t * +parse_mergeinfo(svn_boolean_t *found_mergeinfo, + svn_stringbuf_t *line, + svn_diff_hunk_t *hunk, + svn_patch_t *patch, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char *slash = strchr(line->data, '/'); + char *colon = strrchr(line->data, ':'); + + *found_mergeinfo = FALSE; + + if (slash && colon && colon[1] == 'r' && slash < colon) + { + svn_stringbuf_t *input; + svn_mergeinfo_t mergeinfo = NULL; + char *s; + svn_error_t *err; + + input = svn_stringbuf_create_ensure(line->len, scratch_pool); + + /* Copy the merge source path + colon */ + s = slash; + while (s <= colon) + { + svn_stringbuf_appendbyte(input, *s); + s++; + } + + /* skip 'r' after colon */ + s++; + + /* Copy the revision range. */ + while (s < line->data + line->len) + { + if (svn_ctype_isspace(*s)) + break; + svn_stringbuf_appendbyte(input, *s); + s++; + } + + err = svn_mergeinfo_parse(&mergeinfo, input->data, result_pool); + if (err && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) + { + svn_error_clear(err); + mergeinfo = NULL; + } + else + SVN_ERR(err); + + if (mergeinfo) + { + if (hunk->original_length > 0) /* reverse merges */ + { + if (patch->reverse) + { + if (patch->mergeinfo == NULL) + patch->mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + else + { + if (patch->reverse_mergeinfo == NULL) + patch->reverse_mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->reverse_mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + hunk->original_length--; + } + else if (hunk->modified_length > 0) /* forward merges */ + { + if (patch->reverse) + { + if (patch->reverse_mergeinfo == NULL) + patch->reverse_mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->reverse_mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + else + { + if (patch->mergeinfo == NULL) + patch->mergeinfo = mergeinfo; + else + SVN_ERR(svn_mergeinfo_merge2(patch->mergeinfo, + mergeinfo, + result_pool, + scratch_pool)); + } + hunk->modified_length--; + } + + *found_mergeinfo = TRUE; + } + } + + 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 @@ -600,6 +720,17 @@ parse_next_hunk(svn_diff_hunk_t **hunk, continue; } + if (in_hunk && *is_property && *prop_name && + strcmp(*prop_name, SVN_PROP_MERGEINFO) == 0) + { + svn_boolean_t found_mergeinfo; + + SVN_ERR(parse_mergeinfo(&found_mergeinfo, line, *hunk, patch, + result_pool, iterpool)); + if (found_mergeinfo) + continue; /* Proceed to the next line in the patch. */ + } + if (in_hunk) { char c; @@ -1192,6 +1323,13 @@ parse_hunks(svn_patch_t *patch, apr_file_t *apr_file, prop_name = last_prop_name; else last_prop_name = prop_name; + + /* Skip svn:mergeinfo properties. + * Mergeinfo data cannot be represented as a hunk and + * is therefore stored in PATCH itself. */ + if (strcmp(prop_name, SVN_PROP_MERGEINFO) == 0) + continue; + SVN_ERR(add_property_hunk(patch, prop_name, hunk, prop_operation, result_pool)); } @@ -1229,7 +1367,7 @@ static struct transition transitions[] = }; svn_error_t * -svn_diff_parse_next_patch(svn_patch_t **patch, +svn_diff_parse_next_patch(svn_patch_t **patch_p, svn_patch_file_t *patch_file, svn_boolean_t reverse, svn_boolean_t ignore_whitespace, @@ -1240,16 +1378,17 @@ svn_diff_parse_next_patch(svn_patch_t **patch, svn_boolean_t eof; svn_boolean_t line_after_tree_header_read = FALSE; apr_pool_t *iterpool; + svn_patch_t *patch; enum parse_state state = state_start; if (apr_file_eof(patch_file->apr_file) == APR_EOF) { /* No more patches here. */ - *patch = NULL; + *patch_p = NULL; return SVN_NO_ERROR; } - *patch = apr_pcalloc(result_pool, sizeof(**patch)); + 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)); @@ -1282,7 +1421,7 @@ svn_diff_parse_next_patch(svn_patch_t **patch, if (starts_with(line->data, transitions[i].expected_input) && state == transitions[i].required_state) { - SVN_ERR(transitions[i].fn(&state, line->data, *patch, + SVN_ERR(transitions[i].fn(&state, line->data, patch, result_pool, iterpool)); valid_header_line = TRUE; break; @@ -1328,22 +1467,22 @@ svn_diff_parse_next_patch(svn_patch_t **patch, } while (! eof); - (*patch)->reverse = reverse; + patch->reverse = reverse; if (reverse) { const char *temp; - temp = (*patch)->old_filename; - (*patch)->old_filename = (*patch)->new_filename; - (*patch)->new_filename = temp; + temp = patch->old_filename; + patch->old_filename = patch->new_filename; + patch->new_filename = temp; } - if ((*patch)->old_filename == NULL || (*patch)->new_filename == NULL) + if (patch->old_filename == NULL || patch->new_filename == NULL) { /* Something went wrong, just discard the result. */ - *patch = NULL; + patch = NULL; } else - SVN_ERR(parse_hunks(*patch, patch_file->apr_file, ignore_whitespace, + SVN_ERR(parse_hunks(patch, patch_file->apr_file, ignore_whitespace, result_pool, iterpool)); svn_pool_destroy(iterpool); @@ -1352,16 +1491,16 @@ svn_diff_parse_next_patch(svn_patch_t **patch, SVN_ERR(svn_io_file_seek(patch_file->apr_file, APR_CUR, &patch_file->next_patch_offset, scratch_pool)); - if (*patch) + 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); + svn_sort__array(patch->hunks, compare_hunks); } + *patch_p = patch; return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_diff/util.c b/contrib/subversion/subversion/libsvn_diff/util.c index 412b20be1..a931822ca 100644 --- a/contrib/subversion/subversion/libsvn_diff/util.c +++ b/contrib/subversion/subversion/libsvn_diff/util.c @@ -34,11 +34,11 @@ #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 "private/svn_sorts_private.h" #include "diff.h" #include "svn_private_config.h" @@ -77,9 +77,11 @@ svn_diff_contains_diffs(svn_diff_t *diff) } svn_error_t * -svn_diff_output(svn_diff_t *diff, - void *output_baton, - const svn_diff_output_fns_t *vtable) +svn_diff_output2(svn_diff_t *diff, + void *output_baton, + const svn_diff_output_fns_t *vtable, + svn_cancel_func_t cancel_func, + void *cancel_baton) { svn_error_t *(*output_fn)(void *, apr_off_t, apr_off_t, @@ -88,6 +90,9 @@ svn_diff_output(svn_diff_t *diff, while (diff != NULL) { + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + switch (diff->type) { case svn_diff__type_common: @@ -449,11 +454,19 @@ display_mergeinfo_diff(const char *old_mergeinfo_val, new_mergeinfo_hash, TRUE, pool, pool)); + /* Print a hint for 'svn patch' or smilar tools, indicating the + * number of reverse-merges and forward-merges. */ + SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool, + "## -0,%u +0,%u ##%s", + apr_hash_count(deleted), + apr_hash_count(added), + APR_EOL_STR)); + 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); + const char *from_path = apr_hash_this_key(hi); + svn_rangelist_t *merge_revarray = apr_hash_this_val(hi); svn_string_t *merge_revstr; svn_pool_clear(iterpool); @@ -469,8 +482,8 @@ display_mergeinfo_diff(const char *old_mergeinfo_val, 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); + const char *from_path = apr_hash_this_key(hi); + svn_rangelist_t *merge_revarray = apr_hash_this_val(hi); svn_string_t *merge_revstr; svn_pool_clear(iterpool); @@ -487,7 +500,7 @@ display_mergeinfo_diff(const char *old_mergeinfo_val, return SVN_NO_ERROR; } -/* qsort callback handling svn_prop_t by name */ +/* svn_sort__array callback handling svn_prop_t by name */ static int propchange_sort(const void *k1, const void *k2) { @@ -503,6 +516,9 @@ svn_diff__display_prop_diffs(svn_stream_t *outstream, const apr_array_header_t *propchanges, apr_hash_t *original_props, svn_boolean_t pretty_print_mergeinfo, + int context_size, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { apr_pool_t *pool = scratch_pool; @@ -510,7 +526,7 @@ svn_diff__display_prop_diffs(svn_stream_t *outstream, apr_array_header_t *changes = apr_array_copy(scratch_pool, propchanges); int i; - qsort(changes->elts, changes->nelts, changes->elt_size, propchange_sort); + svn_sort__array(changes, propchange_sort); for (i = 0; i < changes->nelts; i++) { @@ -586,10 +602,11 @@ svn_diff__display_prop_diffs(svn_stream_t *outstream, * 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( + * We also suppress the diff header. */ + SVN_ERR(svn_diff_mem_string_output_unified3( outstream, diff, FALSE /* no header */, "##", NULL, NULL, - encoding, orig, val, iterpool)); + encoding, orig, val, context_size, + cancel_func, cancel_baton, iterpool)); } } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/libsvn_fs/access.c b/contrib/subversion/subversion/libsvn_fs/access.c index 9918be4c4..2b1901f71 100644 --- a/contrib/subversion/subversion/libsvn_fs/access.c +++ b/contrib/subversion/subversion/libsvn_fs/access.c @@ -91,13 +91,6 @@ svn_fs_access_add_lock_token2(svn_fs_access_t *access_ctx, return SVN_NO_ERROR; } -svn_error_t * -svn_fs_access_add_lock_token(svn_fs_access_t *access_ctx, - const char *token) -{ - return svn_fs_access_add_lock_token2(access_ctx, (const char *) 1, token); -} - apr_hash_t * svn_fs__access_get_lock_tokens(svn_fs_access_t *access_ctx) { diff --git a/contrib/subversion/subversion/libsvn_fs/deprecated.c b/contrib/subversion/subversion/libsvn_fs/deprecated.c new file mode 100644 index 000000000..03045773f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs/deprecated.c @@ -0,0 +1,90 @@ +/* + * deprecated.c: holding file for all deprecated APIs. + * "we can't lose 'em, but we can shun 'em!" + * + * ==================================================================== + * 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. + * ==================================================================== + */ + +/* We define this here to remove any further warnings about the usage of + deprecated functions in this file. */ +#define SVN_DEPRECATED + +#include "svn_fs.h" + + +/*** From fs-loader.c ***/ +svn_error_t * +svn_fs_upgrade(const char *path, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_upgrade2(path, NULL, NULL, NULL, NULL, pool)); +} + +svn_error_t * +svn_fs_hotcopy2(const char *src_path, const char *dest_path, + svn_boolean_t clean, svn_boolean_t incremental, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean, + incremental, NULL, NULL, + cancel_func, cancel_baton, + scratch_pool)); +} + +svn_error_t * +svn_fs_hotcopy(const char *src_path, const char *dest_path, + svn_boolean_t clean, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean, + FALSE, NULL, NULL, pool)); +} + +svn_error_t * +svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, + apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool)); +} + +svn_error_t * +svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name, + const svn_string_t *value, apr_pool_t *pool) +{ + return svn_error_trace( + svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool)); +} + +svn_error_t * +svn_fs_get_locks(svn_fs_t *fs, const char *path, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity, + get_locks_func, get_locks_baton, + pool)); +} + +/*** From access.c ***/ +svn_error_t * +svn_fs_access_add_lock_token(svn_fs_access_t *access_ctx, + const char *token) +{ + return svn_fs_access_add_lock_token2(access_ctx, (const char *) 1, token); +} diff --git a/contrib/subversion/subversion/libsvn_fs/editor.c b/contrib/subversion/subversion/libsvn_fs/editor.c index a75f21043..b4cd3bca3 100644 --- a/contrib/subversion/subversion/libsvn_fs/editor.c +++ b/contrib/subversion/subversion/libsvn_fs/editor.c @@ -62,9 +62,7 @@ struct edit_baton { svn_fs_root_t *root; }; -#define FSPATH(relpath, pool) apr_pstrcat(pool, "/", relpath, NULL) -#define UNUSED(x) ((void)(x)) - +#define FSPATH(relpath, pool) apr_pstrcat(pool, "/", relpath, SVN_VA_NULL) static svn_error_t * get_root(svn_fs_root_t **root, @@ -94,8 +92,8 @@ add_new_props(svn_fs_root_t *root, for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_string_t *value = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_string_t *value = apr_hash_this_val(hi); svn_pool_clear(iterpool); @@ -177,7 +175,7 @@ can_modify(svn_fs_root_t *txn_root, SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath, scratch_pool)); - /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination) + /* Uncommitted nodes (eg. a descendant of a copy/move destination) have no (committed) revision number. Let the caller go ahead and modify these nodes. @@ -195,7 +193,7 @@ can_modify(svn_fs_root_t *txn_root, have supplied a valid revision number [that they expect to change]. The checks further below will determine the out-of-dateness of the specified revision. */ - /* ### ugh. descendents of copy/move/rotate destinations carry along + /* ### ugh. descendants of copy/move destinations carry along ### their original immutable state and (thus) a valid CREATED_REV. ### but they are logically uncommitted, so the caller will pass ### SVN_INVALID_REVNUM. (technically, the caller could provide @@ -203,9 +201,9 @@ can_modify(svn_fs_root_t *txn_root, ### API). ### ### for now, we will assume the caller knows what they are doing - ### and an invalid revision implies such a descendent. in the + ### and an invalid revision implies such a descendant. in the ### future, we could examine the ancestor chain looking for a - ### copy/move/rotate-here node and allow the modification (and the + ### copy/move-here node and allow the modification (and the ### converse: if no such ancestor, the caller must specify the ### correct/intended revision to modify). */ @@ -240,23 +238,18 @@ can_modify(svn_fs_root_t *txn_root, of those new revisions. In either case, the node may not have changed in those new revisions; use the node's ID to determine this case. */ - const svn_fs_id_t *txn_noderev_id; svn_fs_root_t *rev_root; - const svn_fs_id_t *new_noderev_id; - - /* The ID of the node that we would be modifying in the txn */ - SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath, - scratch_pool)); + svn_fs_node_relation_t relation; /* Get the ID from the future/new revision. */ SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root), revision, scratch_pool)); - SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath, - scratch_pool)); + SVN_ERR(svn_fs_node_relation(&relation, txn_root, fspath, rev_root, + fspath, scratch_pool)); svn_fs_close_root(rev_root); /* Has the target node changed in the future? */ - if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0) + if (relation != svn_fs_node_unchanged) { /* Restarting the commit will base the txn on the future/new revision, allowing the modification at REVISION. */ @@ -299,7 +292,7 @@ can_create(svn_fs_root_t *txn_root, ### test the ancestor to determine if it has been *-here in this ### txn, or just a simple modification. */ - /* Are any of the parents copied/moved/rotated-here? */ + /* Are any of the parents copied/moved-here? */ for (cur_fspath = fspath; strlen(cur_fspath) > 1; /* not the root */ cur_fspath = svn_fspath__dirname(cur_fspath, scratch_pool)) @@ -488,9 +481,9 @@ 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_hash_t *props, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; @@ -521,13 +514,14 @@ static svn_error_t * alter_symlink_cb(void *baton, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const char *target, + apr_hash_t *props, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; - UNUSED(eb); SVN__NOT_IMPLEMENTED(); + SVN_UNUSED(eb); + SVN__NOT_IMPLEMENTED(); } @@ -633,19 +627,6 @@ move_cb(void *baton, } -/* 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 edit_baton *eb = baton; - - UNUSED(eb); SVN__NOT_IMPLEMENTED(); -} - - /* This implements svn_editor_cb_complete_t */ static svn_error_t * complete_cb(void *baton, @@ -714,7 +695,6 @@ make_editor(svn_editor_t **editor, delete_cb, copy_cb, move_cb, - rotate_cb, complete_cb, abort_cb }; @@ -804,9 +784,9 @@ svn_fs__editor_commit(svn_revnum_t *revision, if (!err) err = svn_fs_commit_txn(&inner_conflict_path, - revision, - eb->txn, - scratch_pool); + revision, + eb->txn, + scratch_pool); if (SVN_IS_VALID_REVNUM(*revision)) { if (err) @@ -829,7 +809,7 @@ svn_fs__editor_commit(svn_revnum_t *revision, /* Copy this into the correct pool (see note above). */ *conflict_path = apr_pstrdup(result_pool, inner_conflict_path); - /* Return sucess. The caller should inspect CONFLICT_PATH to + /* Return success. The caller should inspect CONFLICT_PATH to determine this particular case. */ svn_error_clear(err); err = SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_fs/fs-loader.c b/contrib/subversion/subversion/libsvn_fs/fs-loader.c index 336b84f7b..ba90554fa 100644 --- a/contrib/subversion/subversion/libsvn_fs/fs-loader.c +++ b/contrib/subversion/subversion/libsvn_fs/fs-loader.c @@ -24,12 +24,14 @@ #include #include +#include #include #include #include #include #include +#include "svn_private_config.h" #include "svn_hash.h" #include "svn_ctype.h" #include "svn_types.h" @@ -40,8 +42,9 @@ #include "svn_xml.h" #include "svn_pools.h" #include "svn_string.h" -#include "svn_private_config.h" +#include "svn_sorts.h" +#include "private/svn_atomic.h" #include "private/svn_fs_private.h" #include "private/svn_fs_util.h" #include "private/svn_utf_private.h" @@ -60,8 +63,9 @@ /* A pool common to all FS objects. See the documentation on the open/create functions in fs-loader.h and for svn_fs_initialize(). */ -static apr_pool_t *common_pool; -svn_mutex__t *common_pool_lock; +static apr_pool_t *common_pool = NULL; +static svn_mutex__t *common_pool_lock = NULL; +static svn_atomic_t common_pool_initialized = FALSE; /* --- Utility functions for the loader --- */ @@ -70,6 +74,7 @@ struct fs_type_defn { const char *fs_type; const char *fsap_name; fs_init_func_t initfunc; + fs_library_vtable_t *vtable; struct fs_type_defn *next; }; @@ -81,10 +86,23 @@ static struct fs_type_defn base_defn = #else NULL, #endif + NULL, NULL /* End of static list: this needs to be reset to NULL if the common_pool used when setting it has been cleared. */ }; +static struct fs_type_defn fsx_defn = + { + SVN_FS_TYPE_FSX, "x", +#ifdef SVN_LIBSVN_FS_LINKS_FS_X + svn_fs_x__init, +#else + NULL, +#endif + NULL, + &base_defn + }; + static struct fs_type_defn fsfs_defn = { SVN_FS_TYPE_FSFS, "fs", @@ -93,7 +111,8 @@ static struct fs_type_defn fsfs_defn = #else NULL, #endif - &base_defn + NULL, + &fsx_defn }; static struct fs_type_defn *fs_modules = &fsfs_defn; @@ -148,13 +167,20 @@ load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) /* Fetch a library vtable by a pointer into the library definitions array. */ static svn_error_t * get_library_vtable_direct(fs_library_vtable_t **vtable, - const struct fs_type_defn *fst, + struct fs_type_defn *fst, apr_pool_t *pool) { fs_init_func_t initfunc = NULL; const svn_version_t *my_version = svn_fs_version(); const svn_version_t *fs_version; + /* most times, we get lucky */ + *vtable = apr_atomic_casptr((volatile void **)&fst->vtable, NULL, NULL); + if (*vtable) + return SVN_NO_ERROR; + + /* o.k. the first access needs to actually load the module, find the + vtable and check for version compatibility. */ initfunc = fst->initfunc; if (! initfunc) SVN_ERR(load_module(&initfunc, fst->fsap_name, pool)); @@ -172,8 +198,7 @@ get_library_vtable_direct(fs_library_vtable_t **vtable, unloaded. This function makes a best effort by creating the common pool as a child of the global pool; the window of failure due to thread collision is small. */ - if (!common_pool) - SVN_ERR(svn_fs_initialize(NULL)); + SVN_ERR(svn_fs_initialize(NULL)); /* Invoke the FS module's initfunc function with the common pool protected by a lock. */ @@ -191,6 +216,10 @@ get_library_vtable_direct(fs_library_vtable_t **vtable, my_version->patch, my_version->tag, fs_version->major, fs_version->minor, fs_version->patch, fs_version->tag); + + /* the vtable will not change. Remember it */ + apr_atomic_casptr((volatile void **)&fst->vtable, *vtable, NULL); + return SVN_NO_ERROR; } @@ -211,31 +240,37 @@ get_or_allocate_third(struct fs_type_defn **fst, (*fst)->fs_type = apr_pstrdup(common_pool, fs_type); (*fst)->fsap_name = (*fst)->fs_type; (*fst)->initfunc = NULL; + (*fst)->vtable = NULL; (*fst)->next = NULL; return SVN_NO_ERROR; } #endif -/* Fetch a library vtable by FS type. */ +/* Fetch a library *VTABLE by FS_TYPE. + Use POOL for temporary allocations. */ static svn_error_t * get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type, apr_pool_t *pool) { - struct fs_type_defn **fst = &fs_modules; + struct fs_type_defn **fst; svn_boolean_t known = FALSE; - /* There are two FS module definitions known at compile time. We + /* There are three FS module definitions known at compile time. We want to check these without any locking overhead even when dynamic third party modules are enabled. The third party modules cannot be checked until the lock is held. */ - if (strcmp(fs_type, (*fst)->fs_type) == 0) - known = TRUE; - else + for (fst = &fs_modules; *fst; fst = &(*fst)->next) { - fst = &(*fst)->next; if (strcmp(fs_type, (*fst)->fs_type) == 0) - known = TRUE; + { + known = TRUE; + break; + } + else if (!(*fst)->next) + { + break; + } } #if defined(SVN_USE_DSO) && APR_HAS_DSO @@ -254,8 +289,8 @@ get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type, if (!known) { fst = &(*fst)->next; - if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */ - SVN_ERR(svn_fs_initialize(NULL)); + /* Best-effort init, see get_library_vtable_direct. */ + SVN_ERR(svn_fs_initialize(NULL)); SVN_MUTEX__WITH_LOCK(common_pool_lock, get_or_allocate_third(fst, fs_type)); known = TRUE; @@ -320,7 +355,9 @@ fs_library_vtable(fs_library_vtable_t **vtable, const char *path, SVN_ERR(svn_fs_type(&fs_type, path, pool)); /* Fetch the library vtable by name, now that we've chosen one. */ - return svn_error_trace(get_library_vtable(vtable, fs_type, pool)); + SVN_ERR(get_library_vtable(vtable, fs_type, pool)); + + return SVN_NO_ERROR; } static svn_error_t * @@ -345,16 +382,15 @@ write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool) static apr_status_t uninit(void *data) { common_pool = NULL; + common_pool_lock = NULL; + common_pool_initialized = 0; + return APR_SUCCESS; } -svn_error_t * -svn_fs_initialize(apr_pool_t *pool) +static svn_error_t * +synchronized_initialize(void *baton, apr_pool_t *pool) { - /* Protect against multiple calls. */ - if (common_pool) - return SVN_NO_ERROR; - common_pool = svn_pool_create(pool); base_defn.next = NULL; SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool)); @@ -369,19 +405,44 @@ svn_fs_initialize(apr_pool_t *pool) return SVN_NO_ERROR; } +svn_error_t * +svn_fs_initialize(apr_pool_t *pool) +{ +#if defined(SVN_USE_DSO) && APR_HAS_DSO + /* Ensure that DSO subsystem is initialized early as possible if + we're going to use it. */ + SVN_ERR(svn_dso_initialize2()); +#endif + /* Protect against multiple calls. */ + return svn_error_trace(svn_atomic__init_once(&common_pool_initialized, + synchronized_initialize, + NULL, pool)); +} + /* A default warning handling function. */ static void default_warning_func(void *baton, svn_error_t *err) { /* The one unforgiveable sin is to fail silently. Dumping to stderr or /dev/tty is not acceptable default behavior for server - processes, since those may both be equivalent to /dev/null. */ + processes, since those may both be equivalent to /dev/null. + + That said, be a good citizen and print something anyway, in case it goes + somewhere, and our caller hasn't overridden the abort() call. + */ + if (svn_error_get_malfunction_handler() + == svn_error_abort_on_malfunction) + /* ### TODO: extend the malfunction API such that non-abort()ing consumers + ### also get the information on ERR. */ + svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: "); SVN_ERR_MALFUNCTION_NO_RETURN(); } svn_error_t * svn_fs__path_valid(const char *path, apr_pool_t *pool) { + char *c; + /* UTF-8 encoded string without NULs. */ if (! svn_utf__cstring_is_valid(path)) { @@ -398,6 +459,18 @@ svn_fs__path_valid(const char *path, apr_pool_t *pool) path); } + /* Raise an error if PATH contains a newline because svn:mergeinfo and + friends can't handle them. Issue #4340 describes a similar problem + in the FSFS code itself. + */ + 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)); + } + /* That's good enough. */ return SVN_NO_ERROR; } @@ -451,42 +524,66 @@ svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, /* Perform the actual creation. */ *fs_p = fs_new(fs_config, pool); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->create(*fs_p, path, pool, common_pool)); - SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open)); + SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2)); return SVN_NO_ERROR; } svn_error_t * -svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, - apr_pool_t *pool) +svn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { fs_library_vtable_t *vtable; - SVN_ERR(fs_library_vtable(&vtable, path, pool)); - *fs_p = fs_new(fs_config, pool); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->open_fs(*fs_p, path, pool, common_pool)); - SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open)); + SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool)); + *fs_p = fs_new(fs_config, result_pool); + SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool, + common_pool)); + SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2)); return SVN_NO_ERROR; } svn_error_t * -svn_fs_upgrade(const char *path, apr_pool_t *pool) +svn_fs_open(svn_fs_t **fs_p, + const char *path, + apr_hash_t *fs_config, + apr_pool_t *pool) +{ + return svn_fs_open2(fs_p, path, fs_config, pool, pool); +} + +svn_error_t * +svn_fs_upgrade2(const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { fs_library_vtable_t *vtable; svn_fs_t *fs; - SVN_ERR(fs_library_vtable(&vtable, path, pool)); - fs = fs_new(NULL, pool); + SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool)); + fs = fs_new(NULL, scratch_pool); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->upgrade_fs(fs, path, pool, common_pool)); + SVN_ERR(vtable->upgrade_fs(fs, path, + notify_func, notify_baton, + cancel_func, cancel_baton, + common_pool_lock, + scratch_pool, common_pool)); return SVN_NO_ERROR; } +/* A warning handling function that does not abort on errors, + but just lets them be returned normally. */ +static void +verify_fs_warning_func(void *baton, svn_error_t *err) +{ +} + svn_error_t * svn_fs_verify(const char *path, apr_hash_t *fs_config, @@ -503,12 +600,13 @@ svn_fs_verify(const char *path, SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(fs_config, pool); + svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->verify_fs(fs, path, start, end, - notify_func, notify_baton, - cancel_func, cancel_baton, - pool, common_pool)); + SVN_ERR(vtable->verify_fs(fs, path, start, end, + notify_func, notify_baton, + cancel_func, cancel_baton, + common_pool_lock, + pool, common_pool)); return SVN_NO_ERROR; } @@ -537,9 +635,12 @@ svn_fs_delete_fs(const char *path, apr_pool_t *pool) } svn_error_t * -svn_fs_hotcopy2(const char *src_path, const char *dst_path, +svn_fs_hotcopy3(const char *src_path, const char *dst_path, svn_boolean_t clean, svn_boolean_t incremental, - svn_cancel_func_t cancel_func, void *cancel_baton, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { fs_library_vtable_t *vtable; @@ -592,19 +693,12 @@ svn_fs_hotcopy2(const char *src_path, const char *dst_path, } SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean, - incremental, cancel_func, cancel_baton, - scratch_pool)); + incremental, notify_func, notify_baton, + cancel_func, cancel_baton, common_pool_lock, + scratch_pool, common_pool)); return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool)); } -svn_error_t * -svn_fs_hotcopy(const char *src_path, const char *dest_path, - svn_boolean_t clean, apr_pool_t *pool) -{ - return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean, - FALSE, NULL, NULL, pool)); -} - svn_error_t * svn_fs_pack(const char *path, svn_fs_pack_notify_t notify_func, @@ -619,10 +713,9 @@ svn_fs_pack(const char *path, SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(NULL, pool); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->pack_fs(fs, path, notify_func, notify_baton, - cancel_func, cancel_baton, pool, - common_pool)); + SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton, + cancel_func, cancel_baton, common_pool_lock, + pool, common_pool)); return SVN_NO_ERROR; } @@ -637,9 +730,8 @@ svn_fs_recover(const char *path, SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(NULL, pool); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->open_fs_for_recovery(fs, path, pool, - common_pool)); + SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock, + pool, common_pool)); return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton, pool)); } @@ -680,9 +772,8 @@ svn_fs_create_berkeley(svn_fs_t *fs, const char *path) SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool)); /* Perform the actual creation. */ - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->create(fs, path, fs->pool, common_pool)); - SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open)); + SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2)); return SVN_NO_ERROR; } @@ -693,9 +784,8 @@ svn_fs_open_berkeley(svn_fs_t *fs, const char *path) fs_library_vtable_t *vtable; SVN_ERR(fs_library_vtable(&vtable, path, fs->pool)); - SVN_MUTEX__WITH_LOCK(common_pool_lock, - vtable->open_fs(fs, path, fs->pool, common_pool)); - SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open)); + SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2)); return SVN_NO_ERROR; } @@ -716,8 +806,9 @@ svn_error_t * svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path, svn_boolean_t clean_logs, apr_pool_t *pool) { - return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs, - FALSE, NULL, NULL, pool)); + return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs, + FALSE, NULL, NULL, NULL, NULL, + pool)); } svn_error_t * @@ -757,17 +848,9 @@ svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, } -svn_error_t * -svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, - apr_pool_t *pool) -{ - return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool)); -} - - svn_error_t * svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev, - svn_fs_txn_t *txn, apr_pool_t *pool) + svn_fs_txn_t *txn, apr_pool_t *pool) { svn_error_t *err; @@ -791,24 +874,9 @@ svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev, SVN_ERR(err); -#ifdef PACK_AFTER_EVERY_COMMIT - { - svn_fs_t *fs = txn->fs; - const char *fs_path = svn_fs_path(fs, pool); - err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool); - if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) - /* Pre-1.6 filesystem. */ - svn_error_clear(err); - else if (err) - /* Real error. */ - return svn_error_trace(err); - } -#endif - return SVN_NO_ERROR; } - svn_error_t * svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool) { @@ -848,23 +916,49 @@ svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs, return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool)); } +static svn_boolean_t +is_internal_txn_prop(const char *name) +{ + return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 || + strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 || + strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0; +} + svn_error_t * svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn, const char *propname, apr_pool_t *pool) { + if (is_internal_txn_prop(propname)) + { + *value_p = NULL; + return SVN_NO_ERROR; + } + return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool)); } svn_error_t * svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool) { - return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool)); + SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool)); + + /* Don't give away internal transaction properties. */ + svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL); + svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL); + svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL); + + return SVN_NO_ERROR; } svn_error_t * svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name, const svn_string_t *value, apr_pool_t *pool) { + if (is_internal_txn_prop(name)) + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Attempt to modify internal transaction " + "property '%s'"), name); + return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool)); } @@ -872,6 +966,18 @@ svn_error_t * svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props, apr_pool_t *pool) { + int i; + + for (i = 0; i < props->nelts; ++i) + { + svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); + + if (is_internal_txn_prop(prop->name)) + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Attempt to modify internal transaction " + "property '%s'"), prop->name); + } + return svn_error_trace(txn->vtable->change_props(txn, props, pool)); } @@ -940,7 +1046,8 @@ svn_fs_revision_root_revision(svn_fs_root_t *root) } svn_error_t * -svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root, +svn_fs_paths_changed2(apr_hash_t **changed_paths_p, + svn_fs_root_t *root, apr_pool_t *pool) { return root->vtable->paths_changed(changed_paths_p, root, pool); @@ -983,12 +1090,22 @@ svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root, return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool)); } +svn_error_t * +svn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root, + const char *path, apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(root->vtable->node_history(history_p, root, path, + result_pool, + scratch_pool)); +} + svn_error_t * svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, const char *path, apr_pool_t *pool) { return svn_error_trace(root->vtable->node_history(history_p, root, path, - pool)); + pool, pool)); } svn_error_t * @@ -1020,6 +1137,25 @@ svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root, return svn_error_trace(root->vtable->node_id(id_p, root, path, pool)); } +svn_error_t * +svn_fs_node_relation(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, const char *path_a, + svn_fs_root_t *root_b, const char *path_b, + apr_pool_t *scratch_pool) +{ + /* Different repository types? */ + if (root_a->fs != root_b->fs) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + return svn_error_trace(root_a->vtable->node_relation(relation, + root_a, path_a, + root_b, path_b, + scratch_pool)); +} + svn_error_t * svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root, const char *path, apr_pool_t *pool) @@ -1060,6 +1196,16 @@ svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root, pool)); } +svn_error_t * +svn_fs_node_has_props(svn_boolean_t *has_props, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(root->vtable->node_has_props(has_props, root, path, + scratch_pool)); +} + svn_error_t * svn_fs_change_node_prop(svn_fs_root_t *root, const char *path, const char *name, const svn_string_t *value, @@ -1069,6 +1215,17 @@ svn_fs_change_node_prop(svn_fs_root_t *root, const char *path, value, pool)); } +svn_error_t * +svn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *scratch_pool) +{ + return svn_error_trace(root1->vtable->props_changed(changed_p, + root1, path1, + root2, path2, + TRUE, scratch_pool)); +} + svn_error_t * svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, const char *path1, svn_fs_root_t *root2, @@ -1077,7 +1234,7 @@ svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, return svn_error_trace(root1->vtable->props_changed(changed_p, root1, path1, root2, path2, - pool)); + FALSE, pool)); } svn_error_t * @@ -1125,6 +1282,30 @@ svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, TRUE, pool, pool)); } +svn_error_t * +svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo, + svn_fs_root_t *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) +{ + apr_array_header_t *paths + = apr_array_make(scratch_pool, 1, sizeof(const char *)); + svn_mergeinfo_catalog_t catalog; + + APR_ARRAY_PUSH(paths, const char *) = path; + + SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, paths, + inherit, FALSE /*include_descendants*/, + adjust_inherited_mergeinfo, + result_pool, scratch_pool)); + *mergeinfo = svn_hash_gets(catalog, path); + + return SVN_NO_ERROR; +} + svn_error_t * svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root, const char *source_path, svn_fs_root_t *target_root, @@ -1146,6 +1327,19 @@ svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root, pool)); } +svn_error_t * +svn_fs_dir_optimal_order(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root, + entries, + result_pool, + scratch_pool)); +} + svn_error_t * svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool) { @@ -1301,6 +1495,18 @@ svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root, result, pool)); } +svn_error_t * +svn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *scratch_pool) +{ + return svn_error_trace(root1->vtable->contents_changed(changed_p, + root1, path1, + root2, path2, + TRUE, + scratch_pool)); +} + svn_error_t * svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, const char *path1, svn_fs_root_t *root2, @@ -1309,7 +1515,7 @@ svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, return svn_error_trace(root1->vtable->contents_changed(changed_p, root1, path1, root2, path2, - pool)); + FALSE, pool)); } svn_error_t * @@ -1318,6 +1524,29 @@ svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool) return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool)); } +svn_error_t * +svn_fs_info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(fs->vtable->info_format(fs_format, supports_version, + fs, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_fs_info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(fs->vtable->info_config_files(files, fs, + result_pool, + scratch_pool)); +} + svn_error_t * svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool) { @@ -1350,14 +1579,6 @@ svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name, value, pool)); } -svn_error_t * -svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name, - const svn_string_t *value, apr_pool_t *pool) -{ - return svn_error_trace( - svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool)); -} - svn_error_t * svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p, svn_fs_root_t *source_root, @@ -1398,54 +1619,144 @@ svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool) } svn_error_t * -svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, - const char *token, const char *comment, - svn_boolean_t is_dav_comment, apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool) +svn_fs_lock_many(svn_fs_t *fs, + apr_hash_t *targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { + apr_hash_index_t *hi; + apr_hash_t *ok_targets = apr_hash_make(scratch_pool); + svn_error_t *err, *cb_err = SVN_NO_ERROR; + /* Enforce that the comment be xml-escapable. */ if (comment) - { - if (! svn_xml_is_xml_safe(comment, strlen(comment))) - return svn_error_create - (SVN_ERR_XML_UNESCAPABLE_DATA, NULL, - _("Lock comment contains illegal characters")); - } + if (! svn_xml_is_xml_safe(comment, strlen(comment))) + return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL, + _("Lock comment contains illegal characters")); + + if (expiration_date < 0) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Negative expiration date passed to svn_fs_lock")); /* Enforce that the token be an XML-safe URI. */ - if (token) + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) { - const char *c; - - if (strncmp(token, "opaquelocktoken:", 16)) - return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, - _("Lock token URI '%s' has bad scheme; " - "expected '%s'"), - token, "opaquelocktoken"); - - for (c = token; *c; c++) - if (! svn_ctype_isascii(*c)) - return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, - _("Lock token '%s' is not ASCII " - "at byte %u"), - token, (unsigned)(c - token)); - - /* strlen(token) == c - token. */ - if (! svn_xml_is_xml_safe(token, c - token)) - return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, - _("Lock token URI '%s' is not XML-safe"), - token); + const svn_fs_lock_target_t *target = apr_hash_this_val(hi); + + err = SVN_NO_ERROR; + if (target->token) + { + const char *c; + + + if (strncmp(target->token, "opaquelocktoken:", 16)) + err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token URI '%s' has bad scheme; " + "expected '%s'"), + target->token, "opaquelocktoken"); + + if (!err) + for (c = target->token; *c && !err; c++) + if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c)) + err = svn_error_createf( + SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token '%s' is not ASCII or is a " + "control character at byte %u"), + target->token, + (unsigned)(c - target->token)); + + /* strlen(token) == c - token. */ + if (!err && !svn_xml_is_xml_safe(target->token, c - target->token)) + err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token URI '%s' is not XML-safe"), + target->token); + } + + if (err) + { + if (!cb_err && lock_callback) + cb_err = lock_callback(lock_baton, apr_hash_this_key(hi), + NULL, err, scratch_pool); + svn_error_clear(err); + } + else + svn_hash_sets(ok_targets, apr_hash_this_key(hi), target); } - if (expiration_date < 0) - return svn_error_create - (SVN_ERR_INCORRECT_PARAMS, NULL, - _("Negative expiration date passed to svn_fs_lock")); + if (!apr_hash_count(ok_targets)) + return svn_error_trace(cb_err); + + err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment, + expiration_date, steal_lock, + lock_callback, lock_baton, + result_pool, scratch_pool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; + + return svn_error_trace(err); +} + +struct lock_baton_t { + const svn_lock_t *lock; + svn_error_t *fs_err; +}; + +/* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and + svn_fs_unlock to record the lock and error from svn_fs_lock_many + and svn_fs_unlock_many. */ +static svn_error_t * +lock_cb(void *lock_baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + struct lock_baton_t *b = lock_baton; + + b->lock = lock; + b->fs_err = svn_error_dup(fs_err); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, + const char *token, const char *comment, + svn_boolean_t is_dav_comment, apr_time_t expiration_date, + svn_revnum_t current_rev, svn_boolean_t steal_lock, + apr_pool_t *pool) +{ + apr_hash_t *targets = apr_hash_make(pool); + svn_fs_lock_target_t target; + svn_error_t *err; + struct lock_baton_t baton = {0}; + + target.token = token; + target.current_rev = current_rev; + svn_hash_sets(targets, path, &target); - return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment, - is_dav_comment, expiration_date, - current_rev, steal_lock, pool)); + err = svn_fs_lock_many(fs, targets, comment, is_dav_comment, + expiration_date, steal_lock, lock_cb, &baton, + pool, pool); + + if (baton.lock) + *lock = (svn_lock_t*)baton.lock; + + if (err && baton.fs_err) + svn_error_compose(err, baton.fs_err); + else if (!err) + err = baton.fs_err; + + return svn_error_trace(err); } svn_error_t * @@ -1454,12 +1765,62 @@ svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool) return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool)); } +svn_fs_lock_target_t * +svn_fs_lock_target_create(const char *token, + svn_revnum_t current_rev, + apr_pool_t *result_pool) +{ + svn_fs_lock_target_t *target = apr_palloc(result_pool, + sizeof(svn_fs_lock_target_t)); + + target->token = token; + target->current_rev = current_rev; + + return target; +} + +void +svn_fs_lock_target_set_token(svn_fs_lock_target_t *target, + const char *token) +{ + target->token = token; +} + +svn_error_t * +svn_fs_unlock_many(svn_fs_t *fs, + apr_hash_t *targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock, + lock_callback, lock_baton, + result_pool, scratch_pool)); +} + svn_error_t * svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token, svn_boolean_t break_lock, apr_pool_t *pool) { - return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock, - pool)); + apr_hash_t *targets = apr_hash_make(pool); + svn_error_t *err; + struct lock_baton_t baton = {0}; + + if (!token) + token = ""; + svn_hash_sets(targets, path, token); + + err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton, + pool, pool); + + if (err && baton.fs_err) + svn_error_compose(err, baton.fs_err); + else if (!err) + err = baton.fs_err; + + return svn_error_trace(err); } svn_error_t * @@ -1483,26 +1844,26 @@ svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth, get_locks_baton, pool)); } + +/* --- History functions --- */ + svn_error_t * -svn_fs_get_locks(svn_fs_t *fs, const char *path, - svn_fs_get_locks_callback_t get_locks_func, - void *get_locks_baton, apr_pool_t *pool) +svn_fs_history_prev2(svn_fs_history_t **prev_history_p, + svn_fs_history_t *history, svn_boolean_t cross_copies, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity, - get_locks_func, get_locks_baton, - pool)); + return svn_error_trace(history->vtable->prev(prev_history_p, history, + cross_copies, result_pool, + scratch_pool)); } - -/* --- History functions --- */ - svn_error_t * svn_fs_history_prev(svn_fs_history_t **prev_history_p, svn_fs_history_t *history, svn_boolean_t cross_copies, apr_pool_t *pool) { return svn_error_trace(history->vtable->prev(prev_history_p, history, - cross_copies, pool)); + cross_copies, pool, pool)); } svn_error_t * @@ -1540,20 +1901,28 @@ svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool) svn_boolean_t svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b) { - return (a->vtable->compare(a, b) != -1); + return (a->vtable->compare(a, b) != svn_fs_node_unrelated); } int svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b) { - return a->vtable->compare(a, b); + switch (a->vtable->compare(a, b)) + { + case svn_fs_node_unchanged: + return 0; + case svn_fs_node_common_ancestor: + return 1; + default: + return -1; + } } svn_error_t * svn_fs_print_modules(svn_stringbuf_t *output, apr_pool_t *pool) { - const struct fs_type_defn *defn = fs_modules; + struct fs_type_defn *defn = fs_modules; fs_library_vtable_t *vtable; apr_pool_t *iterpool = svn_pool_create(pool); @@ -1602,3 +1971,43 @@ svn_fs_version(void) { SVN_VERSION_BODY; } + + +/** info **/ +svn_error_t * +svn_fs_info(const svn_fs_info_placeholder_t **info_p, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if (fs->vtable->info_fsap) + { + SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs, + result_pool, scratch_pool)); + } + else + { + svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info)); + /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */ + SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool)); + *info_p = info; + } + return SVN_NO_ERROR; +} + +void * +svn_fs_info_dup(const void *info_void, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_fs_info_placeholder_t *info = info_void; + fs_library_vtable_t *vtable; + + SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool)); + + if (vtable->info_fsap_dup) + return vtable->info_fsap_dup(info_void, result_pool); + else + return apr_pmemdup(result_pool, info, sizeof(*info)); +} + diff --git a/contrib/subversion/subversion/libsvn_fs/fs-loader.h b/contrib/subversion/subversion/libsvn_fs/fs-loader.h index 532ff0511..db8374425 100644 --- a/contrib/subversion/subversion/libsvn_fs/fs-loader.h +++ b/contrib/subversion/subversion/libsvn_fs/fs-loader.h @@ -27,6 +27,8 @@ #include "svn_types.h" #include "svn_fs.h" +#include "svn_props.h" +#include "private/svn_mutex.h" #ifdef __cplusplus extern "C" { @@ -72,20 +74,32 @@ typedef struct fs_library_vtable_t this statement, now that the minor version has increased. */ const svn_version_t *(*get_version)(void); - /* The open_fs/create/open_fs_for_recovery/upgrade_fs functions are - serialized so that they may use the common_pool parameter to - allocate fs-global objects such as the bdb env cache. */ - svn_error_t *(*create)(svn_fs_t *fs, const char *path, apr_pool_t *pool, + /* The open_fs/create/open_fs_for_recovery/upgrade_fs functions must + use the common_pool_lock to serialize the access to the common_pool + parameter for allocating fs-global objects such as an env cache. */ + svn_error_t *(*create)(svn_fs_t *fs, const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool); - svn_error_t *(*open_fs)(svn_fs_t *fs, const char *path, apr_pool_t *pool, + svn_error_t *(*open_fs)(svn_fs_t *fs, const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool); /* open_for_recovery() is like open(), but used to fill in an fs pointer that will be passed to recover(). We assume that the open() method might not be immediately appropriate for recovery. */ svn_error_t *(*open_fs_for_recovery)(svn_fs_t *fs, const char *path, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool); - svn_error_t *(*upgrade_fs)(svn_fs_t *fs, const char *path, apr_pool_t *pool, + svn_error_t *(*upgrade_fs)(svn_fs_t *fs, + const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, apr_pool_t *common_pool); svn_error_t *(*verify_fs)(svn_fs_t *fs, const char *path, svn_revnum_t start, @@ -94,14 +108,23 @@ typedef struct fs_library_vtable_t void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool); svn_error_t *(*delete_fs)(const char *path, apr_pool_t *pool); - svn_error_t *(*hotcopy)(svn_fs_t *src_fs, svn_fs_t *dst_fs, - const char *src_path, const char *dst_path, - svn_boolean_t clean, svn_boolean_t incremental, - svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool); + svn_error_t *(*hotcopy)(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *src_path, + const char *dst_path, + svn_boolean_t clean, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, + apr_pool_t *common_pool); const char *(*get_description)(void); svn_error_t *(*recover)(svn_fs_t *fs, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -109,6 +132,7 @@ typedef struct fs_library_vtable_t svn_error_t *(*pack_fs)(svn_fs_t *fs, const char *path, svn_fs_pack_notify_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool); /* Provider-specific functions should go here, even if they could go @@ -130,8 +154,11 @@ typedef struct fs_library_vtable_t svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, + apr_pool_t *, apr_pool_t *)); - + /* For svn_fs_info_fsfs_dup(). */ + void *(*info_fsap_dup)(const void *fsap_info, + apr_pool_t *result_pool); } fs_library_vtable_t; /* This is the type of symbol an FS module defines to fetch the @@ -163,6 +190,9 @@ svn_error_t *svn_fs_base__init(const svn_version_t *loader_version, svn_error_t *svn_fs_fs__init(const svn_version_t *loader_version, fs_library_vtable_t **vtable, apr_pool_t* common_pool); +svn_error_t *svn_fs_x__init(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, + apr_pool_t* common_pool); @@ -196,22 +226,38 @@ typedef struct fs_vtable_t svn_error_t *(*list_transactions)(apr_array_header_t **names_p, svn_fs_t *fs, apr_pool_t *pool); svn_error_t *(*deltify)(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool); - svn_error_t *(*lock)(svn_lock_t **lock, svn_fs_t *fs, - const char *path, const char *token, + svn_error_t *(*lock)(svn_fs_t *fs, + apr_hash_t *targets, const char *comment, svn_boolean_t is_dav_comment, - apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool); + apr_time_t expiration_date, svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, void *lock_baton, + apr_pool_t *result_pool, apr_pool_t *scratch_pool); svn_error_t *(*generate_lock_token)(const char **token, svn_fs_t *fs, apr_pool_t *pool); - svn_error_t *(*unlock)(svn_fs_t *fs, const char *path, const char *token, - svn_boolean_t break_lock, apr_pool_t *pool); + svn_error_t *(*unlock)(svn_fs_t *fs, apr_hash_t *targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, void *lock_baton, + apr_pool_t *result_pool, apr_pool_t *scratch_pool); svn_error_t *(*get_lock)(svn_lock_t **lock, svn_fs_t *fs, const char *path, apr_pool_t *pool); svn_error_t *(*get_locks)(svn_fs_t *fs, const char *path, svn_depth_t depth, svn_fs_get_locks_callback_t get_locks_func, void *get_locks_baton, apr_pool_t *pool); + svn_error_t *(*info_format)(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + svn_error_t *(*info_config_files)(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + svn_error_t *(*info_fsap)(const void **fsap_info, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* info_fsap_dup is in the library vtable. */ svn_error_t *(*verify_root)(svn_fs_root_t *root, apr_pool_t *pool); svn_error_t *(*freeze)(svn_fs_t *fs, @@ -261,9 +307,14 @@ typedef struct root_vtable_t const char *path, apr_pool_t *pool); svn_error_t *(*node_history)(svn_fs_history_t **history_p, svn_fs_root_t *root, const char *path, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); svn_error_t *(*node_id)(const svn_fs_id_t **id_p, svn_fs_root_t *root, const char *path, apr_pool_t *pool); + svn_error_t *(*node_relation)(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, const char *path_a, + svn_fs_root_t *root_b, const char *path_b, + apr_pool_t *scratch_pool); svn_error_t *(*node_created_rev)(svn_revnum_t *revision, svn_fs_root_t *root, const char *path, apr_pool_t *pool); @@ -275,6 +326,13 @@ typedef struct root_vtable_t apr_pool_t *pool); svn_error_t *(*delete_node)(svn_fs_root_t *root, const char *path, apr_pool_t *pool); + svn_error_t *(*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_error_t *(*revision_link)(svn_fs_root_t *from_root, + svn_fs_root_t *to_root, + const char *path, + apr_pool_t *pool); svn_error_t *(*copied_from)(svn_revnum_t *rev_p, const char **path_p, svn_fs_root_t *root, const char *path, apr_pool_t *pool); @@ -288,26 +346,27 @@ typedef struct root_vtable_t apr_pool_t *pool); svn_error_t *(*node_proplist)(apr_hash_t **table_p, svn_fs_root_t *root, const char *path, apr_pool_t *pool); + svn_error_t *(*node_has_props)(svn_boolean_t *has_props, svn_fs_root_t *root, + const char *path, apr_pool_t *scratch_pool); svn_error_t *(*change_node_prop)(svn_fs_root_t *root, const char *path, const char *name, const svn_string_t *value, apr_pool_t *pool); svn_error_t *(*props_changed)(int *changed_p, svn_fs_root_t *root1, const char *path1, svn_fs_root_t *root2, - const char *path2, apr_pool_t *pool); + const char *path2, svn_boolean_t strict, + apr_pool_t *scratch_pool); /* Directories */ svn_error_t *(*dir_entries)(apr_hash_t **entries_p, svn_fs_root_t *root, const char *path, apr_pool_t *pool); + svn_error_t *(*dir_optimal_order)(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); svn_error_t *(*make_dir)(svn_fs_root_t *root, const char *path, apr_pool_t *pool); - svn_error_t *(*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_error_t *(*revision_link)(svn_fs_root_t *from_root, - svn_fs_root_t *to_root, - const char *path, - apr_pool_t *pool); /* Files */ svn_error_t *(*file_length)(svn_filesize_t *length_p, svn_fs_root_t *root, @@ -337,7 +396,8 @@ typedef struct root_vtable_t apr_pool_t *pool); svn_error_t *(*contents_changed)(int *changed_p, svn_fs_root_t *root1, const char *path1, svn_fs_root_t *root2, - const char *path2, apr_pool_t *pool); + const char *path2, svn_boolean_t strict, + apr_pool_t *scratch_pool); svn_error_t *(*get_file_delta_stream)(svn_txdelta_stream_t **stream_p, svn_fs_root_t *source_root, const char *source_path, @@ -370,7 +430,7 @@ typedef struct history_vtable_t { svn_error_t *(*prev)(svn_fs_history_t **prev_history_p, svn_fs_history_t *history, svn_boolean_t cross_copies, - apr_pool_t *pool); + apr_pool_t *result_pool, apr_pool_t *scratch_pool); svn_error_t *(*location)(const char **path, svn_revnum_t *revision, svn_fs_history_t *history, apr_pool_t *pool); } history_vtable_t; @@ -378,8 +438,10 @@ typedef struct history_vtable_t typedef struct id_vtable_t { - svn_string_t *(*unparse)(const svn_fs_id_t *id, apr_pool_t *pool); - int (*compare)(const svn_fs_id_t *a, const svn_fs_id_t *b); + svn_string_t *(*unparse)(const svn_fs_id_t *id, + apr_pool_t *pool); + svn_fs_node_relation_t (*compare)(const svn_fs_id_t *a, + const svn_fs_id_t *b); } id_vtable_t; @@ -390,6 +452,8 @@ typedef struct id_vtable_t in the 'flags' argument to svn_fs_lock(). */ #define SVN_FS__PROP_TXN_CHECK_LOCKS SVN_PROP_PREFIX "check-locks" #define SVN_FS__PROP_TXN_CHECK_OOD SVN_PROP_PREFIX "check-ood" +/* Set to "0" at the start of the txn, to "1" when svn:date changes. */ +#define SVN_FS__PROP_TXN_CLIENT_DATE SVN_PROP_PREFIX "client-date" struct svn_fs_t { @@ -487,12 +551,18 @@ struct svn_fs_access_t const char *username; /* A collection of lock-tokens supplied by the fs caller. - Hash maps (const char *) UUID --> (void *) 1 + Hash maps (const char *) UUID --> path where path can be the + magic value (void *) 1 if no path was specified. fs functions should really only be interested whether a UUID exists as a hash key at all; the value is irrelevant. */ apr_hash_t *lock_tokens; }; +struct svn_fs_lock_target_t +{ + const char *token; + svn_revnum_t current_rev; +}; #ifdef __cplusplus diff --git a/contrib/subversion/subversion/libsvn_fs/libsvn_fs.pc.in b/contrib/subversion/subversion/libsvn_fs/libsvn_fs.pc.in new file mode 100644 index 000000000..67154f01d --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs/libsvn_fs.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs +Description: Subversion Repository Filesystem Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_fs_util libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_fs +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_fs_base/bdb/changes-table.c b/contrib/subversion/subversion/libsvn_fs_base/bdb/changes-table.c index 80ff46807..b20631863 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/bdb/changes-table.c +++ b/contrib/subversion/subversion/libsvn_fs_base/bdb/changes-table.c @@ -121,12 +121,32 @@ svn_fs_bdb__changes_delete(svn_fs_t *fs, return SVN_NO_ERROR; } +/* Return a deep FS API type copy of SOURCE in internal format and allocate + * the result in RESULT_POOL. + */ +static svn_fs_path_change2_t * +change_to_fs_change(const change_t *change, + apr_pool_t *result_pool) +{ + svn_fs_path_change2_t *result = svn_fs__path_change_create_internal( + svn_fs_base__id_copy(change->noderev_id, + result_pool), + change->kind, + result_pool); + result->text_mod = change->text_mod; + result->prop_mod = change->prop_mod; + result->node_kind = svn_node_unknown; + result->copyfrom_known = FALSE; + + return result; +} /* Merge the internal-use-only CHANGE into a hash of public-FS svn_fs_path_change2_t CHANGES, collapsing multiple changes into a single succinct change per path. */ static svn_error_t * fold_change(apr_hash_t *changes, + apr_hash_t *deletions, const change_t *change) { apr_pool_t *pool = apr_hash_pool_get(changes); @@ -185,7 +205,7 @@ fold_change(apr_hash_t *changes, case svn_fs_path_change_reset: /* A reset here will simply remove the path change from the hash. */ - old_change = NULL; + new_change = NULL; break; case svn_fs_path_change_delete: @@ -194,14 +214,21 @@ fold_change(apr_hash_t *changes, /* If the path was introduced in this transaction via an add, and we are deleting it, just remove the path altogether. */ - old_change = NULL; + new_change = NULL; + } + else if (old_change->change_kind == svn_fs_path_change_replace) + { + /* A deleting a 'replace' restore the original deletion. */ + new_change = svn_hash_gets(deletions, path); + SVN_ERR_ASSERT(new_change); } 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; + new_change = old_change; + new_change->change_kind = svn_fs_path_change_delete; + new_change->text_mod = change->text_mod; + new_change->prop_mod = change->prop_mod; } break; @@ -209,38 +236,33 @@ fold_change(apr_hash_t *changes, 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_base__id_copy(change->noderev_id, - pool); - old_change->text_mod = change->text_mod; - old_change->prop_mod = change->prop_mod; + + new_change = change_to_fs_change(change, pool); + new_change->change_kind = svn_fs_path_change_replace; + + /* Remember the original deletion. + * Make sure to allocate the hash key in a durable pool. */ + svn_hash_sets(deletions, + apr_pstrdup(apr_hash_pool_get(deletions), path), + old_change); break; case svn_fs_path_change_modify: default: + new_change = old_change; if (change->text_mod) - old_change->text_mod = TRUE; + new_change->text_mod = TRUE; if (change->prop_mod) - old_change->prop_mod = TRUE; + new_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 = svn_fs__path_change_create_internal( - svn_fs_base__id_copy(change->noderev_id, pool), - change->kind, - pool); - new_change->text_mod = change->text_mod; - new_change->prop_mod = change->prop_mod; - new_change->node_kind = svn_node_unknown; - new_change->copyfrom_known = FALSE; + new_change = change_to_fs_change(change, pool); path = apr_pstrdup(pool, change->path); } @@ -265,6 +287,8 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, svn_error_t *err = SVN_NO_ERROR; apr_hash_t *changes = apr_hash_make(pool); apr_pool_t *subpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *deletions = apr_hash_make(subpool); /* Get a cursor on the first record matching KEY, and then loop over the records, adding them to the return array. */ @@ -286,11 +310,11 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, svn_skel_t *result_skel; /* Clear the per-iteration subpool. */ - svn_pool_clear(subpool); + svn_pool_clear(iterpool); /* RESULT now contains a change record associated with KEY. We need to parse that skel into an change_t structure ... */ - result_skel = svn_skel__parse(result.data, result.size, subpool); + result_skel = svn_skel__parse(result.data, result.size, iterpool); if (! result_skel) { err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, @@ -298,12 +322,12 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, key); goto cleanup; } - err = svn_fs_base__parse_change_skel(&change, result_skel, subpool); + err = svn_fs_base__parse_change_skel(&change, result_skel, iterpool); if (err) goto cleanup; /* ... and merge it with our return hash. */ - err = fold_change(changes, change); + err = fold_change(changes, deletions, change); if (err) goto cleanup; @@ -319,7 +343,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, { apr_hash_index_t *hi; - for (hi = apr_hash_first(subpool, changes); + for (hi = apr_hash_first(iterpool, changes); hi; hi = apr_hash_next(hi)) { @@ -347,6 +371,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, } /* Destroy the per-iteration subpool. */ + svn_pool_destroy(iterpool); svn_pool_destroy(subpool); /* If there are no (more) change records for this KEY, we're diff --git a/contrib/subversion/subversion/libsvn_fs_base/bdb/locks-table.c b/contrib/subversion/subversion/libsvn_fs_base/bdb/locks-table.c index a22663f39..e81bca8fa 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/bdb/locks-table.c +++ b/contrib/subversion/subversion/libsvn_fs_base/bdb/locks-table.c @@ -257,7 +257,7 @@ svn_fs_bdb__locks_get(svn_fs_t *fs, DB_SET_RANGE); if (!svn_fspath__is_root(path, strlen(path))) - lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL); + lookup_path = apr_pstrcat(pool, path, "/", SVN_VA_NULL); lookup_len = strlen(lookup_path); /* As long as the prefix of the returned KEY matches LOOKUP_PATH we diff --git a/contrib/subversion/subversion/libsvn_fs_base/bdb/strings-table.c b/contrib/subversion/subversion/libsvn_fs_base/bdb/strings-table.c index f5348e7c1..e1f4b90aa 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/bdb/strings-table.c +++ b/contrib/subversion/subversion/libsvn_fs_base/bdb/strings-table.c @@ -236,9 +236,9 @@ svn_fs_bdb__string_read(svn_fs_t *fs, { svn_fs_base__clear_dbt(&result); result.data = buf + bytes_read; - result.ulen = *len - bytes_read; + result.ulen = (u_int32_t)(*len - bytes_read); result.doff = (u_int32_t)offset; - result.dlen = *len - bytes_read; + result.dlen = result.ulen; result.flags |= (DB_DBT_USERMEM | DB_DBT_PARTIAL); db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_CURRENT); if (db_err) diff --git a/contrib/subversion/subversion/libsvn_fs_base/dag.c b/contrib/subversion/subversion/libsvn_fs_base/dag.c index 510ccbbc7..7c79dc201 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/dag.c +++ b/contrib/subversion/subversion/libsvn_fs_base/dag.c @@ -1028,12 +1028,14 @@ svn_fs_base__dag_delete_if_mutable(svn_fs_t *fs, void *val; svn_fs_dirent_t *dirent; + svn_pool_clear(subpool); apr_hash_this(hi, NULL, NULL, &val); dirent = val; SVN_ERR(svn_fs_base__dag_delete_if_mutable(fs, dirent->id, txn_id, trail, subpool)); } + svn_pool_destroy(subpool); } } @@ -1342,7 +1344,7 @@ svn_fs_base__dag_finalize_edits(dag_node_t *file, dag_node_t * -svn_fs_base__dag_dup(dag_node_t *node, +svn_fs_base__dag_dup(const dag_node_t *node, apr_pool_t *pool) { /* Allocate our new node. */ @@ -1579,10 +1581,10 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev, apr_pool_t *pool) { revision_t revision; - svn_string_t date; apr_hash_t *txnprops; svn_fs_t *fs = txn->fs; const char *txn_id = txn->id; + const svn_string_t *client_date; /* Remove any temporary transaction properties initially created by begin_txn(). */ @@ -1601,16 +1603,27 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev, SVN_ERR(svn_fs_base__set_txn_prop (fs, txn_id, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, trail, pool)); + client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE); + if (client_date) + SVN_ERR(svn_fs_base__set_txn_prop + (fs, txn_id, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, trail, pool)); + /* Promote the unfinished transaction to a committed one. */ SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev, trail, pool)); - /* Set a date on the commit. We wait until now to fetch the date, - so it's definitely newer than any previous revision's date. */ - date.data = svn_time_to_cstring(apr_time_now(), pool); - date.len = strlen(date.data); - return svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE, - NULL, &date, trail, pool); + if (!client_date || strcmp(client_date->data, "1")) + { + /* Set a date on the commit if requested. We wait until now to fetch the + date, so it's definitely newer than any previous revision's date. */ + svn_string_t date; + date.data = svn_time_to_cstring(apr_time_now(), pool); + date.len = strlen(date.data); + SVN_ERR(svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE, + NULL, &date, trail, pool)); + } + + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_fs_base/dag.h b/contrib/subversion/subversion/libsvn_fs_base/dag.h index 4c50c8441..fb963ce66 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/dag.h +++ b/contrib/subversion/subversion/libsvn_fs_base/dag.h @@ -82,7 +82,7 @@ svn_error_t *svn_fs_base__dag_get_node(dag_node_t **node, /* Return a new dag_node_t object referring to the same node as NODE, allocated in POOL. */ -dag_node_t *svn_fs_base__dag_dup(dag_node_t *node, +dag_node_t *svn_fs_base__dag_dup(const dag_node_t *node, apr_pool_t *pool); diff --git a/contrib/subversion/subversion/libsvn_fs_base/fs.c b/contrib/subversion/subversion/libsvn_fs_base/fs.c index 4ad9e6f72..06dfbaf2f 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/fs.c +++ b/contrib/subversion/subversion/libsvn_fs_base/fs.c @@ -65,8 +65,6 @@ #include "../libsvn_fs/fs-loader.h" #include "private/svn_fs_util.h" -#include "private/svn_subr_private.h" - /* Checking for return values, and reporting errors. */ @@ -472,6 +470,59 @@ bdb_write_config(svn_fs_t *fs) return svn_io_file_close(dbconfig_file, fs->pool); } +static svn_error_t * +base_bdb_info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + base_fs_data_t *bfd = fs->fsap_data; + + *fs_format = bfd->format; + *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); + + (*supports_version)->major = SVN_VER_MAJOR; + (*supports_version)->minor = 0; + (*supports_version)->patch = 0; + (*supports_version)->tag = ""; + + switch (bfd->format) + { + case 1: + break; + case 2: + (*supports_version)->minor = 4; + break; + case 3: + (*supports_version)->minor = 5; + break; + case 4: + (*supports_version)->minor = 6; + break; +#ifdef SVN_DEBUG +# if SVN_FS_BASE__FORMAT_NUMBER != 4 +# error "Need to add a 'case' statement here" +# endif +#endif + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +base_bdb_info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *files = apr_array_make(result_pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, + BDB_CONFIG_FILE, + result_pool); + return SVN_NO_ERROR; +} + static svn_error_t * base_bdb_verify_root(svn_fs_root_t *root, apr_pool_t *scratch_pool) @@ -509,6 +560,9 @@ static fs_vtable_t fs_vtable = { svn_fs_base__unlock, svn_fs_base__get_lock, svn_fs_base__get_locks, + base_bdb_info_format, + base_bdb_info_config_files, + NULL /* info_fsap */, base_bdb_verify_root, base_bdb_freeze, base_bdb_set_errcall, @@ -675,7 +729,10 @@ populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool) } static svn_error_t * -base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_create(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { int format = SVN_FS_BASE__FORMAT_NUMBER; @@ -684,12 +741,27 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, /* 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; + svn_version_t *compatible_version; + SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, + pool)); + + /* select format number */ + switch(compatible_version->minor) + { + case 0: + case 1: + case 2: + case 3: format = 1; + break; + + case 4: format = 2; + break; + + case 5: format = 3; + break; + + default:format = SVN_FS_BASE__FORMAT_NUMBER; + } } /* Create the environment and databases. */ @@ -711,8 +783,8 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, return SVN_NO_ERROR;; error: - svn_error_clear(cleanup_fs(fs)); - return svn_err; + return svn_error_compose_create(svn_err, + svn_error_trace(cleanup_fs(fs))); } @@ -751,7 +823,10 @@ check_format(int format) } static svn_error_t * -base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_open(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { int format; @@ -796,8 +871,8 @@ base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, return SVN_NO_ERROR; error: - svn_error_clear(cleanup_fs(fs)); - return svn_err; + return svn_error_compose_create(svn_err, + svn_error_trace(cleanup_fs(fs))); } @@ -834,7 +909,10 @@ bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool) } static svn_error_t * -base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_open_for_recovery(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { /* Just stash the path in the fs pointer - it's all we really need. */ @@ -844,7 +922,14 @@ base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool, } static svn_error_t * -base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_upgrade(svn_fs_t *fs, + const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { const char *version_file_path; @@ -867,6 +952,9 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, /* Bump the format file's stored version number. */ SVN_ERR(svn_io_write_version_file(version_file_path, SVN_FS_BASE__FORMAT_NUMBER, pool)); + if (notify_func) + SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER, + svn_fs_upgrade_format_bumped, pool)); /* Check and see if we need to record the "bump" revision. */ if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT) @@ -883,7 +971,7 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, But it's better to use the existing encapsulation of "opening the filesystem" rather than duplicating (or worse, partially duplicating) that logic here. */ - SVN_ERR(base_open(fs, path, subpool, common_pool)); + SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool)); /* Fetch the youngest rev, and record it */ SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool)); @@ -905,6 +993,7 @@ base_verify(svn_fs_t *fs, const char *path, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool) { @@ -929,6 +1018,7 @@ base_bdb_pack(svn_fs_t *fs, void *notify_baton, svn_cancel_func_t cancel, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool) { @@ -994,7 +1084,7 @@ svn_fs_base__clean_logs(const char *live_path, { /* Process unused logs from live area */ int idx; - apr_pool_t *sub_pool = svn_pool_create(pool); + apr_pool_t *subpool = svn_pool_create(pool); /* Process log files. */ for (idx = 0; idx < logfiles->nelts; idx++) @@ -1003,9 +1093,9 @@ svn_fs_base__clean_logs(const char *live_path, const char *live_log_path; const char *backup_log_path; - svn_pool_clear(sub_pool); - live_log_path = svn_dirent_join(live_path, log_file, sub_pool); - backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool); + svn_pool_clear(subpool); + live_log_path = svn_dirent_join(live_path, log_file, subpool); + backup_log_path = svn_dirent_join(backup_path, log_file, subpool); { /* Compare files. No point in using MD5 and wasting CPU cycles as we got full copies of both logs */ @@ -1022,17 +1112,17 @@ svn_fs_base__clean_logs(const char *live_path, SVN_ERR(svn_io_files_contents_same_p(&files_match, live_log_path, backup_log_path, - sub_pool)); + subpool)); /* If log files do not match, go to the next log file. */ if (!files_match) continue; } - SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool)); + SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool)); } - svn_pool_destroy(sub_pool); + svn_pool_destroy(subpool); } return SVN_NO_ERROR; @@ -1201,9 +1291,13 @@ base_hotcopy(svn_fs_t *src_fs, const char *dest_path, svn_boolean_t clean_logs, svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool) + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, + apr_pool_t *common_pool) { svn_error_t *err; u_int32_t pagesize; @@ -1388,6 +1482,7 @@ base_set_svn_fs_open(svn_fs_t *fs, svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, + apr_pool_t *, apr_pool_t *)) { return SVN_NO_ERROR; @@ -1409,7 +1504,8 @@ static fs_library_vtable_t library_vtable = { base_bdb_pack, base_bdb_logfiles, svn_fs_base__id_parse, - base_set_svn_fs_open + base_set_svn_fs_open, + NULL /* info_fsap_dup */ }; svn_error_t * @@ -1420,6 +1516,7 @@ svn_fs_base__init(const svn_version_t *loader_version, { { "svn_subr", svn_subr_version }, { "svn_delta", svn_delta_version }, + { "svn_fs_util", svn_fs_util__version }, { NULL, NULL } }; diff --git a/contrib/subversion/subversion/libsvn_fs_base/fs.h b/contrib/subversion/subversion/libsvn_fs_base/fs.h index 017c89856..cc8911683 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/fs.h +++ b/contrib/subversion/subversion/libsvn_fs_base/fs.h @@ -43,7 +43,11 @@ extern "C" { repository format number, and independent of any other FS back ends. See the SVN_FS_BASE__MIN_*_FORMAT defines to get a sense of what changes and features were added in which versions of this - back-end's format. */ + back-end's format. + + Note: If you bump this, please update the switch statement in + base_create() as well. + */ #define SVN_FS_BASE__FORMAT_NUMBER 4 /* Minimum format number that supports representation sharing. This diff --git a/contrib/subversion/subversion/libsvn_fs_base/id.c b/contrib/subversion/subversion/libsvn_fs_base/id.c index c063d02e5..a0947430a 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/id.c +++ b/contrib/subversion/subversion/libsvn_fs_base/id.c @@ -108,13 +108,14 @@ svn_fs_base__id_check_related(const svn_fs_id_t *a, } -int +svn_fs_node_relation_t svn_fs_base__id_compare(const svn_fs_id_t *a, const svn_fs_id_t *b) { if (svn_fs_base__id_eq(a, b)) - return 0; - return (svn_fs_base__id_check_related(a, b) ? 1 : -1); + return svn_fs_node_unchanged; + return (svn_fs_base__id_check_related(a, b) ? svn_fs_node_common_ancestor + : svn_fs_node_unrelated); } diff --git a/contrib/subversion/subversion/libsvn_fs_base/id.h b/contrib/subversion/subversion/libsvn_fs_base/id.h index 4cdb45c92..9cdc46df2 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/id.h +++ b/contrib/subversion/subversion/libsvn_fs_base/id.h @@ -53,9 +53,9 @@ svn_boolean_t svn_fs_base__id_eq(const svn_fs_id_t *a, svn_boolean_t svn_fs_base__id_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b); -/* Return 0 if A and B are equal, 1 if they are related, -1 otherwise. */ -int svn_fs_base__id_compare(const svn_fs_id_t *a, - const svn_fs_id_t *b); +/* Return the noderev relationship between A and B. */ +svn_fs_node_relation_t svn_fs_base__id_compare(const svn_fs_id_t *a, + const svn_fs_id_t *b); /* Create an ID based on NODE_ID, COPY_ID, and TXN_ID, allocated in POOL. */ diff --git a/contrib/subversion/subversion/libsvn_fs_base/key-gen.c b/contrib/subversion/subversion/libsvn_fs_base/key-gen.c index 411207d50..34f0e0f51 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/key-gen.c +++ b/contrib/subversion/subversion/libsvn_fs_base/key-gen.c @@ -39,20 +39,19 @@ void svn_fs_base__next_key(const char *this, apr_size_t *len, char *next) { apr_size_t olen = *len; /* remember the first length */ - int i = olen - 1; /* initial index; we work backwards */ + apr_size_t i; /* current index */ char c; /* current char */ svn_boolean_t carry = TRUE; /* boolean: do we have a carry or not? We start with a carry, because we're incrementing the number, after all. */ - /* Leading zeros are not allowed, except for the string "0". */ - if ((*len > 1) && (this[0] == '0')) - { - *len = 0; - return; - } + /* Empty strings and leading zeros (except for the string "0") are not + * allowed. Run our malfunction handler to prevent possible db corruption + * from being propagated further. */ + SVN_ERR_ASSERT_NO_RETURN(olen != 0 && (olen == 1 || this[0] != '0')); - for (i = (olen - 1); i >= 0; i--) + i = olen - 1; /* initial index: we work backwords */ + while (1729) { c = this[i]; @@ -79,6 +78,11 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next) } else next[i] = c; + + if (i == 0) + break; + + i--; } /* The new length is OLEN, plus 1 if there's a carry out of the @@ -102,22 +106,6 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next) } -int -svn_fs_base__key_compare(const char *a, const char *b) -{ - int a_len = strlen(a); - int b_len = strlen(b); - int cmp; - - if (a_len > b_len) - return 1; - if (b_len > a_len) - return -1; - cmp = strcmp(a, b); - return (cmp ? (cmp / abs(cmp)) : 0); -} - - svn_boolean_t svn_fs_base__same_keys(const char *a, const char *b) { diff --git a/contrib/subversion/subversion/libsvn_fs_base/key-gen.h b/contrib/subversion/subversion/libsvn_fs_base/key-gen.h index e1cd1aa04..a0634d347 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/key-gen.h +++ b/contrib/subversion/subversion/libsvn_fs_base/key-gen.h @@ -78,13 +78,6 @@ extern "C" { void svn_fs_base__next_key(const char *this, apr_size_t *len, char *next); -/* Compare two strings A and B as base-36 alphanumeric keys. - * - * Return -1, 0, or 1 if A is less than, equal to, or greater than B, - * respectively. - */ -int svn_fs_base__key_compare(const char *a, const char *b); - /* Compare two strings A and B as base-36 alphanumber keys. * * Return TRUE iff both keys are NULL or both keys have the same diff --git a/contrib/subversion/subversion/libsvn_fs_base/libsvn_fs_base.pc.in b/contrib/subversion/subversion/libsvn_fs_base/libsvn_fs_base.pc.in new file mode 100644 index 000000000..ef44eafca --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_base/libsvn_fs_base.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs_base +Description: Subversion Filesystem Base Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr libsvn_fs_util +Libs: -L${libdir} -lsvn_fs_base @SVN_DB_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_fs_base/lock.c b/contrib/subversion/subversion/libsvn_fs_base/lock.c index 79f72ccf7..1145207ea 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/lock.c +++ b/contrib/subversion/subversion/libsvn_fs_base/lock.c @@ -39,6 +39,7 @@ #include "private/svn_fs_util.h" #include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" +#include "revs-txns.h" /* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as @@ -70,6 +71,7 @@ delete_lock_and_token(const char *lock_token, } +/* The effective arguments for txn_body_lock() below. */ struct lock_args { svn_lock_t **lock_p; @@ -80,9 +82,19 @@ struct lock_args svn_boolean_t steal_lock; apr_time_t expiration_date; svn_revnum_t current_rev; + apr_pool_t *result_pool; }; +/* The body of svn_fs_base__lock(), which see. + + BATON is a 'struct lock_args *' holding the effective arguments. + BATON->path is the canonical abspath to lock. Set *BATON->lock_p + to the resulting lock. For the other arguments, see + svn_fs_lock_many(). + + This implements the svn_fs_base__retry_txn() 'body' callback type. + */ static svn_error_t * txn_body_lock(void *baton, trail_t *trail) { @@ -91,6 +103,8 @@ txn_body_lock(void *baton, trail_t *trail) svn_lock_t *existing_lock; svn_lock_t *lock; + *args->lock_p = NULL; + SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool)); /* Until we implement directory locks someday, we only allow locks @@ -194,15 +208,15 @@ txn_body_lock(void *baton, trail_t *trail) } /* Create a new lock, and add it to the tables. */ - lock = svn_lock_create(trail->pool); + lock = svn_lock_create(args->result_pool); if (args->token) - lock->token = apr_pstrdup(trail->pool, args->token); + lock->token = apr_pstrdup(args->result_pool, args->token); else SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs, - trail->pool)); - lock->path = apr_pstrdup(trail->pool, args->path); - lock->owner = apr_pstrdup(trail->pool, trail->fs->access_ctx->username); - lock->comment = apr_pstrdup(trail->pool, args->comment); + args->result_pool)); + lock->path = args->path; /* Already in result_pool. */ + lock->owner = apr_pstrdup(args->result_pool, trail->fs->access_ctx->username); + lock->comment = apr_pstrdup(args->result_pool, args->comment); lock->is_dav_comment = args->is_dav_comment; lock->creation_date = apr_time_now(); lock->expiration_date = args->expiration_date; @@ -215,31 +229,62 @@ txn_body_lock(void *baton, trail_t *trail) svn_error_t * -svn_fs_base__lock(svn_lock_t **lock, - svn_fs_t *fs, - const char *path, - const char *token, +svn_fs_base__lock(svn_fs_t *fs, + apr_hash_t *targets, const char *comment, svn_boolean_t is_dav_comment, apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool) + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - struct lock_args args; + apr_hash_index_t *hi; + svn_error_t *cb_err = SVN_NO_ERROR; + svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_fs__check_fs(fs, TRUE)); + SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, scratch_pool)); - args.lock_p = lock; - args.path = svn_fs__canonicalize_abspath(path, pool); - args.token = token; - args.comment = comment; - args.is_dav_comment = is_dav_comment; - args.steal_lock = steal_lock; - args.expiration_date = expiration_date; - args.current_rev = current_rev; - - return svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE, pool); + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + struct lock_args args; + const char *path = apr_hash_this_key(hi); + const svn_fs_lock_target_t *target = apr_hash_this_val(hi); + svn_lock_t *lock; + svn_error_t *err = NULL; + + svn_pool_clear(iterpool); + args.lock_p = &lock; + args.path = svn_fs__canonicalize_abspath(path, result_pool); + args.token = target->token; + args.comment = comment; + args.is_dav_comment = is_dav_comment; + args.steal_lock = steal_lock; + args.expiration_date = expiration_date; + args.current_rev = target->current_rev; + args.result_pool = result_pool; + + if (SVN_IS_VALID_REVNUM(target->current_rev)) + { + if (target->current_rev > youngest_rev) + err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), + target->current_rev); + } + + if (!err) + err = svn_fs_base__retry_txn(fs, txn_body_lock, &args, TRUE, + iterpool); + if (!cb_err && lock_callback) + cb_err = lock_callback(lock_baton, args.path, lock, err, iterpool); + svn_error_clear(err); + } + svn_pool_destroy(iterpool); + + return svn_error_trace(cb_err); } @@ -253,11 +298,12 @@ svn_fs_base__generate_lock_token(const char **token, generate a URI that matches the DAV RFC. We could change this to some other URI scheme someday, if we wish. */ *token = apr_pstrcat(pool, "opaquelocktoken:", - svn_uuid_generate(pool), (char *)NULL); + svn_uuid_generate(pool), SVN_VA_NULL); return SVN_NO_ERROR; } +/* The effective arguments for txn_body_unlock() below. */ struct unlock_args { const char *path; @@ -266,6 +312,14 @@ struct unlock_args }; +/* The body of svn_fs_base__unlock(), which see. + + BATON is a 'struct unlock_args *' holding the effective arguments. + BATON->path is the canonical path and BATON->token is the token. + For the other arguments, see svn_fs_unlock_many(). + + This implements the svn_fs_base__retry_txn() 'body' callback type. + */ static svn_error_t * txn_body_unlock(void *baton, trail_t *trail) { @@ -308,19 +362,40 @@ txn_body_unlock(void *baton, trail_t *trail) svn_error_t * svn_fs_base__unlock(svn_fs_t *fs, - const char *path, - const char *token, + apr_hash_t *targets, svn_boolean_t break_lock, - apr_pool_t *pool) + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - struct unlock_args args; + apr_hash_index_t *hi; + svn_error_t *cb_err = SVN_NO_ERROR; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_fs__check_fs(fs, TRUE)); - args.path = svn_fs__canonicalize_abspath(path, pool); - args.token = token; - args.break_lock = break_lock; - return svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, pool); + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + struct unlock_args args; + const char *path = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); + svn_error_t *err; + + svn_pool_clear(iterpool); + args.path = svn_fs__canonicalize_abspath(path, result_pool); + args.token = token; + args.break_lock = break_lock; + + err = svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, + iterpool); + if (!cb_err && lock_callback) + cb_err = lock_callback(lock_baton, path, NULL, err, iterpool); + svn_error_clear(err); + } + svn_pool_destroy(iterpool); + + return svn_error_trace(cb_err); } @@ -465,8 +540,9 @@ svn_fs_base__get_locks(svn_fs_t *fs, args.path = svn_fs__canonicalize_abspath(path, pool); args.depth = depth; /* Enough for 100+ locks if the comments are small. */ - args.stream = svn_stream__from_spillbuf(4 * 1024 /* blocksize */, - 64 * 1024 /* maxsize */, + args.stream = svn_stream__from_spillbuf(svn_spillbuf__create(4 * 1024 /* blocksize */, + 64 * 1024 /* maxsize */, + pool), pool); SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool)); @@ -495,12 +571,12 @@ svn_fs_base__get_locks(svn_fs_t *fs, /* Now read that much into a buffer. */ skel_buf = apr_palloc(pool, skel_len + 1); - SVN_ERR(svn_stream_read(stream, skel_buf, &skel_len)); + SVN_ERR(svn_stream_read_full(stream, skel_buf, &skel_len)); skel_buf[skel_len] = '\0'; /* Read the extra newline that follows the skel. */ len = 1; - SVN_ERR(svn_stream_read(stream, &c, &len)); + SVN_ERR(svn_stream_read_full(stream, &c, &len)); if (c != '\n') return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); diff --git a/contrib/subversion/subversion/libsvn_fs_base/lock.h b/contrib/subversion/subversion/libsvn_fs_base/lock.h index 603e78c5a..3a17ce05e 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/lock.h +++ b/contrib/subversion/subversion/libsvn_fs_base/lock.h @@ -33,34 +33,41 @@ extern "C" { /* These functions implement part of the FS loader library's fs - vtables. See the public svn_fs.h for docstrings.*/ + vtables. */ -svn_error_t *svn_fs_base__lock(svn_lock_t **lock, - svn_fs_t *fs, - const char *path, - const char *token, +/* See svn_fs_lock(), svn_fs_lock_many(). */ +svn_error_t *svn_fs_base__lock(svn_fs_t *fs, + apr_hash_t *targets, const char *comment, svn_boolean_t is_dav_comment, apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool); + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* See svn_fs_generate_lock_token(). */ svn_error_t *svn_fs_base__generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool); +/* See svn_fs_unlock(), svn_fs_unlock_many(). */ svn_error_t *svn_fs_base__unlock(svn_fs_t *fs, - const char *path, - const char *token, + apr_hash_t *targets, svn_boolean_t break_lock, - apr_pool_t *pool); + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* See svn_fs_get_lock(). */ svn_error_t *svn_fs_base__get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, apr_pool_t *pool); +/* See svn_fs_get_locks2(). */ svn_error_t * svn_fs_base__get_locks(svn_fs_t *fs, const char *path, diff --git a/contrib/subversion/subversion/libsvn_fs_base/reps-strings.c b/contrib/subversion/subversion/libsvn_fs_base/reps-strings.c index 553075d4e..e88583aa4 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/reps-strings.c +++ b/contrib/subversion/subversion/libsvn_fs_base/reps-strings.c @@ -920,7 +920,7 @@ txn_body_read_rep(void *baton, trail_t *trail) args->rb->md5_checksum))) return svn_error_create(SVN_ERR_FS_CORRUPT, svn_checksum_mismatch_err(rep->md5_checksum, - args->rb->sha1_checksum, trail->pool, + args->rb->md5_checksum, trail->pool, _("MD5 checksum mismatch on representation '%s'"), args->rb->rep_key), NULL); @@ -1224,7 +1224,8 @@ svn_fs_base__rep_contents_read_stream(svn_stream_t **rs_p, SVN_ERR(rep_read_get_baton(&rb, fs, rep_key, use_trail_for_reads, trail, pool)); *rs_p = svn_stream_create(rb, pool); - svn_stream_set_read(*rs_p, rep_read_contents); + svn_stream_set_read2(*rs_p, NULL /* only full read support */, + rep_read_contents); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_fs_base/revs-txns.c b/contrib/subversion/subversion/libsvn_fs_base/revs-txns.c index d21884311..f1029f4e0 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/revs-txns.c +++ b/contrib/subversion/subversion/libsvn_fs_base/revs-txns.c @@ -574,6 +574,10 @@ svn_fs_base__set_txn_prop(svn_fs_t *fs, txn->proplist = apr_hash_make(pool); /* Set the property. */ + if (svn_hash_gets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE) + && !strcmp(name, SVN_PROP_REVISION_DATE)) + svn_hash_sets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE, + svn_string_create("1", pool)); svn_hash_sets(txn->proplist, name, value); /* Now overwrite the transaction. */ @@ -707,6 +711,34 @@ txn_body_begin_txn(void *baton, trail_t *trail) SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); } + /* 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.) Do + this before setting CLIENT_DATE so that it is not recorded as an + explicit setting. */ + { + struct change_txn_prop_args cpargs; + svn_string_t date; + cpargs.fs = trail->fs; + cpargs.id = txn_id; + cpargs.name = SVN_PROP_REVISION_DATE; + date.data = svn_time_to_cstring(apr_time_now(), trail->pool); + date.len = strlen(date.data); + cpargs.value = &date; + SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); + } + + if (args->flags & SVN_FS_TXN_CLIENT_DATE) + { + struct change_txn_prop_args cpargs; + cpargs.fs = trail->fs; + cpargs.id = txn_id; + cpargs.name = SVN_FS__PROP_TXN_CLIENT_DATE; + cpargs.value = svn_string_create("0", trail->pool); + + SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); + } + *args->txn_p = make_txn(trail->fs, txn_id, args->base_rev, trail->pool); return SVN_NO_ERROR; } @@ -722,7 +754,6 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p, { svn_fs_txn_t *txn; struct begin_txn_args args; - svn_string_t date; SVN_ERR(svn_fs__check_fs(fs, TRUE)); @@ -733,15 +764,7 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p, *txn_p = txn; - /* 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); - return svn_fs_base__change_txn_prop(txn, SVN_PROP_REVISION_DATE, - &date, pool); + return SVN_NO_ERROR; } @@ -897,7 +920,7 @@ delete_txn_tree(svn_fs_t *fs, svn_error_t *err; /* If this sucker isn't mutable, there's nothing to do. */ - if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(id), txn_id) != 0) + if (strcmp(svn_fs_base__id_txn_id(id), txn_id) != 0) return SVN_NO_ERROR; /* See if the thing has dirents that need to be recursed upon. If diff --git a/contrib/subversion/subversion/libsvn_fs_base/tree.c b/contrib/subversion/subversion/libsvn_fs_base/tree.c index 046ab5daa..924e7c94a 100644 --- a/contrib/subversion/subversion/libsvn_fs_base/tree.c +++ b/contrib/subversion/subversion/libsvn_fs_base/tree.c @@ -543,7 +543,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, parent_copy_id = svn_fs_base__id_copy_id(parent_id); /* Easy out: if this child is already mutable, we have nothing to do. */ - if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0) + if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0) return SVN_NO_ERROR; /* If the child and its parent are on the same branch, then the @@ -560,7 +560,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, target of any copy, and therefore must be on the same branch as its parent. */ if ((strcmp(child_copy_id, "0") == 0) - || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0)) + || (strcmp(child_copy_id, parent_copy_id) == 0)) { *inherit_p = copy_id_inherit_parent; return SVN_NO_ERROR; @@ -569,7 +569,8 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, { copy_t *copy; SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool)); - if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1) + if ( svn_fs_base__id_compare(copy->dst_noderev_id, child_id) + == svn_fs_node_unrelated) { *inherit_p = copy_id_inherit_parent; return SVN_NO_ERROR; @@ -1027,6 +1028,30 @@ base_node_id(const svn_fs_id_t **id_p, return SVN_NO_ERROR; } +static svn_error_t * +base_node_relation(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, const char *path_a, + svn_fs_root_t *root_b, const char *path_b, + apr_pool_t *pool) +{ + const svn_fs_id_t *id_a, *id_b; + + /* Paths from different repository are never related. */ + if (root_a->fs != root_b->fs) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + /* Naive implementation. */ + SVN_ERR(base_node_id(&id_a, root_a, path_a, pool)); + SVN_ERR(base_node_id(&id_b, root_b, path_b, pool)); + + *relation = svn_fs_base__id_compare(id_a, id_b); + + return SVN_NO_ERROR; +} + struct node_created_rev_args { svn_revnum_t revision; @@ -1212,7 +1237,7 @@ base_node_prop(svn_string_t **value_p, args.propname = propname; SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args, FALSE, scratch_pool)); - *value_p = value ? svn_string_dup(value, pool) : NULL; + *value_p = svn_string_dup(value, pool); svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } @@ -1260,6 +1285,21 @@ base_node_proplist(apr_hash_t **table_p, return SVN_NO_ERROR; } +static svn_error_t * +base_node_has_props(svn_boolean_t *has_props, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + apr_hash_t *props; + + SVN_ERR(base_node_proplist(&props, root, path, scratch_pool)); + + *has_props = (0 < apr_hash_count(props)); + + return SVN_NO_ERROR; +} + struct change_node_prop_args { svn_fs_root_t *root; @@ -1369,6 +1409,7 @@ struct things_changed_args svn_fs_root_t *root2; const char *path1; const char *path2; + svn_boolean_t strict; apr_pool_t *pool; }; @@ -1378,11 +1419,26 @@ txn_body_props_changed(void *baton, trail_t *trail) { struct things_changed_args *args = baton; dag_node_t *node1, *node2; + apr_hash_t *proplist1, *proplist2; SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool)); SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); - return svn_fs_base__things_different(args->changed_p, NULL, - node1, node2, trail, trail->pool); + SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL, + node1, node2, trail, trail->pool)); + + /* Is there a potential false positive and do we want to correct it? */ + if (!args->strict || !*args->changed_p) + return SVN_NO_ERROR; + + /* Different representations. They might still have equal contents. */ + SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1, + trail, trail->pool)); + SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2, + trail, trail->pool)); + + *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2, + trail->pool); + return SVN_NO_ERROR; } @@ -1392,6 +1448,7 @@ base_props_changed(svn_boolean_t *changed_p, const char *path1, svn_fs_root_t *root2, const char *path2, + svn_boolean_t strict, apr_pool_t *pool) { struct things_changed_args args; @@ -1408,6 +1465,7 @@ base_props_changed(svn_boolean_t *changed_p, args.path2 = path2; args.changed_p = changed_p; args.pool = pool; + args.strict = strict; return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args, TRUE, pool); @@ -1561,6 +1619,25 @@ base_dir_entries(apr_hash_t **table_p, return SVN_NO_ERROR; } +static svn_error_t * +base_dir_optimal_order(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* 1:1 copy of entries with no differnce in ordering */ + apr_hash_index_t *hi; + apr_array_header_t *result + = apr_array_make(result_pool, apr_hash_count(entries), + sizeof(svn_fs_dirent_t *)); + for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi)) + APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi); + + *ordered_p = result; + return SVN_NO_ERROR; +} + /* Merges and commits. */ @@ -3104,7 +3181,8 @@ txn_body_copy(void *baton, if ((to_parent_path->node) && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node), svn_fs_base__dag_get_id - (to_parent_path->node)) == 0)) + (to_parent_path->node)) + == svn_fs_node_unchanged)) return SVN_NO_ERROR; if (! from_root->is_txn_root) @@ -3292,8 +3370,8 @@ txn_body_copied_from(void *baton, trail_t *trail) return SVN_NO_ERROR; /* If NODE's copy-ID is the same as that of its predecessor... */ - if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), - svn_fs_base__id_copy_id(pred_id)) != 0) + if (strcmp(svn_fs_base__id_copy_id(node_id), + svn_fs_base__id_copy_id(pred_id)) != 0) { /* ... then NODE was either the target of a copy operation, a copied subtree item. We examine the actual copy record @@ -3942,11 +4020,53 @@ txn_body_contents_changed(void *baton, trail_t *trail) { struct things_changed_args *args = baton; dag_node_t *node1, *node2; + svn_checksum_t *checksum1, *checksum2; + svn_stream_t *stream1, *stream2; + svn_boolean_t same; SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool)); SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); - return svn_fs_base__things_different(NULL, args->changed_p, - node1, node2, trail, trail->pool); + SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p, + node1, node2, trail, trail->pool)); + + /* Is there a potential false positive and do we want to correct it? */ + if (!args->strict || !*args->changed_p) + return SVN_NO_ERROR; + + /* Different representations. They might still have equal contents. */ + + /* Compare MD5 checksums. These should be readily accessible. */ + SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5, + node1, trail, trail->pool)); + SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5, + node2, trail, trail->pool)); + + /* Different MD5 checksums -> different contents */ + if (!svn_checksum_match(checksum1, checksum2)) + return SVN_NO_ERROR; + + /* Paranoia. Compare SHA1 checksums because that's the level of + confidence we require for e.g. the working copy. */ + SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1, + node1, trail, trail->pool)); + SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1, + node2, trail, trail->pool)); + + /* Different SHA1 checksums -> different contents */ + if (checksum1 && checksum2) + { + *args->changed_p = !svn_checksum_match(checksum1, checksum2); + return SVN_NO_ERROR; + } + + /* SHA1 checksums are not available for very old reps / repositories. */ + SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool)); + SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool)); + SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool)); + + /* Now, it's definitive. */ + *args->changed_p = !same; + return SVN_NO_ERROR; } @@ -3958,6 +4078,7 @@ base_contents_changed(svn_boolean_t *changed_p, const char *path1, svn_fs_root_t *root2, const char *path2, + svn_boolean_t strict, apr_pool_t *pool) { struct things_changed_args args; @@ -3989,6 +4110,7 @@ base_contents_changed(svn_boolean_t *changed_p, args.path2 = path2; args.changed_p = changed_p; args.pool = pool; + args.strict = strict; return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args, TRUE, pool); @@ -4108,7 +4230,8 @@ static svn_error_t * base_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, const char *path, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_node_kind_t kind; @@ -4117,15 +4240,16 @@ base_node_history(svn_fs_history_t **history_p, return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); /* And we require that the path exist in the root. */ - SVN_ERR(base_check_path(&kind, root, path, pool)); + SVN_ERR(base_check_path(&kind, root, path, scratch_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), + svn_fs__canonicalize_abspath(path, + result_pool), root->rev, FALSE, NULL, - SVN_INVALID_REVNUM, pool); + SVN_INVALID_REVNUM, result_pool); return SVN_NO_ERROR; } @@ -4298,8 +4422,7 @@ txn_body_history_prev(void *baton, trail_t *trail) (which is either a real predecessor, or is the node itself playing the predecessor role to an imaginary mutable successor), then we need to report a copy. */ - if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), - end_copy_id) != 0) + if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0) { const char *remainder; dag_node_t *dst_node; @@ -4376,7 +4499,8 @@ static svn_error_t * base_history_prev(svn_fs_history_t **prev_history_p, svn_fs_history_t *history, svn_boolean_t cross_copies, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_fs_history_t *prev_history = NULL; base_history_data_t *bhd = history->fsap_data; @@ -4390,10 +4514,12 @@ base_history_prev(svn_fs_history_t **prev_history_p, { if (! bhd->is_interesting) prev_history = assemble_history(fs, "/", bhd->revision, - 1, NULL, SVN_INVALID_REVNUM, pool); + 1, NULL, SVN_INVALID_REVNUM, + result_pool); else if (bhd->revision > 0) prev_history = assemble_history(fs, "/", bhd->revision - 1, - 1, NULL, SVN_INVALID_REVNUM, pool); + 1, NULL, SVN_INVALID_REVNUM, + result_pool); } else { @@ -4407,9 +4533,9 @@ base_history_prev(svn_fs_history_t **prev_history_p, args.prev_history_p = &prev_history; args.history = prev_history; args.cross_copies = cross_copies; - args.pool = pool; + args.pool = result_pool; SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args, - FALSE, pool)); + FALSE, result_pool)); if (! prev_history) break; bhd = prev_history->fsap_data; @@ -5370,20 +5496,23 @@ static root_vtable_t root_vtable = { base_check_path, base_node_history, base_node_id, + base_node_relation, base_node_created_rev, base_node_origin_rev, base_node_created_path, base_delete_node, + base_copy, + base_revision_link, base_copied_from, base_closest_copy, base_node_prop, base_node_proplist, + base_node_has_props, base_change_node_prop, base_props_changed, base_dir_entries, + base_dir_optimal_order, base_make_dir, - base_copy, - base_revision_link, base_file_length, base_file_checksum, base_file_contents, diff --git a/contrib/subversion/subversion/libsvn_fs_fs/cached_data.c b/contrib/subversion/subversion/libsvn_fs_fs/cached_data.c new file mode 100644 index 000000000..948870e6b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/cached_data.c @@ -0,0 +1,3387 @@ +/* cached_data.c --- cached (read) access to FSFS data + * + * ==================================================================== + * 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 "cached_data.h" + +#include + +#include "svn_hash.h" +#include "svn_ctype.h" +#include "svn_sorts.h" +#include "private/svn_delta_private.h" +#include "private/svn_io_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "fs_fs.h" +#include "id.h" +#include "index.h" +#include "low_level.h" +#include "pack.h" +#include "util.h" +#include "temp_serializer.h" + +#include "../libsvn_fs/fs-loader.h" +#include "../libsvn_delta/delta.h" /* for SVN_DELTA_WINDOW_SIZE */ + +#include "svn_private_config.h" + +/* forward-declare. See implementation for the docstring */ +static svn_error_t * +block_read(void **result, + svn_fs_t *fs, + svn_revnum_t revision, + apr_uint64_t item_index, + svn_fs_fs__revision_file_t *revision_file, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Defined this to enable access logging via dgb__log_access +#define SVN_FS_FS__LOG_ACCESS + */ + +/* When SVN_FS_FS__LOG_ACCESS has been defined, write a line to console + * showing where REVISION, ITEM_INDEX is located in FS and use ITEM to + * show details on it's contents if not NULL. To support format 6 and + * earlier repos, ITEM_TYPE (SVN_FS_FS__ITEM_TYPE_*) must match ITEM. + * Use SCRATCH_POOL for temporary allocations. + * + * For pre-format7 repos, the display will be restricted. + */ +static svn_error_t * +dbg_log_access(svn_fs_t *fs, + svn_revnum_t revision, + apr_uint64_t item_index, + void *item, + apr_uint32_t item_type, + apr_pool_t *scratch_pool) +{ + /* no-op if this macro is not defined */ +#ifdef SVN_FS_FS__LOG_ACCESS + fs_fs_data_t *ffd = fs->fsap_data; + apr_off_t end_offset = 0; + svn_fs_fs__p2l_entry_t *entry = NULL; + static const char *types[] = {"", "frep ", "drep ", "fprop", "dprop", + "node ", "chgs ", "rep "}; + const char *description = ""; + const char *type = types[item_type]; + const char *pack = ""; + apr_off_t offset; + svn_fs_fs__revision_file_t *rev_file; + + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, revision, + scratch_pool)); + + /* determine rev / pack file offset */ + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, revision, NULL, + item_index, scratch_pool)); + + /* constructing the pack file description */ + if (revision < ffd->min_unpacked_rev) + pack = apr_psprintf(scratch_pool, "%4ld|", + revision / ffd->max_files_per_dir); + + /* construct description if possible */ + if (item_type == SVN_FS_FS__ITEM_TYPE_NODEREV && item != NULL) + { + node_revision_t *node = item; + const char *data_rep + = node->data_rep + ? apr_psprintf(scratch_pool, " d=%ld/%" APR_UINT64_T_FMT, + node->data_rep->revision, + node->data_rep->item_index) + : ""; + const char *prop_rep + = node->prop_rep + ? apr_psprintf(scratch_pool, " p=%ld/%" APR_UINT64_T_FMT, + node->prop_rep->revision, + node->prop_rep->item_index) + : ""; + description = apr_psprintf(scratch_pool, "%s (pc=%d%s%s)", + node->created_path, + node->predecessor_count, + data_rep, + prop_rep); + } + else if (item_type == SVN_FS_FS__ITEM_TYPE_ANY_REP) + { + svn_fs_fs__rep_header_t *header = item; + if (header == NULL) + description = " (txdelta window)"; + else if (header->type == svn_fs_fs__rep_plain) + description = " PLAIN"; + else if (header->type == svn_fs_fs__rep_self_delta) + description = " DELTA"; + else + description = apr_psprintf(scratch_pool, + " DELTA against %ld/%" APR_UINT64_T_FMT, + header->base_revision, + header->base_item_index); + } + else if (item_type == SVN_FS_FS__ITEM_TYPE_CHANGES && item != NULL) + { + apr_array_header_t *changes = item; + switch (changes->nelts) + { + case 0: description = " no change"; + break; + case 1: description = " 1 change"; + break; + default: description = apr_psprintf(scratch_pool, " %d changes", + changes->nelts); + } + } + + /* some info is only available in format7 repos */ + if (svn_fs_fs__use_log_addressing(fs)) + { + /* reverse index lookup: get item description in ENTRY */ + SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, rev_file, revision, + offset, scratch_pool)); + if (entry) + { + /* more details */ + end_offset = offset + entry->size; + type = types[entry->type]; + } + + /* line output */ + printf("%5s%4lx:%04lx -%4lx:%04lx %s %7ld %5"APR_UINT64_T_FMT" %s\n", + pack, (long)(offset / ffd->block_size), + (long)(offset % ffd->block_size), + (long)(end_offset / ffd->block_size), + (long)(end_offset % ffd->block_size), + type, revision, item_index, description); + } + else + { + /* reduced logging for format 6 and earlier */ + printf("%5s%10" APR_UINT64_T_HEX_FMT " %s %7ld %7" APR_UINT64_T_FMT \ + " %s\n", + pack, (apr_uint64_t)(offset), type, revision, item_index, + description); + } + +#endif + + return SVN_NO_ERROR; +} + +/* Convenience wrapper around svn_io_file_aligned_seek, taking filesystem + FS instead of a block size. */ +static svn_error_t * +aligned_seek(svn_fs_t *fs, + apr_file_t *file, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return svn_error_trace(svn_io_file_aligned_seek(file, ffd->block_size, + buffer_start, offset, + 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(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_uint64_t item, + apr_pool_t *pool) +{ + svn_fs_fs__revision_file_t *rev_file; + apr_off_t offset = -1; + + SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool)); + + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rev, pool, pool)); + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, rev, NULL, item, + pool)); + + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool)); + + *file = rev_file; + + return SVN_NO_ERROR; +} + +/* Open the representation REP for a node-revision in filesystem FS, seek + to its position and store the newly opened file in FILE. Perform + temporary allocations in POOL. */ +static svn_error_t * +open_and_seek_transaction(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + representation_t *rep, + apr_pool_t *pool) +{ + apr_off_t offset; + + SVN_ERR(svn_fs_fs__open_proto_rev_file(file, fs, &rep->txn_id, pool, pool)); + + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, NULL, SVN_INVALID_REVNUM, + &rep->txn_id, rep->item_index, pool)); + SVN_ERR(aligned_seek(fs, (*file)->file, NULL, offset, pool)); + + 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(svn_fs_fs__revision_file_t **file_p, + svn_fs_t *fs, + representation_t *rep, + apr_pool_t *pool) +{ + if (! svn_fs_fs__id_txn_used(&rep->txn_id)) + return open_and_seek_revision(file_p, fs, rep->revision, rep->item_index, + pool); + else + return open_and_seek_transaction(file_p, fs, rep, pool); +} + + + +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); +} + +/* Return TRUE, if FS is of a format that supports block-read and the + feature has been enabled. */ +static svn_boolean_t +use_block_read(svn_fs_t *fs) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return svn_fs_fs__use_log_addressing(fs) && ffd->use_block_read; +} + +/* 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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_boolean_t is_cached = FALSE; + fs_fs_data_t *ffd = fs->fsap_data; + + if (svn_fs_fs__id_is_txn(id)) + { + apr_file_t *file; + + /* This is a transaction node-rev. Its storage logic is very + different from that of rev / pack files. */ + err = svn_io_file_open(&file, + svn_fs_fs__path_txn_node_rev(fs, id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_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(file, + FALSE, + scratch_pool), + result_pool, scratch_pool)); + } + else + { + svn_fs_fs__revision_file_t *revision_file; + + /* noderevs in rev / pack files can be cached */ + const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_item(id); + pair_cache_key_t key = { 0 }; + key.revision = rev_item->revision; + key.second = rev_item->number; + + /* Not found or not applicable. Try a noderev cache lookup. + * If that succeeds, we are done here. */ + if (ffd->node_revision_cache) + { + SVN_ERR(svn_cache__get((void **) noderev_p, + &is_cached, + ffd->node_revision_cache, + &key, + result_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + /* read the data from disk */ + SVN_ERR(open_and_seek_revision(&revision_file, fs, + rev_item->revision, + rev_item->number, + scratch_pool)); + + if (use_block_read(fs)) + { + /* block-read will parse the whole block and will also return + the one noderev that we need right now. */ + SVN_ERR(block_read((void **)noderev_p, fs, + rev_item->revision, + rev_item->number, + revision_file, + result_pool, + scratch_pool)); + } + else + { + /* physical addressing mode reading, parsing and caching */ + SVN_ERR(svn_fs_fs__read_noderev(noderev_p, + revision_file->stream, + result_pool, + scratch_pool)); + + /* Workaround issue #4031: is-fresh-txn-root in revision files. */ + (*noderev_p)->is_fresh_txn_root = FALSE; + + /* The noderev is not in cache, yet. Add it, if caching has been enabled. */ + if (ffd->node_revision_cache) + SVN_ERR(svn_cache__set(ffd->node_revision_cache, + &key, + *noderev_p, + scratch_pool)); + } + + SVN_ERR(svn_fs_fs__close_revision_file(revision_file)); + } + + 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 *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_item(id); + + svn_error_t *err = get_node_revision_body(noderev_p, fs, id, + result_pool, scratch_pool); + if (err && err->apr_err == SVN_ERR_FS_CORRUPT) + { + svn_string_t *id_string = svn_fs_fs__id_unparse(id, scratch_pool); + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + "Corrupt node-revision '%s'", + id_string->data); + } + + SVN_ERR(dbg_log_access(fs, + rev_item->revision, + rev_item->number, + *noderev_p, + SVN_FS_FS__ITEM_TYPE_NODEREV, + scratch_pool)); + + return svn_error_trace(err); +} + + +/* 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, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_off_t offset, + apr_pool_t *pool) +{ + node_revision_t *noderev; + + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool)); + SVN_ERR(svn_fs_fs__read_noderev(&noderev, + rev_file->stream, + pool, pool)); + + /* noderev->id is const, get rid of that */ + *id_p = svn_fs_fs__id_copy(noderev->id, pool); + + /* assert that the txn_id is REV + * (asserting on offset would be harder because we the rev_offset is not + * known here) */ + assert(svn_fs_fs__id_rev(*id_p) == rev); + + 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. + + Allocate temporary variables from POOL. */ +static svn_error_t * +get_root_changes_offset(apr_off_t *root_offset, + apr_off_t *changes_offset, + svn_fs_fs__revision_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 rev_offset; + apr_seek_where_t seek_relative; + svn_stringbuf_t *trailer; + char buffer[64]; + apr_off_t start; + apr_off_t end; + apr_size_t len; + + /* 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 (rev_file->is_packed && ((rev + 1) % ffd->max_files_per_dir != 0)) + { + SVN_ERR(svn_fs_fs__get_packed_offset(&end, fs, rev + 1, pool)); + seek_relative = APR_SET; + } + else + { + seek_relative = APR_END; + end = 0; + } + + /* Offset of the revision from the start of the pack file, if applicable. */ + if (rev_file->is_packed) + SVN_ERR(svn_fs_fs__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->file, seek_relative, &end, pool)); + + if (end < sizeof(buffer)) + { + len = (apr_size_t)end; + start = 0; + } + else + { + len = sizeof(buffer); + start = end - sizeof(buffer); + } + + /* Read in this last block, from which we will identify the last line. */ + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, start, pool)); + SVN_ERR(svn_io_file_read_full2(rev_file->file, buffer, len, NULL, NULL, + pool)); + + /* Parse the last line. */ + trailer = svn_stringbuf_ncreate(buffer, len, pool); + SVN_ERR(svn_fs_fs__parse_revision_trailer(root_offset, + changes_offset, + trailer, + rev)); + + /* return absolute offsets */ + if (root_offset) + *root_offset += rev_offset; + if (changes_offset) + *changes_offset += rev_offset; + + 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 *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool)); + + if (svn_fs_fs__use_log_addressing(fs)) + { + *root_id_p = svn_fs_fs__id_create_root(rev, result_pool); + } + else + { + svn_fs_fs__revision_file_t *revision_file; + apr_off_t root_offset; + svn_fs_id_t *root_id = NULL; + svn_boolean_t is_cached; + + SVN_ERR(svn_cache__get((void **) root_id_p, &is_cached, + ffd->rev_root_id_cache, &rev, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, + scratch_pool, scratch_pool)); + SVN_ERR(get_root_changes_offset(&root_offset, NULL, + revision_file, fs, rev, + scratch_pool)); + + SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev, + root_offset, result_pool)); + + SVN_ERR(svn_fs_fs__close_revision_file(revision_file)); + + SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, + scratch_pool)); + + *root_id_p = root_id; + } + + return SVN_NO_ERROR; +} + +/* Describes a lazily opened rev / pack file. Instances will be shared + between multiple instances of rep_state_t. */ +typedef struct shared_file_t +{ + /* The opened file. NULL while file is not open, yet. */ + svn_fs_fs__revision_file_t *rfile; + + /* file system to open the file in */ + svn_fs_t *fs; + + /* a revision contained in the FILE. Since this file may be shared, + that value may be different from REP_STATE_T->REVISION. */ + svn_revnum_t revision; + + /* pool to use when creating the FILE. This guarantees that the file + remains open / valid beyond the respective local context that required + the file to be opened eventually. */ + apr_pool_t *pool; +} shared_file_t; + +/* Represents where in the current svndiff data block each + representation is. */ +typedef struct rep_state_t +{ + /* shared lazy-open rev/pack file structure */ + shared_file_t *sfile; + /* The txdelta window cache to use or NULL. */ + svn_cache__t *raw_window_cache; + /* Caches raw (unparsed) windows. May be NULL. */ + svn_cache__t *window_cache; + /* Caches un-deltified windows. May be NULL. */ + svn_cache__t *combined_cache; + /* revision containing the representation */ + svn_revnum_t revision; + /* representation's item index in REVISION */ + apr_uint64_t item_index; + /* length of the header at the start of the rep. + 0 iff this is rep is stored in a container + (i.e. does not have a header) */ + apr_size_t header_size; + apr_off_t start; /* The starting offset for the raw + svndiff/plaintext data minus header. + -1 if the offset is yet unknown. */ + apr_off_t current;/* The current offset relative to START. */ + apr_off_t size; /* The on-disk size of the representation. */ + int ver; /* If a delta, what svndiff version? + -1 for unknown delta version. */ + int chunk_index; /* number of the window to read */ +} rep_state_t; + +/* Simple wrapper around svn_fs_fs__get_file_offset to simplify callers. */ +static svn_error_t * +get_file_offset(apr_off_t *offset, + rep_state_t *rs, + apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_fs__get_file_offset(offset, + rs->sfile->rfile->file, + pool)); +} + +/* Simple wrapper around svn_io_file_aligned_seek to simplify callers. */ +static svn_error_t * +rs_aligned_seek(rep_state_t *rs, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = rs->sfile->fs->fsap_data; + return svn_error_trace(svn_io_file_aligned_seek(rs->sfile->rfile->file, + ffd->block_size, + buffer_start, offset, + pool)); +} + +/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */ +static svn_error_t* +auto_open_shared_file(shared_file_t *file) +{ + if (file->rfile == NULL) + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&file->rfile, file->fs, + file->revision, file->pool, + file->pool)); + + return SVN_NO_ERROR; +} + +/* Set RS->START to the begin of the representation raw in RS->FILE->FILE, + if that hasn't been done yet. Use POOL for temporary allocations. */ +static svn_error_t* +auto_set_start_offset(rep_state_t *rs, apr_pool_t *pool) +{ + if (rs->start == -1) + { + SVN_ERR(svn_fs_fs__item_offset(&rs->start, rs->sfile->fs, + rs->sfile->rfile, rs->revision, NULL, + rs->item_index, pool)); + rs->start += rs->header_size; + } + + return SVN_NO_ERROR; +} + +/* Set RS->VER depending on what is found in the already open RS->FILE->FILE + if the diff version is still unknown. Use POOL for temporary allocations. + */ +static svn_error_t* +auto_read_diff_version(rep_state_t *rs, apr_pool_t *pool) +{ + if (rs->ver == -1) + { + char buf[4]; + SVN_ERR(rs_aligned_seek(rs, NULL, rs->start, pool)); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->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->current = 4; + } + + return SVN_NO_ERROR; +} + +/* See create_rep_state, which wraps this and adds another error. */ +static svn_error_t * +create_rep_state_body(rep_state_t **rep_state, + svn_fs_fs__rep_header_t **rep_header, + shared_file_t **shared_file, + representation_t *rep, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + rep_state_t *rs = apr_pcalloc(result_pool, sizeof(*rs)); + svn_fs_fs__rep_header_t *rh; + svn_boolean_t is_cached = FALSE; + apr_uint64_t estimated_window_storage; + + /* 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 + * we can re-use the same, already open file object + */ + svn_boolean_t reuse_shared_file + = shared_file && *shared_file && (*shared_file)->rfile + && SVN_IS_VALID_REVNUM((*shared_file)->revision) + && (*shared_file)->revision < ffd->min_unpacked_rev + && rep->revision < ffd->min_unpacked_rev + && ( ((*shared_file)->revision / ffd->max_files_per_dir) + == (rep->revision / ffd->max_files_per_dir)); + + pair_cache_key_t key; + key.revision = rep->revision; + key.second = rep->item_index; + + /* continue constructing RS and RA */ + rs->size = rep->size; + rs->revision = rep->revision; + rs->item_index = rep->item_index; + rs->raw_window_cache = ffd->raw_window_cache; + rs->ver = -1; + rs->start = -1; + + /* Very long files stored as self-delta will produce a huge number of + delta windows. Don't cache them lest we don't thrash the cache. + Since we don't know the depth of the delta chain, let's assume, the + whole contents get rewritten 3 times. + */ + estimated_window_storage + = 4 * ( (rep->expanded_size ? rep->expanded_size : rep->size) + + SVN_DELTA_WINDOW_SIZE); + estimated_window_storage = MIN(estimated_window_storage, APR_SIZE_MAX); + + rs->window_cache = ffd->txdelta_window_cache + && svn_cache__is_cachable(ffd->txdelta_window_cache, + (apr_size_t)estimated_window_storage) + ? ffd->txdelta_window_cache + : NULL; + rs->combined_cache = ffd->combined_window_cache + && svn_cache__is_cachable(ffd->combined_window_cache, + (apr_size_t)estimated_window_storage) + ? ffd->combined_window_cache + : NULL; + + /* cache lookup, i.e. skip reading the rep header if possible */ + if (ffd->rep_header_cache && !svn_fs_fs__id_txn_used(&rep->txn_id)) + SVN_ERR(svn_cache__get((void **) &rh, &is_cached, + ffd->rep_header_cache, &key, result_pool)); + + /* initialize the (shared) FILE member in RS */ + if (reuse_shared_file) + { + rs->sfile = *shared_file; + } + else + { + shared_file_t *file = apr_pcalloc(result_pool, sizeof(*file)); + file->revision = rep->revision; + file->pool = result_pool; + file->fs = fs; + rs->sfile = file; + + /* remember the current file, if suggested by the caller */ + if (shared_file) + *shared_file = file; + } + + /* read rep header, if necessary */ + if (!is_cached) + { + /* ensure file is open and navigate to the start of rep header */ + if (reuse_shared_file) + { + apr_off_t offset; + + /* ... we can re-use the same, already open file object. + * This implies that we don't read from a txn. + */ + rs->sfile = *shared_file; + SVN_ERR(auto_open_shared_file(rs->sfile)); + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rs->sfile->rfile, + rep->revision, NULL, rep->item_index, + scratch_pool)); + SVN_ERR(rs_aligned_seek(rs, NULL, offset, scratch_pool)); + } + else + { + /* otherwise, create a new file object. May or may not be + * an in-txn file. + */ + SVN_ERR(open_and_seek_representation(&rs->sfile->rfile, fs, rep, + result_pool)); + } + + SVN_ERR(svn_fs_fs__read_rep_header(&rh, rs->sfile->rfile->stream, + result_pool, scratch_pool)); + SVN_ERR(get_file_offset(&rs->start, rs, result_pool)); + + /* populate the cache if appropriate */ + if (! svn_fs_fs__id_txn_used(&rep->txn_id)) + { + if (use_block_read(fs)) + SVN_ERR(block_read(NULL, fs, rep->revision, rep->item_index, + rs->sfile->rfile, result_pool, scratch_pool)); + else + if (ffd->rep_header_cache) + SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, + scratch_pool)); + } + } + + /* finalize */ + SVN_ERR(dbg_log_access(fs, rep->revision, rep->item_index, rh, + SVN_FS_FS__ITEM_TYPE_ANY_REP, scratch_pool)); + + rs->header_size = rh->header_size; + *rep_state = rs; + *rep_header = rh; + + if (rh->type == svn_fs_fs__rep_plain) + /* This is a plaintext, so just return the current rep_state. */ + return SVN_NO_ERROR; + + /* skip "SVNx" diff marker */ + rs->current = 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 header in *REP_HEADER, both allocated in POOL. + + When reading multiple reps, i.e. a skip delta chain, you may provide + non-NULL SHARED_FILE. (If SHARED_FILE is not NULL, in the first + call it should be a pointer to NULL.) The function will use this + variable to store the previous call results and tries to re-use it. + This may result in significant savings in I/O for packed files and + number of open file handles. + */ +static svn_error_t * +create_rep_state(rep_state_t **rep_state, + svn_fs_fs__rep_header_t **rep_header, + shared_file_t **shared_file, + representation_t *rep, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = create_rep_state_body(rep_state, rep_header, + shared_file, rep, fs, + result_pool, scratch_pool); + if (err && err->apr_err == SVN_ERR_FS_CORRUPT) + { + fs_fs_data_t *ffd = fs->fsap_data; + const char *rep_str; + + /* ### 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! */ + rep_str = rep + ? svn_fs_fs__unparse_representation + (rep, ffd->format, TRUE, scratch_pool, scratch_pool)->data + : "(null)"; + + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + "Corrupt representation '%s'", + rep_str); + } + /* ### Call representation_string() ? */ + return svn_error_trace(err); +} + +svn_error_t * +svn_fs_fs__check_rep(representation_t *rep, + svn_fs_t *fs, + void **hint, + apr_pool_t *scratch_pool) +{ + if (svn_fs_fs__use_log_addressing(fs)) + { + apr_off_t offset; + svn_fs_fs__p2l_entry_t *entry; + svn_fs_fs__revision_file_t *rev_file = NULL; + + /* Reuse the revision file provided by *HINT, if it is given and + * actually the rev / pack file that we want. */ + svn_revnum_t start_rev = svn_fs_fs__packed_base_rev(fs, rep->revision); + if (hint) + rev_file = *(svn_fs_fs__revision_file_t **)hint; + + if (rev_file == NULL || rev_file->start_revision != start_rev) + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rep->revision, + scratch_pool, scratch_pool)); + + if (hint) + *hint = rev_file; + + /* This will auto-retry if there was a background pack. */ + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, rep->revision, + NULL, rep->item_index, scratch_pool)); + + /* This may fail if there is a background pack operation (can't auto- + retry because the item offset lookup has to be redone as well). */ + SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, rev_file, + rep->revision, offset, + scratch_pool, scratch_pool)); + + if ( entry == NULL + || entry->type < SVN_FS_FS__ITEM_TYPE_FILE_REP + || entry->type > SVN_FS_FS__ITEM_TYPE_DIR_PROPS) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("No representation found at offset %s " + "for item %s in revision %ld"), + apr_off_t_toa(scratch_pool, offset), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + rep->item_index), + rep->revision); + } + else + { + rep_state_t *rs; + svn_fs_fs__rep_header_t *rep_header; + + /* ### Should this be using read_rep_line() directly? */ + SVN_ERR(create_rep_state(&rs, &rep_header, (shared_file_t**)hint, + rep, fs, scratch_pool, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__rep_chain_length(int *chain_length, + int *shard_count, + representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_revnum_t shard_size = ffd->max_files_per_dir + ? ffd->max_files_per_dir + : 1; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_boolean_t is_delta = FALSE; + int count = 0; + int shards = 1; + svn_revnum_t last_shard = rep->revision / shard_size; + + /* Check whether the length of the deltification chain is acceptable. + * Otherwise, shared reps may form a non-skipping delta chain in + * extreme cases. */ + representation_t base_rep = *rep; + + /* re-use open files between iterations */ + shared_file_t *file_hint = NULL; + + svn_fs_fs__rep_header_t *header; + + /* follow the delta chain towards the end but for at most + * MAX_CHAIN_LENGTH steps. */ + do + { + rep_state_t *rep_state; + + svn_pool_clear(iterpool); + + if (base_rep.revision / shard_size != last_shard) + { + last_shard = base_rep.revision / shard_size; + ++shards; + } + + SVN_ERR(create_rep_state_body(&rep_state, + &header, + &file_hint, + &base_rep, + fs, + subpool, + iterpool)); + + base_rep.revision = header->base_revision; + base_rep.item_index = header->base_item_index; + base_rep.size = header->base_length; + svn_fs_fs__id_txn_reset(&base_rep.txn_id); + is_delta = header->type == svn_fs_fs__rep_delta; + + /* Clear it the SUBPOOL once in a while. Doing it too frequently + * renders the FILE_HINT ineffective. Doing too infrequently, may + * leave us with too many open file handles. + * + * Note that this is mostly about efficiency, with larger values + * being more efficient, and any non-zero value is legal here. When + * reading deltified contents, we may keep 10s of rev files open at + * the same time and the system has to cope with that. Thus, the + * limit of 16 chosen below is in the same ballpark. + */ + ++count; + if (count % 16 == 0) + { + file_hint = NULL; + svn_pool_clear(subpool); + } + } + while (is_delta && base_rep.revision); + + *chain_length = count; + *shard_count = shards; + svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +struct rep_read_baton +{ + /* The FS from which we're reading. */ + svn_fs_t *fs; + + /* Representation to read. */ + representation_t rep; + + /* 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. */ + rep_state_t *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. */ + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; + + 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; + + /* If not NULL, attempt to read the data from this cache. + Once that lookup fails, reset it to NULL. */ + svn_cache__t *fulltext_cache; + + /* Bytes delivered from the FULLTEXT_CACHE so far. If the next + lookup fails, we need to skip that much data from the reconstructed + window stream before we continue normal operation. */ + svn_filesize_t fulltext_delivered; + + /* 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; +}; + +/* Set window key in *KEY to address the window described by RS. + For convenience, return the KEY. */ +static window_cache_key_t * +get_window_key(window_cache_key_t *key, rep_state_t *rs) +{ + assert(rs->revision <= APR_UINT32_MAX); + key->revision = (apr_uint32_t)rs->revision; + key->item_index = rs->item_index; + key->chunk_index = rs->chunk_index; + + return key; +} + +/* Implement svn_cache__partial_getter_func_t for raw txdelta windows. + * Parse the raw data and return a svn_fs_fs__txdelta_cached_window_t. + */ +static svn_error_t * +parse_raw_window(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + svn_string_t raw_window; + svn_stream_t *stream; + + /* unparsed and parsed window */ + const svn_fs_fs__raw_cached_window_t *window + = (const svn_fs_fs__raw_cached_window_t *)data; + svn_fs_fs__txdelta_cached_window_t *result + = apr_pcalloc(result_pool, sizeof(*result)); + + /* create a read stream taking the raw window as input */ + raw_window.data = svn_temp_deserializer__ptr(window, + (const void * const *)&window->window.data); + raw_window.len = window->window.len; + stream = svn_stream_from_string(&raw_window, result_pool); + + /* parse it */ + SVN_ERR(svn_txdelta_read_svndiff_window(&result->window, stream, 1, + result_pool)); + + /* complete the window and return it */ + result->end_offset = window->end_offset; + *out = result; + + return SVN_NO_ERROR; +} + + +/* Read the WINDOW_P number CHUNK_INDEX for the representation given in + * 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 will be made + * from RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + * + * If the information could be found, put RS to CHUNK_INDEX. + */ +static svn_error_t * +get_cached_window(svn_txdelta_window_t **window_p, + rep_state_t *rs, + int chunk_index, + svn_boolean_t *is_cached, + apr_pool_t *result_pool, + apr_pool_t *scratch_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; + window_cache_key_t key = { 0 }; + get_window_key(&key, rs); + key.chunk_index = chunk_index; + SVN_ERR(svn_cache__get((void **) &cached_window, + is_cached, + rs->window_cache, + &key, + result_pool)); + + /* If we did not find a parsed txdelta window, we might have a raw + version of it in our cache. If so, read, parse and re-cache it. */ + if (!*is_cached && rs->raw_window_cache) + { + SVN_ERR(svn_cache__get_partial((void **) &cached_window, is_cached, + rs->raw_window_cache, &key, + parse_raw_window, NULL, result_pool)); + if (*is_cached) + SVN_ERR(svn_cache__set(rs->window_cache, &key, cached_window, + scratch_pool)); + } + + /* Return cached information. */ + 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->current = cached_window->end_offset; + rs->chunk_index = chunk_index; + } + } + + return SVN_NO_ERROR; +} + +/* Store the WINDOW read 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, + rep_state_t *rs, + 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; + window_cache_key_t key = {0}; + + cached_window.window = window; + cached_window.end_offset = rs->current; + + /* but key it with the start offset because that is the known state + * when we will look it up */ + SVN_ERR(svn_cache__set(rs->window_cache, + get_window_key(&key, rs), + &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 particular) will be made from POOL. + */ +static svn_error_t * +get_cached_combined_window(svn_stringbuf_t **window_p, + rep_state_t *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 */ + window_cache_key_t key = { 0 }; + return svn_cache__get((void **)window_p, + is_cached, + rs->combined_cache, + get_window_key(&key, rs), + pool); + } + + return SVN_NO_ERROR; +} + +/* Store the WINDOW read 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, + rep_state_t *rs, + 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 */ + window_cache_key_t key = { 0 }; + return svn_cache__set(rs->combined_cache, + get_window_key(&key, rs), + 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, + rep_state_t **src_state, + svn_filesize_t *expanded_size, + svn_fs_t *fs, + representation_t *first_rep, + apr_pool_t *pool) +{ + representation_t rep; + rep_state_t *rs = NULL; + svn_fs_fs__rep_header_t *rep_header; + svn_boolean_t is_cached = FALSE; + shared_file_t *shared_file = NULL; + apr_pool_t *iterpool = svn_pool_create(pool); + + *list = apr_array_make(pool, 1, sizeof(rep_state_t *)); + 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_header, &shared_file, &rep, fs, pool, + iterpool)); + + /* 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_header->type == svn_fs_fs__rep_plain || first_rep->size != 4) + *expanded_size = first_rep->size; + + while (1) + { + svn_pool_clear(iterpool); + + /* fetch state, if that has not been done already */ + if (!rs) + SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, + &rep, fs, pool, iterpool)); + + /* for txn reps, there won't be a cached combined window */ + if (!svn_fs_fs__id_txn_used(&rep.txn_id)) + 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->start = 0; + rs->current = 0; + rs->size = (*window_p)->len; + *src_state = rs; + break; + } + + if (rep_header->type == svn_fs_fs__rep_plain) + { + /* This is a plaintext, so just return the current rep_state. */ + *src_state = rs; + break; + } + + /* Push this rep onto the list. If it's self-compressed, we're done. */ + APR_ARRAY_PUSH(*list, rep_state_t *) = rs; + if (rep_header->type == svn_fs_fs__rep_self_delta) + { + *src_state = NULL; + break; + } + + rep.revision = rep_header->base_revision; + rep.item_index = rep_header->base_item_index; + rep.size = rep_header->base_length; + svn_fs_fs__id_txn_reset(&rep.txn_id); + + rs = NULL; + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* Create a rep_read_baton structure for node revision NODEREV in + filesystem FS and store it in *RB_P. 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->rep = *rep; + 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; + memcpy(b->md5_digest, rep->md5_digest, sizeof(rep->md5_digest)); + 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); + b->fulltext_cache = NULL; + b->fulltext_delivered = 0; + 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. Note that RS->CHUNK_INDEX will be THIS_CHUNK rather + than THIS_CHUNK + 1 when this function returns. */ +static svn_error_t * +read_delta_window(svn_txdelta_window_t **nwin, int this_chunk, + rep_state_t *rs, apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t is_cached; + apr_off_t start_offset; + apr_off_t end_offset; + apr_pool_t *iterpool; + + SVN_ERR_ASSERT(rs->chunk_index <= this_chunk); + + SVN_ERR(dbg_log_access(rs->sfile->fs, rs->revision, rs->item_index, + NULL, SVN_FS_FS__ITEM_TYPE_ANY_REP, scratch_pool)); + + /* Read the next window. But first, try to find it in the cache. */ + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* someone has to actually read the data from file. Open it */ + SVN_ERR(auto_open_shared_file(rs->sfile)); + + /* invoke the 'block-read' feature for non-txn data. + However, don't do that if we are in the middle of some representation, + because the block is unlikely to contain other data. */ + if ( rs->chunk_index == 0 + && SVN_IS_VALID_REVNUM(rs->revision) + && use_block_read(rs->sfile->fs) + && rs->raw_window_cache) + { + SVN_ERR(block_read(NULL, rs->sfile->fs, rs->revision, rs->item_index, + rs->sfile->rfile, result_pool, scratch_pool)); + + /* reading the whole block probably also provided us with the + desired txdelta window */ + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + /* data is still not cached -> we need to read it. + Make sure we have all the necessary info. */ + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); + SVN_ERR(auto_read_diff_version(rs, scratch_pool)); + + /* RS->FILE may be shared between RS instances -> make sure we point + * to the right data. */ + start_offset = rs->start + rs->current; + SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, scratch_pool)); + + /* Skip windows to reach the current chunk if we aren't there yet. */ + iterpool = svn_pool_create(scratch_pool); + while (rs->chunk_index < this_chunk) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_txdelta_skip_svndiff_window(rs->sfile->rfile->file, + rs->ver, iterpool)); + rs->chunk_index++; + SVN_ERR(get_file_offset(&start_offset, rs, iterpool)); + rs->current = start_offset - rs->start; + if (rs->current >= rs->size) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Reading one svndiff window read " + "beyond the end of the " + "representation")); + } + svn_pool_destroy(iterpool); + + /* Actually read the next window. */ + SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->sfile->rfile->stream, + rs->ver, result_pool)); + SVN_ERR(get_file_offset(&end_offset, rs, scratch_pool)); + rs->current = end_offset - rs->start; + if (rs->current > rs->size) + 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) */ + if (SVN_IS_VALID_REVNUM(rs->revision)) + SVN_ERR(set_cached_window(*nwin, rs, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read SIZE bytes from the representation RS and return it in *NWIN. */ +static svn_error_t * +read_plain_window(svn_stringbuf_t **nwin, rep_state_t *rs, + apr_size_t size, apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_off_t offset; + + /* RS->FILE may be shared between RS instances -> make sure we point + * to the right data. */ + SVN_ERR(auto_open_shared_file(rs->sfile)); + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); + + offset = rs->start + rs->current; + SVN_ERR(rs_aligned_seek(rs, NULL, offset, scratch_pool)); + + /* Read the plain data. */ + *nwin = svn_stringbuf_create_ensure(size, result_pool); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, (*nwin)->data, size, + NULL, NULL, result_pool)); + (*nwin)->data[size] = 0; + + /* Update RS. */ + rs->current += (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; + apr_array_header_t *windows; + svn_stringbuf_t *source, *buf = rb->base_window; + rep_state_t *rs; + apr_pool_t *iterpool; + + /* 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 *)); + iterpool = svn_pool_create(rb->pool); + for (i = 0; i < rb->rs_list->nelts; ++i) + { + svn_txdelta_window_t *window; + + svn_pool_clear(iterpool); + + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); + SVN_ERR(read_delta_window(&window, rb->chunk_index, rs, window_pool, + iterpool)); + + 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) + { + svn_txdelta_window_t *window; + + svn_pool_clear(iterpool); + + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); + 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. + 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 && window->src_ops) + SVN_ERR(read_plain_window(&source, rb->src_state, window->sview_len, + pool, iterpool)); + + /* 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->current == rs->size) + && SVN_IS_VALID_REVNUM(rs->revision)) + SVN_ERR(set_cached_combined_window(buf, rs, new_pool)); + + rs->chunk_index++; + + /* Cycle pools so that we only need to hold three windows at a time. */ + svn_pool_destroy(pool); + pool = new_pool; + } + svn_pool_destroy(iterpool); + + 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 from our plain / delta windows + and store them in *BUF. */ +static svn_error_t * +get_contents_from_windows(struct rep_read_baton *rb, + char *buf, + apr_size_t *len) +{ + apr_size_t copy_len, remaining = *len; + char *cur = buf; + rep_state_t *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->current; + 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 + { + apr_off_t offset; + if (((apr_off_t) copy_len) > rs->size - rs->current) + copy_len = (apr_size_t) (rs->size - rs->current); + + SVN_ERR(auto_open_shared_file(rs->sfile)); + SVN_ERR(auto_set_start_offset(rs, rb->pool)); + + offset = rs->start + rs->current; + SVN_ERR(rs_aligned_seek(rs, NULL, offset, rb->pool)); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, cur, + copy_len, NULL, NULL, rb->pool)); + } + + rs->current += 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, rep_state_t *); + if (rs->current == rs->size) + 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 type for get_fulltext_partial. */ +typedef struct fulltext_baton_t +{ + /* Target buffer to write to; of at least LEN bytes. */ + char *buffer; + + /* Offset within the respective fulltext at which we shall start to + copy data into BUFFER. */ + apr_size_t start; + + /* Number of bytes to copy. The actual amount may be less in case + the fulltext is short(er). */ + apr_size_t len; + + /* Number of bytes actually copied into BUFFER. */ + apr_size_t read; +} fulltext_baton_t; + +/* Implement svn_cache__partial_getter_func_t for fulltext caches. + * From the fulltext in DATA, we copy the range specified by the + * fulltext_baton_t* BATON into the buffer provided by that baton. + * OUT and RESULT_POOL are not used. + */ +static svn_error_t * +get_fulltext_partial(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + fulltext_baton_t *fulltext_baton = baton; + + /* We cached the fulltext with an NUL appended to it. */ + apr_size_t fulltext_len = data_len - 1; + + /* Clip the copy range to what the fulltext size allows. */ + apr_size_t start = MIN(fulltext_baton->start, fulltext_len); + fulltext_baton->read = MIN(fulltext_len - start, fulltext_baton->len); + + /* Copy the data to the output buffer and be done. */ + memcpy(fulltext_baton->buffer, (const char *)data + start, + fulltext_baton->read); + + return SVN_NO_ERROR; +} + +/* Find the fulltext specified in BATON in the fulltext cache given + * as well by BATON. If that succeeds, set *CACHED to TRUE and copy + * up to the next *LEN bytes into BUFFER. Set *LEN to the actual + * number of bytes copied. + */ +static svn_error_t * +get_contents_from_fulltext(svn_boolean_t *cached, + struct rep_read_baton *baton, + char *buffer, + apr_size_t *len) +{ + void *dummy; + fulltext_baton_t fulltext_baton; + + SVN_ERR_ASSERT((apr_size_t)baton->fulltext_delivered + == baton->fulltext_delivered); + fulltext_baton.buffer = buffer; + fulltext_baton.start = (apr_size_t)baton->fulltext_delivered; + fulltext_baton.len = *len; + fulltext_baton.read = 0; + + SVN_ERR(svn_cache__get_partial(&dummy, cached, baton->fulltext_cache, + &baton->fulltext_cache_key, + get_fulltext_partial, &fulltext_baton, + baton->pool)); + + if (*cached) + { + baton->fulltext_delivered += fulltext_baton.read; + *len = fulltext_baton.read; + } + + return SVN_NO_ERROR; +} + +/* Determine the optimal size of a string buf that shall receive a + * (full-) text of NEEDED bytes. + * + * The critical point is that those buffers may be very large and + * can cause memory fragmentation. We apply simple heuristics to + * make fragmentation less likely. + */ +static apr_size_t +optimimal_allocation_size(apr_size_t needed) +{ + /* For all allocations, assume some overhead that is shared between + * OS memory managemnt, APR memory management and svn_stringbuf_t. */ + const apr_size_t overhead = 0x400; + apr_size_t optimal; + + /* If an allocation size if safe for other ephemeral buffers, it should + * be safe for ours. */ + if (needed <= SVN__STREAM_CHUNK_SIZE) + return needed; + + /* Paranoia edge case: + * Skip our heuristics if they created arithmetical overflow. + * Beware to make this test work for NEEDED = APR_SIZE_MAX as well! */ + if (needed >= APR_SIZE_MAX / 2 - overhead) + return needed; + + /* As per definition SVN__STREAM_CHUNK_SIZE is a power of two. + * Since we know NEEDED to be larger than that, use it as the + * starting point. + * + * Heuristics: Allocate a power-of-two number of bytes that fit + * NEEDED plus some OVERHEAD. The APR allocator + * will round it up to the next full page size. + */ + optimal = SVN__STREAM_CHUNK_SIZE; + while (optimal - overhead < needed) + optimal *= 2; + + /* This is above or equal to NEEDED. */ + return optimal - overhead; +} + +/* After a fulltext cache lookup failure, we will continue to read from + * combined delta or plain windows. However, we must first make that data + * stream in BATON catch up tho the position LEN already delivered from the + * fulltext cache. Also, we need to store the reconstructed fulltext if we + * want to cache it at the end. + */ +static svn_error_t * +skip_contents(struct rep_read_baton *baton, + svn_filesize_t len) +{ + svn_error_t *err = SVN_NO_ERROR; + + /* Do we want to cache the reconstructed fulltext? */ + if (SVN_IS_VALID_REVNUM(baton->fulltext_cache_key.revision)) + { + char *buffer; + svn_filesize_t to_alloc = MAX(len, baton->len); + + /* This should only be happening if BATON->LEN and LEN are + * cacheable, implying they fit into memory. */ + SVN_ERR_ASSERT((apr_size_t)to_alloc == to_alloc); + + /* Allocate the fulltext buffer. */ + baton->current_fulltext = svn_stringbuf_create_ensure( + optimimal_allocation_size((apr_size_t)to_alloc), + baton->filehandle_pool); + + /* Read LEN bytes from the window stream and store the data + * in the fulltext buffer (will be filled by further reads later). */ + baton->current_fulltext->len = (apr_size_t)len; + baton->current_fulltext->data[(apr_size_t)len] = 0; + + buffer = baton->current_fulltext->data; + while (len > 0 && !err) + { + apr_size_t to_read = (apr_size_t)len; + err = get_contents_from_windows(baton, buffer, &to_read); + len -= to_read; + buffer += to_read; + } + } + else if (len > 0) + { + /* Simply drain LEN bytes from the window stream. */ + apr_pool_t *subpool = subpool = svn_pool_create(baton->pool); + char *buffer = apr_palloc(subpool, SVN__STREAM_CHUNK_SIZE); + + while (len > 0 && !err) + { + apr_size_t to_read = len > SVN__STREAM_CHUNK_SIZE + ? SVN__STREAM_CHUNK_SIZE + : (apr_size_t)len; + + err = get_contents_from_windows(baton, buffer, &to_read); + len -= to_read; + } + + svn_pool_destroy(subpool); + } + + return svn_error_trace(err); +} + +/* 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 data from the fulltext cache for as long as we can. */ + if (rb->fulltext_cache) + { + svn_boolean_t cached; + SVN_ERR(get_contents_from_fulltext(&cached, rb, buf, len)); + if (cached) + return SVN_NO_ERROR; + + /* Cache miss. From now on, we will never read from the fulltext + * cache for this representation anymore. */ + rb->fulltext_cache = NULL; + } + + /* No fulltext cache to help us. We must read from the window stream. */ + if (!rb->rs_list) + { + /* Window stream not initialized, yet. Do it now. */ + SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window, + &rb->src_state, &rb->len, rb->fs, &rb->rep, + rb->filehandle_pool)); + + /* In case we did read from the fulltext cache before, make the + * window stream catch up. Also, initialize the fulltext buffer + * if we want to cache the fulltext at the end. */ + SVN_ERR(skip_contents(rb, rb->fulltext_delivered)); + } + + /* Get the next block of data. */ + SVN_ERR(get_contents_from_windows(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; + svn_checksum_t expected; + expected.kind = svn_checksum_md5; + expected.digest = rb->md5_digest; + + rb->checksum_finalized = TRUE; + SVN_ERR(svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx, + rb->pool)); + if (!svn_checksum_match(md5_checksum, &expected)) + return svn_error_create(SVN_ERR_FS_CORRUPT, + svn_checksum_mismatch_err(&expected, 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; +} + +svn_error_t * +svn_fs_fs__get_contents(svn_stream_t **contents_p, + svn_fs_t *fs, + representation_t *rep, + svn_boolean_t cache_fulltext, + apr_pool_t *pool) +{ + if (! rep) + { + *contents_p = svn_stream_empty(pool); + } + else + { + fs_fs_data_t *ffd = fs->fsap_data; + svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size; + struct rep_read_baton *rb; + + pair_cache_key_t fulltext_cache_key = { 0 }; + fulltext_cache_key.revision = rep->revision; + fulltext_cache_key.second = rep->item_index; + + /* Initialize the reader baton. Some members may added lazily + * while reading from the stream */ + SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, pool)); + + /* Make the stream attempt fulltext cache lookups if the fulltext + * is cacheable. If it is not, then also don't try to buffer and + * cache it. */ + if (ffd->fulltext_cache && cache_fulltext + && SVN_IS_VALID_REVNUM(rep->revision) + && fulltext_size_is_cachable(ffd, len)) + { + rb->fulltext_cache = ffd->fulltext_cache; + } + else + { + /* This will also prevent the reconstructed fulltext from being + put into the cache. */ + rb->fulltext_cache_key.revision = SVN_INVALID_REVNUM; + } + + *contents_p = svn_stream_create(rb, pool); + svn_stream_set_read2(*contents_p, NULL /* only full read support */, + rep_read_contents); + svn_stream_set_close(*contents_p, rep_read_contents_close); + } + + 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->item_index; + 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; +} + + +/* Baton used when reading delta windows. */ +struct delta_read_baton +{ + rep_state_t *rs; + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; +}; + +/* 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; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + *window = NULL; + if (drb->rs->current < drb->rs->size) + { + SVN_ERR(read_delta_window(window, drb->rs->chunk_index, drb->rs, pool, + scratch_pool)); + drb->rs->chunk_index++; + } + + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +/* 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; + return drb->md5_digest; +} + +/* Return a txdelta stream for on-disk representation REP_STATE + * of TARGET. Allocate the result in POOL. + */ +static svn_txdelta_stream_t * +get_storaged_delta_stream(rep_state_t *rep_state, + node_revision_t *target, + apr_pool_t *pool) +{ + /* Create the delta read baton. */ + struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb)); + drb->rs = rep_state; + memcpy(drb->md5_digest, target->data_rep->md5_digest, + sizeof(drb->md5_digest)); + return svn_txdelta_stream_create(drb, delta_read_next_window, + delta_read_md5_digest, pool); +} + +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; + rep_state_t *rep_state; + svn_fs_fs__rep_header_t *rep_header; + fs_fs_data_t *ffd = fs->fsap_data; + + /* Try a shortcut: if the target is stored as a delta against the source, + then just use that delta. However, prefer using the fulltext cache + whenever that is available. */ + if (target->data_rep && (source || ! ffd->fulltext_cache)) + { + /* Read target's base rep if any. */ + SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL, + target->data_rep, fs, pool, pool)); + + if (source && source->data_rep && target->data_rep) + { + /* 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_header->type == svn_fs_fs__rep_delta + && rep_header->base_revision == source->data_rep->revision + && rep_header->base_item_index == source->data_rep->item_index) + { + *stream_p = get_storaged_delta_stream(rep_state, target, pool); + return SVN_NO_ERROR; + } + } + else if (!source) + { + /* We want a self-delta. There is a fair chance that TARGET got + added in this revision and is already stored in the requested + format. */ + if (rep_header->type == svn_fs_fs__rep_self_delta) + { + *stream_p = get_storaged_delta_stream(rep_state, target, pool); + return SVN_NO_ERROR; + } + } + + /* Don't keep file handles open for longer than necessary. */ + if (rep_state->sfile->rfile) + { + SVN_ERR(svn_fs_fs__close_revision_file(rep_state->sfile->rfile)); + rep_state->sfile->rfile = NULL; + } + } + + /* Read both fulltexts and construct a delta. */ + if (source) + SVN_ERR(svn_fs_fs__get_contents(&source_stream, fs, source->data_rep, + TRUE, pool)); + else + source_stream = svn_stream_empty(pool); + SVN_ERR(svn_fs_fs__get_contents(&target_stream, fs, target->data_rep, + TRUE, 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; +} + +/* Return TRUE when all svn_fs_dirent_t* in ENTRIES are already sorted + by their respective name. */ +static svn_boolean_t +sorted(apr_array_header_t *entries) +{ + int i; + + const svn_fs_dirent_t * const *dirents = (const void *)entries->elts; + for (i = 0; i < entries->nelts-1; ++i) + if (strcmp(dirents[i]->name, dirents[i+1]->name) > 0) + return FALSE; + + return TRUE; +} + +/* Compare the names of the two dirents given in **A and **B. */ +static int +compare_dirents(const void *a, const void *b) +{ + const svn_fs_dirent_t *lhs = *((const svn_fs_dirent_t * const *) a); + const svn_fs_dirent_t *rhs = *((const svn_fs_dirent_t * const *) b); + + return strcmp(lhs->name, rhs->name); +} + +/* Compare the name of the dirents given in **A with the C string in *B. */ +static int +compare_dirent_name(const void *a, const void *b) +{ + const svn_fs_dirent_t *lhs = *((const svn_fs_dirent_t * const *) a); + const char *rhs = b; + + return strcmp(lhs->name, rhs); +} + +/* Into ENTRIES, read all directories entries from the key-value text in + * STREAM. If INCREMENTAL is TRUE, read until the end of the STREAM and + * update the data. ID is provided for nicer error messages. + */ +static svn_error_t * +read_dir_entries(apr_array_header_t *entries, + svn_stream_t *stream, + svn_boolean_t incremental, + const svn_fs_id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *hash = incremental ? svn_hash__make(scratch_pool) : NULL; + const char *terminator = SVN_HASH_TERMINATOR; + + /* Read until the terminator (non-incremental) or the end of STREAM + (incremental mode). In the latter mode, we use a temporary HASH + to make updating and removing entries cheaper. */ + while (1) + { + svn_hash__entry_t entry; + svn_fs_dirent_t *dirent; + char *str; + + svn_pool_clear(iterpool); + SVN_ERR(svn_hash__read_entry(&entry, stream, terminator, + incremental, iterpool)); + + /* End of directory? */ + if (entry.key == NULL) + { + /* In incremental mode, we skip the terminator and read the + increments following it until the end of the stream. */ + if (incremental && terminator) + terminator = NULL; + else + break; + } + + /* Deleted entry? */ + if (entry.val == NULL) + { + /* We must be in incremental mode */ + assert(hash); + apr_hash_set(hash, entry.key, entry.keylen, NULL); + continue; + } + + /* Add a new directory entry. */ + dirent = apr_pcalloc(result_pool, sizeof(*dirent)); + dirent->name = apr_pstrmemdup(result_pool, entry.key, entry.keylen); + + str = svn_cstring_tokenize(" ", &entry.val); + if (str == NULL) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_fs__id_unparse(id, scratch_pool)->data); + + if (strcmp(str, SVN_FS_FS__KIND_FILE) == 0) + { + dirent->kind = svn_node_file; + } + else if (strcmp(str, SVN_FS_FS__KIND_DIR) == 0) + { + dirent->kind = svn_node_dir; + } + else + { + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_fs__id_unparse(id, scratch_pool)->data); + } + + str = svn_cstring_tokenize(" ", &entry.val); + if (str == NULL) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_fs__id_unparse(id, scratch_pool)->data); + + SVN_ERR(svn_fs_fs__id_parse(&dirent->id, str, result_pool)); + + /* In incremental mode, update the hash; otherwise, write to the + * final array. Be sure to use hash keys that survive this iteration. + */ + if (incremental) + apr_hash_set(hash, dirent->name, entry.keylen, dirent); + else + APR_ARRAY_PUSH(entries, svn_fs_dirent_t *) = dirent; + } + + /* Convert container to a sorted array. */ + if (incremental) + { + apr_hash_index_t *hi; + for (hi = apr_hash_first(iterpool, hash); hi; hi = apr_hash_next(hi)) + APR_ARRAY_PUSH(entries, svn_fs_dirent_t *) = apr_hash_this_val(hi); + } + + if (!sorted(entries)) + svn_sort__array(entries, compare_dirents); + + svn_pool_destroy(iterpool); + + 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_array_header_t **entries, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *contents; + + *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *)); + if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id)) + { + const char *filename + = svn_fs_fs__path_txn_node_children(fs, noderev->id, scratch_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, scratch_pool, + scratch_pool)); + SVN_ERR(read_dir_entries(*entries, contents, TRUE, noderev->id, + result_pool, scratch_pool)); + SVN_ERR(svn_stream_close(contents)); + } + else if (noderev->data_rep) + { + /* Undeltify content before parsing it. Otherwise, we could only + * parse it byte-by-byte. + */ + 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; + + /* The representation is immutable. Read it normally. */ + SVN_ERR(svn_fs_fs__get_contents(&contents, fs, noderev->data_rep, + FALSE, scratch_pool)); + SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, scratch_pool)); + SVN_ERR(svn_stream_close(contents)); + + /* de-serialize hash */ + contents = svn_stream_from_stringbuf(text, scratch_pool); + SVN_ERR(read_dir_entries(*entries, contents, FALSE, noderev->id, + result_pool, scratch_pool)); + } + + return SVN_NO_ERROR; +} + + +/* Return the cache object in FS responsible to storing the directory the + * NODEREV plus the corresponding *KEY. If no cache exists, return NULL. + * PAIR_KEY must point to some key struct, which does not need to be + * initialized. We use it to avoid dynamic allocation. + */ +static svn_cache__t * +locate_dir_cache(svn_fs_t *fs, + const void **key, + pair_cache_key_t *pair_key, + node_revision_t *noderev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + if (svn_fs_fs__id_is_txn(noderev->id)) + { + /* data in txns requires the expensive fs_id-based addressing mode */ + *key = svn_fs_fs__id_unparse(noderev->id, pool)->data; + return ffd->txn_dir_cache; + } + else + { + /* committed data can use simple rev,item pairs */ + if (noderev->data_rep) + { + pair_key->revision = noderev->data_rep->revision; + pair_key->second = noderev->data_rep->item_index; + *key = pair_key; + } + else + { + /* no data rep -> empty directory. + A NULL key causes a cache miss. */ + *key = NULL; + } + + return ffd->dir_cache; + } +} + +svn_error_t * +svn_fs_fs__rep_contents_dir(apr_array_header_t **entries_p, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + pair_cache_key_t pair_key = { 0 }; + const void *key; + + /* find the cache we may use */ + svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev, + scratch_pool); + if (cache) + { + svn_boolean_t found; + + SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, key, + result_pool)); + if (found) + return SVN_NO_ERROR; + } + + /* Read in the directory contents. */ + SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool, + scratch_pool)); + + /* Update the cache, if we are to use one. + * + * Don't even attempt to serialize very large directories; it would cause + * an unnecessary memory allocation peak. 150 bytes/entry is about right. + */ + if (cache && svn_cache__is_cachable(cache, 150 * (*entries_p)->nelts)) + SVN_ERR(svn_cache__set(cache, key, *entries_p, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_fs_dirent_t * +svn_fs_fs__find_dir_entry(apr_array_header_t *entries, + const char *name, + int *hint) +{ + svn_fs_dirent_t **result + = svn_sort__array_lookup(entries, name, hint, compare_dirent_name); + return result ? *result : NULL; +} + +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 */ + pair_cache_key_t pair_key = { 0 }; + const void *key; + svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev, + scratch_pool); + if (cache) + { + /* Cache lookup. */ + SVN_ERR(svn_cache__get_partial((void **)dirent, + &found, + cache, + key, + 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_array_header_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, scratch_pool)); + + /* find desired entry and return a copy in POOL, if found */ + entry = svn_fs_fs__find_dir_entry(entries, name, NULL); + if (entry) + { + 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 && svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) + { + svn_error_t *err; + const char *filename + = svn_fs_fs__path_txn_node_props(fs, noderev->id, pool); + proplist = apr_hash_make(pool); + + SVN_ERR(svn_stream_open_readonly(&stream, filename, pool, pool)); + err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + svn_error_clear(svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed property list for node-revision '%s' in '%s'"), + id_str->data, filename); + } + SVN_ERR(svn_stream_close(stream)); + } + else if (noderev->prop_rep) + { + svn_error_t *err; + 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->item_index; + 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(svn_fs_fs__get_contents(&stream, fs, noderev->prop_rep, FALSE, + pool)); + err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + svn_error_clear(svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed property list for node-revision '%s'"), + id_str->data); + } + 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__get_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + apr_off_t changes_offset = SVN_FS_FS__ITEM_INDEX_CHANGES; + svn_fs_fs__revision_file_t *revision_file; + svn_boolean_t found; + fs_fs_data_t *ffd = fs->fsap_data; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + + /* try cache lookup first */ + + if (ffd->changes_cache) + { + SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache, + &rev, result_pool)); + } + else + { + found = FALSE; + } + + if (!found) + { + /* read changes from revision file */ + + SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool)); + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, + scratch_pool, scratch_pool)); + + if (use_block_read(fs)) + { + /* 'block-read' will also provide us with the desired data */ + SVN_ERR(block_read((void **)changes, fs, + rev, SVN_FS_FS__ITEM_INDEX_CHANGES, + revision_file, result_pool, scratch_pool)); + } + else + { + /* Addressing is very different for old formats + * (needs to read the revision trailer). */ + if (svn_fs_fs__use_log_addressing(fs)) + SVN_ERR(svn_fs_fs__item_offset(&changes_offset, fs, + revision_file, rev, NULL, + SVN_FS_FS__ITEM_INDEX_CHANGES, + scratch_pool)); + else + SVN_ERR(get_root_changes_offset(NULL, &changes_offset, + revision_file, fs, rev, + scratch_pool)); + + /* Actual reading and parsing are the same, though. */ + SVN_ERR(aligned_seek(fs, revision_file->file, NULL, changes_offset, + scratch_pool)); + SVN_ERR(svn_fs_fs__read_changes(changes, revision_file->stream, + result_pool, scratch_pool)); + + /* cache for future reference */ + + if (ffd->changes_cache) + { + /* Guesstimate for the size of the in-cache representation. */ + apr_size_t estimated_size = (apr_size_t)250 * (*changes)->nelts; + + /* Don't even serialize data that probably won't fit into the + * cache. This often implies that either CHANGES is very + * large, memory is scarce or both. Having a huge temporary + * copy would not be a good thing in either case. */ + if (svn_cache__is_cachable(ffd->changes_cache, estimated_size)) + SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, + scratch_pool)); + } + } + + SVN_ERR(svn_fs_fs__close_revision_file(revision_file)); + } + + SVN_ERR(dbg_log_access(fs, rev, changes_offset, *changes, + SVN_FS_FS__ITEM_TYPE_CHANGES, scratch_pool)); + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + +/* Inialize the representation read state RS for the given REP_HEADER and + * p2l index ENTRY. If not NULL, assign FILE and STREAM to RS. + * Use RESULT_POOL for allocations. + */ +static svn_error_t * +init_rep_state(rep_state_t *rs, + svn_fs_fs__rep_header_t *rep_header, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *file, + svn_fs_fs__p2l_entry_t* entry, + apr_pool_t *result_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + shared_file_t *shared_file = apr_pcalloc(result_pool, sizeof(*shared_file)); + + /* this function does not apply to representation containers */ + SVN_ERR_ASSERT(entry->type >= SVN_FS_FS__ITEM_TYPE_FILE_REP + && entry->type <= SVN_FS_FS__ITEM_TYPE_DIR_PROPS); + + shared_file->rfile = file; + shared_file->fs = fs; + shared_file->revision = entry->item.revision; + shared_file->pool = result_pool; + + rs->sfile = shared_file; + rs->revision = entry->item.revision; + rs->item_index = entry->item.number; + rs->header_size = rep_header->header_size; + rs->start = entry->offset + rs->header_size; + rs->current = rep_header->type == svn_fs_fs__rep_plain ? 0 : 4; + rs->size = entry->size - rep_header->header_size - 7; + rs->ver = 1; + rs->chunk_index = 0; + rs->raw_window_cache = ffd->raw_window_cache; + rs->window_cache = ffd->txdelta_window_cache; + rs->combined_cache = ffd->combined_window_cache; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t for txdelta windows. + * Instead of the whole window data, return only END_OFFSET member. + */ +static svn_error_t * +get_txdelta_window_end(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + const svn_fs_fs__txdelta_cached_window_t *window + = (const svn_fs_fs__txdelta_cached_window_t *)data; + *(apr_off_t*)out = window->end_offset; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t for raw windows. + * Instead of the whole window data, return only END_OFFSET member. + */ +static svn_error_t * +get_raw_window_end(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + const svn_fs_fs__raw_cached_window_t *window + = (const svn_fs_fs__raw_cached_window_t *)data; + *(apr_off_t*)out = window->end_offset; + + return SVN_NO_ERROR; +} + +/* Walk through all windows in the representation addressed by RS in FS + * (excluding the delta bases) and put those not already cached into the + * window caches. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. Use POOL for temporary allocations. + * + * This function requires RS->RAW_WINDOW_CACHE and RS->WINDOW_CACHE to + * be non-NULL. + */ +static svn_error_t * +cache_windows(svn_fs_t *fs, + rep_state_t *rs, + apr_off_t max_offset, + apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); + while (rs->current < rs->size) + { + apr_off_t end_offset; + svn_boolean_t found = FALSE; + window_cache_key_t key = { 0 }; + + svn_pool_clear(iterpool); + + if (max_offset != -1 && rs->start + rs->current >= max_offset) + { + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; + } + + /* We don't need to read the data again if it is already in cache. + * It might be cached as either raw or parsed window. + */ + SVN_ERR(svn_cache__get_partial((void **) &end_offset, &found, + rs->raw_window_cache, + get_window_key(&key, rs), + get_raw_window_end, NULL, + iterpool)); + if (! found) + SVN_ERR(svn_cache__get_partial((void **) &end_offset, &found, + rs->window_cache, &key, + get_txdelta_window_end, NULL, + iterpool)); + + if (found) + { + rs->current = end_offset; + } + else + { + /* Read, decode and cache the window. */ + svn_fs_fs__raw_cached_window_t window; + apr_off_t start_offset = rs->start + rs->current; + apr_size_t window_len; + char *buf; + + /* navigate to the current window */ + SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, iterpool)); + SVN_ERR(svn_txdelta__read_raw_window_len(&window_len, + rs->sfile->rfile->stream, + iterpool)); + + /* Read the raw window. */ + buf = apr_palloc(iterpool, window_len + 1); + SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, iterpool)); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, buf, + window_len, NULL, NULL, iterpool)); + buf[window_len] = 0; + + /* update relative offset in representation */ + rs->current += window_len; + + /* Construct the cachable raw window object. */ + window.end_offset = rs->current; + window.window.len = window_len; + window.window.data = buf; + + /* cache the window now */ + SVN_ERR(svn_cache__set(rs->raw_window_cache, &key, &window, + iterpool)); + } + + if (rs->current > rs->size) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Reading one svndiff window read beyond " + "the end of the representation")); + + rs->chunk_index++; + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Read all txdelta / plain windows following REP_HEADER in FS as described + * by ENTRY. Read the data from the already open FILE and the wrapping + * STREAM object. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. Use SCRATCH_POOL for temporary allocations. + * If caching is not enabled, this is a no-op. + */ +static svn_error_t * +block_read_windows(svn_fs_fs__rep_header_t *rep_header, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t* entry, + apr_off_t max_offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + rep_state_t rs = { 0 }; + apr_off_t offset; + window_cache_key_t key = { 0 }; + + if ( (rep_header->type != svn_fs_fs__rep_plain + && (!ffd->txdelta_window_cache || !ffd->raw_window_cache)) + || (rep_header->type == svn_fs_fs__rep_plain + && !ffd->combined_window_cache)) + return SVN_NO_ERROR; + + SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, + result_pool)); + + /* RS->FILE may be shared between RS instances -> make sure we point + * to the right data. */ + offset = rs.start + rs.current; + if (rep_header->type == svn_fs_fs__rep_plain) + { + svn_stringbuf_t *plaintext; + svn_boolean_t is_cached; + + /* already in cache? */ + SVN_ERR(svn_cache__has_key(&is_cached, rs.combined_cache, + get_window_key(&key, &rs), + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* for larger reps, the header may have crossed a block boundary. + * make sure we still read blocks properly aligned, i.e. don't use + * plain seek here. */ + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, scratch_pool)); + + plaintext = svn_stringbuf_create_ensure(rs.size, result_pool); + SVN_ERR(svn_io_file_read_full2(rev_file->file, plaintext->data, + rs.size, &plaintext->len, NULL, + result_pool)); + plaintext->data[plaintext->len] = 0; + rs.current += rs.size; + + SVN_ERR(set_cached_combined_window(plaintext, &rs, scratch_pool)); + } + else + { + SVN_ERR(cache_windows(fs, &rs, max_offset, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Try to get the representation header identified by KEY from FS's cache. + * If it has not been cached, read it from the current position in STREAM + * and put it into the cache (if caching has been enabled for rep headers). + * Return the result in *REP_HEADER. Use POOL for allocations. + */ +static svn_error_t * +read_rep_header(svn_fs_fs__rep_header_t **rep_header, + svn_fs_t *fs, + svn_stream_t *stream, + pair_cache_key_t *key, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + + if (ffd->rep_header_cache) + { + SVN_ERR(svn_cache__get((void**)rep_header, &is_cached, + ffd->rep_header_cache, key, + result_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(svn_fs_fs__read_rep_header(rep_header, stream, result_pool, + scratch_pool)); + + if (ffd->rep_header_cache) + SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Fetch the representation data (header, txdelta / plain windows) + * addressed by ENTRY->ITEM in FS and cache it if caches are enabled. + * Read the data from the already open FILE and the wrapping + * STREAM object. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +block_read_contents(svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t* entry, + apr_off_t max_offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + pair_cache_key_t header_key = { 0 }; + svn_fs_fs__rep_header_t *rep_header; + + header_key.revision = (apr_int32_t)entry->item.revision; + header_key.second = entry->item.number; + + SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key, + result_pool, scratch_pool)); + SVN_ERR(block_read_windows(rep_header, fs, rev_file, entry, max_offset, + result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* For the given REV_FILE in FS, in *STREAM return a stream covering the + * item specified by ENTRY. Also, verify the item's content by low-level + * checksum. Allocate the result in POOL. + */ +static svn_error_t * +read_item(svn_stream_t **stream, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t* entry, + apr_pool_t *pool) +{ + apr_uint32_t digest; + svn_checksum_t *expected, *actual; + apr_uint32_t plain_digest; + + /* Read item into string buffer. */ + svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool); + text->len = entry->size; + text->data[text->len] = 0; + SVN_ERR(svn_io_file_read_full2(rev_file->file, text->data, text->len, + NULL, NULL, pool)); + + /* Return (construct, calculate) stream and checksum. */ + *stream = svn_stream_from_stringbuf(text, pool); + digest = svn__fnv1a_32x4(text->data, text->len); + + /* Checksums will match most of the time. */ + if (entry->fnv1_checksum == digest) + return SVN_NO_ERROR; + + /* Construct proper checksum objects from their digests to allow for + * nice error messages. */ + plain_digest = htonl(entry->fnv1_checksum); + expected = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + plain_digest = htonl(digest); + actual = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + + /* Construct the full error message with all the info we have. */ + return svn_checksum_mismatch_err(expected, actual, pool, + _("Low-level checksum mismatch while reading\n" + "%s bytes of meta data at offset %s " + "for item %s in revision %ld"), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->size), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->offset), + apr_psprintf(pool, "%" APR_UINT64_T_FMT, entry->item.number), + entry->item.revision); +} + +/* If not already cached or if MUST_READ is set, read the changed paths + * list addressed by ENTRY in FS and retúrn it in *CHANGES. Cache the + * result if caching is enabled. Read the data from the already open + * FILE and wrapping FILE_STREAM. Use POOL for allocations. + */ +static svn_error_t * +block_read_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_stream_t *stream; + if (!must_read && !ffd->changes_cache) + return SVN_NO_ERROR; + + /* already in cache? */ + if (!must_read && ffd->changes_cache) + { + svn_boolean_t is_cached; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache, + &entry->item.revision, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read changes from revision file */ + SVN_ERR(svn_fs_fs__read_changes(changes, stream, result_pool, + scratch_pool)); + + /* cache for future reference */ + if (ffd->changes_cache) + SVN_ERR(svn_cache__set(ffd->changes_cache, &entry->item.revision, + *changes, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* If not already cached or if MUST_READ is set, read the nod revision + * addressed by ENTRY in FS and retúrn it in *NODEREV_P. Cache the + * result if caching is enabled. Read the data from the already open + * FILE and wrapping FILE_STREAM. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +block_read_noderev(node_revision_t **noderev_p, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_stream_t *stream; + + pair_cache_key_t key = { 0 }; + key.revision = entry->item.revision; + key.second = entry->item.number; + + if (!must_read && !ffd->node_revision_cache) + return SVN_NO_ERROR; + + /* already in cache? */ + if (!must_read && ffd->node_revision_cache) + { + svn_boolean_t is_cached; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->node_revision_cache, + &key, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read node rev from revision file */ + SVN_ERR(svn_fs_fs__read_noderev(noderev_p, stream, + result_pool, scratch_pool)); + + /* Workaround issue #4031: is-fresh-txn-root in revision files. */ + (*noderev_p)->is_fresh_txn_root = FALSE; + + if (ffd->node_revision_cache) + SVN_ERR(svn_cache__set(ffd->node_revision_cache, &key, *noderev_p, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the whole (e.g. 64kB) block containing ITEM_INDEX of REVISION in FS + * and put all data into cache. If necessary and depending on heuristics, + * neighboring blocks may also get read. The data is being read from + * already open REVISION_FILE, which must be the correct rev / pack file + * w.r.t. REVISION. + * + * For noderevs and changed path lists, the item fetched can be allocated + * RESULT_POOL and returned in *RESULT. Otherwise, RESULT must be NULL. + */ +static svn_error_t * +block_read(void **result, + svn_fs_t *fs, + svn_revnum_t revision, + apr_uint64_t item_index, + svn_fs_fs__revision_file_t *revision_file, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_off_t offset, wanted_offset = 0; + apr_off_t block_start = 0; + apr_array_header_t *entries; + int run_count = 0; + int i; + apr_pool_t *iterpool; + + /* Block read is an optional feature. If the caller does not want anything + * specific we may not have to read anything. */ + if (!result) + return SVN_NO_ERROR; + + iterpool = svn_pool_create(scratch_pool); + + /* don't try this on transaction protorev files */ + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); + + /* index lookup: find the OFFSET of the item we *must* read plus (in the + * "do-while" block) the list of items in the same block. */ + SVN_ERR(svn_fs_fs__item_offset(&wanted_offset, fs, revision_file, + revision, NULL, item_index, iterpool)); + + offset = wanted_offset; + + /* Heuristics: + * + * Read this block. If the last item crosses the block boundary, read + * the next block but stop there. Because cross-boundary items cause + * blocks to be read twice, this heuristics will limit this effect to + * approx. 50% of blocks, probably less, while providing a sensible + * amount of read-ahead. + */ + do + { + /* fetch list of items in the block surrounding OFFSET */ + block_start = offset - (offset % ffd->block_size); + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, revision_file, + revision, block_start, + ffd->block_size, scratch_pool, + scratch_pool)); + + SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset, + iterpool)); + + /* read all items from the block */ + for (i = 0; i < entries->nelts; ++i) + { + svn_boolean_t is_result, is_wanted; + apr_pool_t *pool; + svn_fs_fs__p2l_entry_t* entry; + + svn_pool_clear(iterpool); + + /* skip empty sections */ + entry = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + continue; + + /* the item / container we were looking for? */ + is_wanted = entry->offset == wanted_offset + && entry->item.revision == revision + && entry->item.number == item_index; + is_result = result && is_wanted; + + /* select the pool that we want the item to be allocated in */ + pool = is_result ? result_pool : iterpool; + + /* handle all items that start within this block and are relatively + * small (i.e. < block size). Always read the item we need to return. + */ + if (is_result || ( entry->offset >= block_start + && entry->size < ffd->block_size)) + { + void *item = NULL; + SVN_ERR(svn_io_file_seek(revision_file->file, APR_SET, + &entry->offset, iterpool)); + switch (entry->type) + { + case SVN_FS_FS__ITEM_TYPE_FILE_REP: + case SVN_FS_FS__ITEM_TYPE_DIR_REP: + case SVN_FS_FS__ITEM_TYPE_FILE_PROPS: + case SVN_FS_FS__ITEM_TYPE_DIR_PROPS: + SVN_ERR(block_read_contents(fs, revision_file, entry, + is_wanted + ? -1 + : block_start + ffd->block_size, + pool, iterpool)); + break; + + case SVN_FS_FS__ITEM_TYPE_NODEREV: + if (ffd->node_revision_cache || is_result) + SVN_ERR(block_read_noderev((node_revision_t **)&item, + fs, revision_file, + entry, is_result, pool, + iterpool)); + break; + + case SVN_FS_FS__ITEM_TYPE_CHANGES: + SVN_ERR(block_read_changes((apr_array_header_t **)&item, + fs, revision_file, + entry, is_result, + pool, iterpool)); + break; + + default: + break; + } + + if (is_result) + *result = item; + + /* if we crossed a block boundary, read the remainder of + * the last block as well */ + offset = entry->offset + entry->size; + if (offset > block_start + ffd->block_size) + ++run_count; + } + } + + } + while(run_count++ == 1); /* can only be true once and only if a block + * boundary got crossed */ + + /* if the caller requested a result, we must have provided one by now */ + assert(!result || *result); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/cached_data.h b/contrib/subversion/subversion/libsvn_fs_fs/cached_data.h new file mode 100644 index 000000000..9bdc51c3d --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/cached_data.h @@ -0,0 +1,166 @@ +/* cached_data.h --- cached (read) access to FSFS data + * + * ==================================================================== + * 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_FS__CACHED_DATA_H +#define SVN_LIBSVN_FS__CACHED_DATA_H + +#include "svn_pools.h" +#include "svn_fs.h" + +#include "fs.h" + + + +/* Set *NODEREV_P to the node-revision for the node ID in FS. Do any + allocations in POOL. */ +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 *result_pool, + apr_pool_t *scratch_pool); + +/* Set *ROOT_ID to the node-id for the root of revision REV in + filesystem FS. Do any allocations in POOL. */ +svn_error_t * +svn_fs_fs__rev_get_root(svn_fs_id_t **root_id, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Verify that representation REP in FS can be accessed. Successive calls + to this function should pass a non-NULL value to HINT. In that case, + many file open / close operations can be eliminated. + Do any allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_fs__check_rep(representation_t *rep, + svn_fs_t *fs, + void **hint, + apr_pool_t *scratch_pool); + +/* Follow the representation delta chain in FS starting with REP. The + number of reps (including REP) in the chain will be returned in + *CHAIN_LENGTH. *SHARD_COUNT will be set to the number of shards + accessed. Do any allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_fs__rep_chain_length(int *chain_length, + int *shard_count, + representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Set *CONTENTS to be a readable svn_stream_t that receives the text + representation REP as seen in filesystem FS. If CACHE_FULLTEXT is + not set, bypass fulltext cache lookup for this rep and don't put the + reconstructed fulltext into cache. + Use POOL for allocations. */ +svn_error_t * +svn_fs_fs__get_contents(svn_stream_t **contents_p, + svn_fs_t *fs, + representation_t *rep, + svn_boolean_t cache_fulltext, + apr_pool_t *pool); + +/* Attempt to fetch the text representation of node-revision NODEREV as + seen in filesystem FS and pass it along with the BATON to the PROCESSOR. + Set *SUCCESS only of the data could be provided and the processing + had been called. + Use POOL for all allocations. + */ +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); + +/* Set *STREAM_P to a delta stream turning the contents of the file SOURCE into + the contents of the file TARGET, allocated in POOL. + If SOURCE is null, the empty string will be used. */ +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); + +/* Set *ENTRIES to an apr_array_header_t of dirent structs that contain + the directory entries of node-revision NODEREV in filesystem FS. The + returned table is allocated in RESULT_POOL and entries are sorted + lexicographically. SCRATCH_POOL is used for temporary allocations. */ +svn_error_t * +svn_fs_fs__rep_contents_dir(apr_array_header_t **entries_p, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return the directory entry from ENTRIES that matches NAME. If no such + entry exists, return NULL. If HINT is not NULL, set *HINT to the array + index of the entry returned. Successive calls in a linear scan scenario + will be faster called with the same HINT variable. */ +svn_fs_dirent_t * +svn_fs_fs__find_dir_entry(apr_array_header_t *entries, + const char *name, + int *hint); + +/* Set *DIRENT to the entry identified by NAME in the directory given + by NODEREV in filesystem FS. If no such entry exits, *DIRENT will + be NULL. The returned object is allocated in RESULT_POOL; SCRATCH_POOL + used for temporary allocations. */ +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); + +/* Set *PROPLIST to be an apr_hash_t containing the property list of + node-revision NODEREV as seen in filesystem FS. Use POOL for + temporary allocations. */ +svn_error_t * +svn_fs_fs__get_proplist(apr_hash_t **proplist, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *pool); + +/* Set *HAS_PROPS to TRUE if NODEREV has properties in FS, otherwise + to FALSE. Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__has_props(svn_boolean_t *has_props, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *scratch_pool); + +/* Fetch the list of change in revision REV in FS and return it in *CHANGES. + * Allocate the result in POOL. + */ +svn_error_t * +svn_fs_fs__get_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/caching.c b/contrib/subversion/subversion/libsvn_fs_fs/caching.c index 42898cb6e..6775fab00 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/caching.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/caching.c @@ -25,6 +25,7 @@ #include "id.h" #include "dag.h" #include "tree.h" +#include "index.h" #include "temp_serializer.h" #include "../libsvn_fs/fs-loader.h" @@ -65,32 +66,18 @@ normalize_key_part(const char *original, 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. +/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS 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, +read_config(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)); - /* 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 @@ -116,7 +103,8 @@ read_config(svn_memcache_t **memcache_p, *cache_txdeltas = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, - FALSE); + TRUE); + /* by default, cache fulltexts. * Most SVN tools care about reconstructed file content. * Thus, this is a reasonable default. @@ -129,13 +117,7 @@ read_config(svn_memcache_t **memcache_p, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, TRUE); - /* For now, always disable revprop caching. - */ - *cache_revprops = FALSE; - - return svn_config_get_bool(ffd->config, fail_stop, - CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, - FALSE); + return SVN_NO_ERROR; } @@ -196,9 +178,10 @@ dump_cache_statistics(void *baton_void) TRUE, baton->pool); - if (! err) + /* skip unused caches */ + if (! err && (info.gets > 0 || info.sets > 0)) { - text_stats = svn_cache__format_info(&info, baton->pool); + text_stats = svn_cache__format_info(&info, TRUE, baton->pool); lines = svn_cstring_split(text_stats->data, "\n", FALSE, baton->pool); for (i = 0; i < lines->nelts; ++i) @@ -219,6 +202,29 @@ dump_cache_statistics(void *baton_void) return result; } + +static apr_status_t +dump_global_cache_statistics(void *baton_void) +{ + apr_pool_t *pool = baton_void; + + svn_cache__info_t *info = svn_cache__membuffer_get_global_info(pool); + svn_string_t *text_stats = svn_cache__format_info(info, FALSE, pool); + apr_array_header_t *lines = svn_cstring_split(text_stats->data, "\n", + FALSE, pool); + + int i; + 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 + } + + return APR_SUCCESS; +} + #endif /* SVN_DEBUG_CACHE_DUMP_STATS */ /* This function sets / registers the required callbacks for a given @@ -267,12 +273,13 @@ init_callbacks(svn_cache__t *cache, * 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. + * otherwise. Use the given PRIORITY class for the new cache. If it + * is 0, then use the default priority class. * * Unless NO_HANDLER is true, register an error handler that reports errors * as warnings to the FS warning callback. * - * Cache is allocated in POOL. + * Cache is allocated in RESULT_POOL, temporaries in SCRATCH_POOL. * */ static svn_error_t * create_cache(svn_cache__t **cache_p, @@ -284,19 +291,23 @@ create_cache(svn_cache__t **cache_p, svn_cache__deserialize_func_t deserializer, apr_ssize_t klen, const char *prefix, + apr_uint32_t priority, svn_fs_t *fs, svn_boolean_t no_handler, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_cache__error_handler_t error_handler = no_handler ? NULL : warn_and_fail_on_cache_errors; + if (priority == 0) + priority = SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY; if (memcache) { SVN_ERR(svn_cache__create_memcache(cache_p, memcache, serializer, deserializer, klen, - prefix, pool)); + prefix, result_pool)); error_handler = no_handler ? NULL : warn_and_continue_on_cache_errors; @@ -305,20 +316,20 @@ create_cache(svn_cache__t **cache_p, { SVN_ERR(svn_cache__create_membuffer_cache( cache_p, membuffer, serializer, deserializer, - klen, prefix, FALSE, pool)); + klen, prefix, priority, FALSE, result_pool, scratch_pool)); } else if (pages) { SVN_ERR(svn_cache__create_inprocess( cache_p, serializer, deserializer, klen, pages, - items_per_page, FALSE, prefix, pool)); + items_per_page, FALSE, prefix, result_pool)); } else { *cache_p = NULL; } - SVN_ERR(init_callbacks(*cache_p, fs, error_handler, pool)); + SVN_ERR(init_callbacks(*cache_p, fs, error_handler, result_pool)); return SVN_NO_ERROR; } @@ -332,29 +343,45 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, "fsfs:", fs->uuid, "/", normalize_key_part(fs->path, pool), ":", - (char *)NULL); - svn_memcache_t *memcache; + SVN_VA_NULL); svn_membuffer_t *membuffer; - svn_boolean_t no_handler; + svn_boolean_t no_handler = ffd->fail_stop; 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, + SVN_ERR(read_config(&cache_namespace, &cache_txdeltas, &cache_fulltexts, - &cache_revprops, fs, pool)); - prefix = apr_pstrcat(pool, "ns:", cache_namespace, ":", prefix, NULL); + prefix = apr_pstrcat(pool, "ns:", cache_namespace, ":", prefix, SVN_VA_NULL); membuffer = svn_cache__get_global_membuffer_cache(); + /* General rules for assigning cache priorities: + * + * - Data that can be reconstructed from other elements has low prio + * (e.g. fulltexts, directories etc.) + * - Index data required to find any of the other data has high prio + * (e.g. noderevs, L2P and P2L index pages) + * - everthing else should use default prio + */ + +#ifdef SVN_DEBUG_CACHE_DUMP_STATS + + /* schedule printing the global access statistics upon pool cleanup, + * i.e. when the repo instance gets closed / cleaned up. + */ + if (membuffer) + apr_pool_cleanup_register(fs->pool, + fs->pool, + dump_global_cache_statistics, + apr_pool_cleanup_null); +#endif + /* 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 @@ -370,10 +397,11 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__serialize_id, svn_fs_fs__deserialize_id, sizeof(svn_revnum_t), - apr_pstrcat(pool, prefix, "RRI", (char *)NULL), + apr_pstrcat(pool, prefix, "RRI", SVN_VA_NULL), + 0, fs, no_handler, - fs->pool)); + fs->pool, pool)); /* Rough estimate: revision DAG nodes have size around 320 bytes, so * let's put 16 on a page. */ @@ -384,13 +412,14 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__dag_serialize, svn_fs_fs__dag_deserialize, APR_HASH_KEY_STRING, - apr_pstrcat(pool, prefix, "DAG", (char *)NULL), + apr_pstrcat(pool, prefix, "DAG", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); /* 1st level DAG node cache */ - ffd->dag_node_cache = svn_fs_fs__create_dag_cache(pool); + ffd->dag_node_cache = svn_fs_fs__create_dag_cache(fs->pool); /* Very rough estimate: 1K per directory. */ SVN_ERR(create_cache(&(ffd->dir_cache), @@ -399,11 +428,12 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, 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), + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "DIR", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); /* Only 16 bytes per entry (a revision number + the corresponding offset). Since we want ~8k pages, that means 512 entries per page. */ @@ -415,51 +445,69 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__deserialize_manifest, sizeof(svn_revnum_t), apr_pstrcat(pool, prefix, "PACK-MANIFEST", - (char *)NULL), + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, 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 */ + 32, 32, /* ~200 byte / entry; 1k entries total */ svn_fs_fs__serialize_node_revision, svn_fs_fs__deserialize_node_revision, sizeof(pair_cache_key_t), - apr_pstrcat(pool, prefix, "NODEREVS", (char *)NULL), + apr_pstrcat(pool, prefix, "NODEREVS", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + + /* initialize representation header cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->rep_header_cache), + NULL, + membuffer, + 1, 1000, /* ~8 bytes / entry; 1k entries total */ + svn_fs_fs__serialize_rep_header, + svn_fs_fs__deserialize_rep_header, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "REPHEADER", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, 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 */ + 1, 8, /* 1k / entry; 8 entries total, rarely used */ svn_fs_fs__serialize_changes, svn_fs_fs__deserialize_changes, sizeof(svn_revnum_t), - apr_pstrcat(pool, prefix, "CHANGES", (char *)NULL), + apr_pstrcat(pool, prefix, "CHANGES", SVN_VA_NULL), + 0, fs, no_handler, - fs->pool)); + fs->pool, pool)); /* if enabled, cache fulltext and other derived information */ if (cache_fulltexts) { SVN_ERR(create_cache(&(ffd->fulltext_cache), - memcache, + ffd->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), + apr_pstrcat(pool, prefix, "TEXT", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); SVN_ERR(create_cache(&(ffd->properties_cache), NULL, @@ -469,10 +517,11 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__deserialize_properties, sizeof(pair_cache_key_t), apr_pstrcat(pool, prefix, "PROP", - (char *)NULL), + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); SVN_ERR(create_cache(&(ffd->mergeinfo_cache), NULL, @@ -482,10 +531,11 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, svn_fs_fs__deserialize_mergeinfo, APR_HASH_KEY_STRING, apr_pstrcat(pool, prefix, "MERGEINFO", - (char *)NULL), + SVN_VA_NULL), + 0, fs, no_handler, - fs->pool)); + fs->pool, pool)); SVN_ERR(create_cache(&(ffd->mergeinfo_existence_cache), NULL, @@ -495,10 +545,11 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, NULL, NULL, APR_HASH_KEY_STRING, apr_pstrcat(pool, prefix, "HAS_MERGEINFO", - (char *)NULL), + SVN_VA_NULL), + 0, fs, no_handler, - fs->pool)); + fs->pool, pool)); } else { @@ -508,42 +559,36 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, ffd->mergeinfo_existence_cache = NULL; } - /* initialize revprop cache, if full-text caching has been enabled */ - if (cache_revprops) + /* if enabled, cache text deltas and their combinations */ + if (cache_txdeltas) { - SVN_ERR(create_cache(&(ffd->revprop_cache), + SVN_ERR(create_cache(&(ffd->raw_window_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), + svn_fs_fs__serialize_raw_window, + svn_fs_fs__deserialize_raw_window, + sizeof(window_cache_key_t), + apr_pstrcat(pool, prefix, "RAW_WINDOW", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, fs, no_handler, - fs->pool)); - } - else - { - ffd->revprop_cache = NULL; - } + fs->pool, pool)); - /* 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, + sizeof(window_cache_key_t), apr_pstrcat(pool, prefix, "TXDELTA_WINDOW", - (char *)NULL), + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); SVN_ERR(create_cache(&(ffd->combined_window_cache), NULL, @@ -551,12 +596,13 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, 0, 0, /* Do not use inprocess cache */ /* Values are svn_stringbuf_t */ NULL, NULL, - APR_HASH_KEY_STRING, + sizeof(window_cache_key_t), apr_pstrcat(pool, prefix, "COMBINED_WINDOW", - (char *)NULL), + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, fs, no_handler, - fs->pool)); + fs->pool, pool)); } else { @@ -564,6 +610,61 @@ svn_fs_fs__initialize_caches(svn_fs_t *fs, ffd->combined_window_cache = NULL; } + SVN_ERR(create_cache(&(ffd->l2p_header_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_fs__serialize_l2p_header, + svn_fs_fs__deserialize_l2p_header, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "L2P_HEADER", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->l2p_page_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_fs__serialize_l2p_page, + svn_fs_fs__deserialize_l2p_page, + sizeof(svn_fs_fs__page_cache_key_t), + apr_pstrcat(pool, prefix, "L2P_PAGE", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->p2l_header_cache), + NULL, + membuffer, + 4, 1, /* Large entries. Rarely used. */ + svn_fs_fs__serialize_p2l_header, + svn_fs_fs__deserialize_p2l_header, + sizeof(pair_cache_key_t), + apr_pstrcat(pool, prefix, "P2L_HEADER", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + SVN_ERR(create_cache(&(ffd->p2l_page_cache), + NULL, + membuffer, + 4, 16, /* Variably sized entries. Rarely used. */ + svn_fs_fs__serialize_p2l_page, + svn_fs_fs__deserialize_p2l_page, + sizeof(svn_fs_fs__page_cache_key_t), + apr_pstrcat(pool, prefix, "P2L_PAGE", + (char *)NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, pool)); + return SVN_NO_ERROR; } @@ -575,12 +676,44 @@ struct txn_cleanup_baton_t /* the position where to reset it */ svn_cache__t **to_reset; + + /* pool that TXN_CACHE was allocated in */ + apr_pool_t *txn_pool; + + /* pool that the FS containing the TO_RESET pointer was allocator */ + apr_pool_t *fs_pool; }; +/* Forward declaration. */ +static apr_status_t +remove_txn_cache_fs(void *baton_void); + +/* APR pool cleanup handler that will reset the cache pointer given in + BATON_VOID when the TXN_POOL gets cleaned up. */ +static apr_status_t +remove_txn_cache_txn(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; + } + + /* It's cleaned up now. Prevent double cleanup. */ + apr_pool_cleanup_kill(baton->fs_pool, + baton, + remove_txn_cache_fs); + + return APR_SUCCESS; +} + /* APR pool cleanup handler that will reset the cache pointer given in - BATON_VOID. */ + BATON_VOID when the FS_POOL gets cleaned up. */ static apr_status_t -remove_txn_cache(void *baton_void) +remove_txn_cache_fs(void *baton_void) { struct txn_cleanup_baton_t *baton = baton_void; @@ -588,19 +721,25 @@ remove_txn_cache(void *baton_void) if (*baton->to_reset == baton->txn_cache) { /* This is equivalent to calling svn_fs_fs__reset_txn_caches(). */ - *baton->to_reset = NULL; + *baton->to_reset = NULL; } + /* It's cleaned up now. Prevent double cleanup. */ + apr_pool_cleanup_kill(baton->txn_pool, + baton, + remove_txn_cache_txn); + 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. + * transaction-specific *CACHE object in FS, if CACHE is not NULL and + * a no-op otherwise. In particular, it will ensure that *CACHE gets + * reset to NULL upon POOL or FS->POOL destruction latest. */ static void -init_txn_callbacks(svn_cache__t **cache, +init_txn_callbacks(svn_fs_t *fs, + svn_cache__t **cache, apr_pool_t *pool) { if (*cache != NULL) @@ -610,10 +749,20 @@ init_txn_callbacks(svn_cache__t **cache, baton = apr_palloc(pool, sizeof(*baton)); baton->txn_cache = *cache; baton->to_reset = cache; + baton->txn_pool = pool; + baton->fs_pool = fs->pool; + /* If any of these pools gets cleaned, we must reset the cache. + * We don't know which one will get cleaned up first, so register + * cleanup actions for both and during the cleanup action, unregister + * the respective other action. */ apr_pool_cleanup_register(pool, baton, - remove_txn_cache, + remove_txn_cache_txn, + apr_pool_cleanup_null); + apr_pool_cleanup_register(fs->pool, + baton, + remove_txn_cache_fs, apr_pool_cleanup_null); } } @@ -635,7 +784,7 @@ svn_fs_fs__initialize_txn_caches(svn_fs_t *fs, "/", fs->path, ":", txn_id, ":", svn_uuid_generate(pool), ":", - (char *)NULL); + SVN_VA_NULL); /* We don't support caching for concurrent transactions in the SAME * FSFS session. Maybe, you forgot to clean POOL. */ @@ -656,13 +805,14 @@ svn_fs_fs__initialize_txn_caches(svn_fs_t *fs, svn_fs_fs__deserialize_dir_entries, APR_HASH_KEY_STRING, apr_pstrcat(pool, prefix, "TXNDIR", - (char *)NULL), + SVN_VA_NULL), + 0, fs, TRUE, - pool)); + pool, pool)); /* reset the transaction-specific cache if the pool gets cleaned up. */ - init_txn_callbacks(&(ffd->txn_dir_cache), pool); + init_txn_callbacks(fs, &(ffd->txn_dir_cache), pool); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/dag.c b/contrib/subversion/subversion/libsvn_fs_fs/dag.c index 3c51ffd7b..d21c17c4b 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/dag.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/dag.c @@ -28,11 +28,12 @@ #include "svn_props.h" #include "svn_pools.h" +#include "cached_data.h" #include "dag.h" #include "fs.h" -#include "key-gen.h" #include "fs_fs.h" #include "id.h" +#include "transaction.h" #include "../libsvn_fs/fs-loader.h" @@ -159,10 +160,13 @@ get_node_revision(node_revision_t **noderev_p, if (! node->node_revision) { node_revision_t *noderev; + apr_pool_t *scratch_pool = svn_pool_create(node->node_pool); SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs, - node->id, node->node_pool)); + node->id, node->node_pool, + scratch_pool)); node->node_revision = noderev; + svn_pool_destroy(scratch_pool); } /* Now NODE->node_revision is set. */ @@ -173,7 +177,7 @@ get_node_revision(node_revision_t **noderev_p, svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node) { - return (svn_fs_fs__id_txn_id(svn_fs_fs__dag_get_id(node)) != NULL); + return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node)); } @@ -312,8 +316,9 @@ dir_entry_id_from_node(const svn_fs_id_t **id_p, { svn_fs_dirent_t *dirent; - SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, scratch_pool)); - *id_p = dirent ? svn_fs_fs__id_copy(dirent->id, result_pool) : NULL; + SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool, + scratch_pool)); + *id_p = dirent ? dirent->id : NULL; return SVN_NO_ERROR; } @@ -332,7 +337,7 @@ set_entry(dag_node_t *parent, const char *name, const svn_fs_id_t *id, svn_node_kind_t kind, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { node_revision_t *parent_noderev; @@ -359,7 +364,7 @@ make_entry(dag_node_t **child_p, const char *parent_path, const char *name, svn_boolean_t is_dir, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *new_node_id; @@ -413,7 +418,7 @@ make_entry(dag_node_t **child_p, svn_error_t * -svn_fs_fs__dag_dir_entries(apr_hash_t **entries, +svn_fs_fs__dag_dir_entries(apr_array_header_t **entries, dag_node_t *node, apr_pool_t *pool) { @@ -425,14 +430,15 @@ svn_fs_fs__dag_dir_entries(apr_hash_t **entries, return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, _("Can't get entries of non-directory")); - return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool); + return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool); } svn_error_t * svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent, dag_node_t *node, const char* name, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { node_revision_t *noderev; SVN_ERR(get_node_revision(&noderev, node)); @@ -442,8 +448,8 @@ svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent, _("Can't get entries of non-directory")); /* Get a dirent hash for this directory. */ - return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, - noderev, name, pool, pool); + return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name, + result_pool, scratch_pool); } @@ -452,7 +458,7 @@ svn_fs_fs__dag_set_entry(dag_node_t *node, const char *entry_name, const svn_fs_id_t *id, svn_node_kind_t kind, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { /* Check it's a directory. */ @@ -492,6 +498,42 @@ svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p, return SVN_NO_ERROR; } +svn_error_t * +svn_fs_fs__dag_has_props(svn_boolean_t *has_props, + dag_node_t *node, + apr_pool_t *scratch_pool) +{ + node_revision_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + + if (! noderev->prop_rep) + { + *has_props = FALSE; /* Easy out */ + return SVN_NO_ERROR; + } + + if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) + { + /* We are in a commit or something. Check actual properties */ + apr_hash_t *proplist; + + SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, + noderev, scratch_pool)); + + *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE; + } + else + { + /* Properties are stored as a standard hash stream, + always ending with "END\n" (4 bytes) */ + *has_props = (noderev->prop_rep->expanded_size > 4 + || (noderev->prop_rep->expanded_size == 0 + && noderev->prop_rep->size > 4)); + } + + return SVN_NO_ERROR; +} svn_error_t * svn_fs_fs__dag_set_proplist(dag_node_t *node, @@ -606,17 +648,31 @@ svn_fs_fs__dag_revision_root(dag_node_t **node_p, svn_revnum_t rev, apr_pool_t *pool) { - svn_fs_id_t *root_id; + dag_node_t *new_node; - SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool)); - return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool); + /* Construct the node. */ + new_node = apr_pcalloc(pool, sizeof(*new_node)); + new_node->fs = fs; + SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool)); + + /* Grab the contents so we can inspect the node's kind and created path. */ + new_node->node_pool = pool; + + /* Initialize the KIND and CREATED_PATH attributes */ + new_node->kind = svn_node_dir; + new_node->created_path = "/"; + new_node->fresh_root_predecessor_id = NULL; + + /* Return a fresh new node */ + *node_p = new_node; + return SVN_NO_ERROR; } svn_error_t * svn_fs_fs__dag_txn_root(dag_node_t **node_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *root_id, *ignored; @@ -629,7 +685,7 @@ svn_fs_fs__dag_txn_root(dag_node_t **node_p, svn_error_t * svn_fs_fs__dag_txn_base_root(dag_node_t **node_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *base_root_id, *ignored; @@ -644,8 +700,8 @@ svn_fs_fs__dag_clone_child(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *copy_id, - const char *txn_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, svn_boolean_t is_parent_copyroot, apr_pool_t *pool) { @@ -668,6 +724,10 @@ svn_fs_fs__dag_clone_child(dag_node_t **child_p, /* Find the node named NAME in PARENT's entries list if it exists. */ SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool)); + if (! cur_entry) + return svn_error_createf + (SVN_ERR_FS_NOT_FOUND, NULL, + "Attempted to open non-existent child node '%s'", name); /* Check for mutability in the node we found. If it's mutable, we don't need to clone it. */ @@ -718,7 +778,7 @@ svn_fs_fs__dag_clone_child(dag_node_t **child_p, svn_error_t * svn_fs_fs__dag_clone_root(dag_node_t **root_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *base_root_id, *root_id; @@ -745,7 +805,7 @@ svn_fs_fs__dag_clone_root(dag_node_t **root_p, svn_error_t * svn_fs_fs__dag_delete(dag_node_t *parent, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { node_revision_t *parent_noderev; @@ -840,23 +900,22 @@ svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs, /* Else it's mutable. Recurse on directories... */ if (node->kind == svn_node_dir) { - apr_hash_t *entries; - apr_hash_index_t *hi; + apr_array_header_t *entries; + int i; + apr_pool_t *iterpool = svn_pool_create(pool); - /* Loop over hash entries */ + /* Loop over directory entries */ SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); if (entries) - { - for (hi = apr_hash_first(pool, entries); - hi; - hi = apr_hash_next(hi)) - { - svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi); - - SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, dirent->id, - pool)); - } - } + for (i = 0; i < entries->nelts; ++i) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, + APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id, + iterpool)); + } + + svn_pool_destroy(iterpool); } /* ... then delete the node itself, after deleting any mutable @@ -869,7 +928,7 @@ svn_fs_fs__dag_make_file(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { /* Call our little helper function */ @@ -882,7 +941,7 @@ svn_fs_fs__dag_make_dir(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { /* Call our little helper function */ @@ -909,7 +968,7 @@ svn_fs_fs__dag_get_contents(svn_stream_t **contents_p, /* Get a stream to the contents. */ SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs, - noderev, pool)); + noderev->data_rep, TRUE, pool)); *contents_p = contents; @@ -1107,7 +1166,7 @@ svn_fs_fs__dag_serialize(void **data, /* The deserializer will use its own pool. */ svn_temp_serializer__set_null(context, - (const void * const *)&node->node_pool); + (const void * const *)&node->node_pool); /* serialize other sub-structures */ svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id); @@ -1164,9 +1223,10 @@ svn_fs_fs__dag_open(dag_node_t **child_p, SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, scratch_pool, scratch_pool)); if (! node_id) - return svn_error_createf - (SVN_ERR_FS_NOT_FOUND, NULL, - "Attempted to open non-existent child node '%s'", name); + { + *child_p = NULL; + return SVN_NO_ERROR; + } /* Make sure that NAME is a single path component. */ if (! svn_path_is_single_path_component(name)) @@ -1187,7 +1247,7 @@ svn_fs_fs__dag_copy(dag_node_t *to_node, svn_boolean_t preserve_history, svn_revnum_t from_rev, const char *from_path, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *id; @@ -1195,7 +1255,7 @@ svn_fs_fs__dag_copy(dag_node_t *to_node, if (preserve_history) { node_revision_t *from_noderev, *to_noderev; - const char *copy_id; + svn_fs_fs__id_part_t copy_id; const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node); svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node); @@ -1221,7 +1281,7 @@ svn_fs_fs__dag_copy(dag_node_t *to_node, to_noderev->copyroot_path = NULL; SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev, - copy_id, txn_id, pool)); + ©_id, txn_id, pool)); } else /* don't preserve history */ @@ -1242,7 +1302,9 @@ svn_error_t * svn_fs_fs__dag_things_different(svn_boolean_t *props_changed, svn_boolean_t *contents_changed, dag_node_t *node1, - dag_node_t *node2) + dag_node_t *node2, + svn_boolean_t strict, + apr_pool_t *pool) { node_revision_t *noderev1, *noderev2; @@ -1255,16 +1317,49 @@ svn_fs_fs__dag_things_different(svn_boolean_t *props_changed, SVN_ERR(get_node_revision(&noderev1, node1)); SVN_ERR(get_node_revision(&noderev2, node2)); - /* Compare property keys. */ - if (props_changed != NULL) - *props_changed = (! svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep, - noderev2->prop_rep)); + if (strict) + { + /* In strict mode, compare text and property representations in the + svn_fs_contents_different() / svn_fs_props_different() manner. + + See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9" + discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and + issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */ + svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1); + svn_boolean_t same; + + /* Compare property keys. */ + if (props_changed != NULL) + { + SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1, + noderev2, pool)); + *props_changed = !same; + } - /* Compare contents keys. */ - if (contents_changed != NULL) - *contents_changed = - (! svn_fs_fs__noderev_same_rep_key(noderev1->data_rep, - noderev2->data_rep)); + /* Compare contents keys. */ + if (contents_changed != NULL) + { + SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1, + noderev2, pool)); + *contents_changed = !same; + } + } + else + { + /* Otherwise, compare representation keys -- as in Subversion 1.8. */ + + /* Compare property keys. */ + if (props_changed != NULL) + *props_changed = + !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep, + noderev2->prop_rep); + + /* Compare contents keys. */ + if (contents_changed != NULL) + *contents_changed = + !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep, + noderev2->data_rep); + } return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/dag.h b/contrib/subversion/subversion/libsvn_fs_fs/dag.h index 867b025f1..edab6d525 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/dag.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/dag.h @@ -27,6 +27,8 @@ #include "svn_delta.h" #include "private/svn_cache.h" +#include "id.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -175,6 +177,12 @@ svn_error_t *svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p, dag_node_t *node, apr_pool_t *pool); +/* Set *HAS_PROPS to TRUE if NODE has properties. Use SCRATCH_POOL + for temporary allocations */ +svn_error_t *svn_fs_fs__dag_has_props(svn_boolean_t *has_props, + dag_node_t *node, + apr_pool_t *scratch_pool); + /* Set the property list of NODE to PROPLIST, allocating from POOL. The node being changed must be mutable. @@ -224,7 +232,7 @@ svn_error_t *svn_fs_fs__dag_revision_root(dag_node_t **node_p, for a transaction, call svn_fs_fs__dag_clone_root. */ svn_error_t *svn_fs_fs__dag_txn_root(dag_node_t **node_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -232,7 +240,7 @@ svn_error_t *svn_fs_fs__dag_txn_root(dag_node_t **node_p, allocating from POOL. Allocate the node in TRAIL->pool. */ svn_error_t *svn_fs_fs__dag_txn_base_root(dag_node_t **node_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -242,7 +250,7 @@ svn_error_t *svn_fs_fs__dag_txn_base_root(dag_node_t **node_p, root directory clone. Allocate *ROOT_P in POOL. */ svn_error_t *svn_fs_fs__dag_clone_root(dag_node_t **root_p, svn_fs_t *fs, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -252,7 +260,8 @@ svn_error_t *svn_fs_fs__dag_clone_root(dag_node_t **root_p, /* Open the node named NAME in the directory PARENT. Set *CHILD_P to the new node, allocated in RESULT_POOL. NAME must be a single path - component; it cannot be a slash-separated directory path. + component; it cannot be a slash-separated directory path. If NAME does + not exist within PARENT, set *CHILD_P to NULL. */ svn_error_t * svn_fs_fs__dag_open(dag_node_t **child_p, @@ -262,24 +271,25 @@ svn_fs_fs__dag_open(dag_node_t **child_p, apr_pool_t *scratch_pool); -/* Set *ENTRIES_P to a hash table of NODE's entries. The keys of the - table are entry names, and the values are svn_fs_dirent_t's. The - returned table (and its keys and values) is allocated in POOL, - which is also used for temporary allocations. */ -svn_error_t *svn_fs_fs__dag_dir_entries(apr_hash_t **entries_p, +/* Set *ENTRIES_P to an array of NODE's entries, sorted by entry names, + and the values are svn_fs_dirent_t's. The returned table (and elements) + is allocated in POOL, which is also used for temporary allocations. */ +svn_error_t *svn_fs_fs__dag_dir_entries(apr_array_header_t **entries_p, dag_node_t *node, apr_pool_t *pool); /* Fetches the NODE's entries and returns a copy of the entry selected by the key value given in NAME and set *DIRENT to a copy of that - entry. If such entry was found, the copy will be allocated in POOL. + entry. If such entry was found, the copy will be allocated in + RESULT_POOL. Temporary data will be used in SCRATCH_POOL. Otherwise, the *DIRENT will be set to NULL. */ /* ### This function is currently only called from dag.c. */ svn_error_t * svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent, dag_node_t *node, const char* name, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Set ENTRY_NAME in NODE to point to ID (with kind KIND), allocating from POOL. NODE must be a mutable directory. ID can refer to a @@ -294,7 +304,7 @@ svn_error_t *svn_fs_fs__dag_set_entry(dag_node_t *node, const char *entry_name, const svn_fs_id_t *id, svn_node_kind_t kind, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -321,8 +331,8 @@ svn_error_t *svn_fs_fs__dag_clone_child(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *copy_id, - const char *txn_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, svn_boolean_t is_parent_copyroot, apr_pool_t *pool); @@ -341,7 +351,7 @@ svn_error_t *svn_fs_fs__dag_clone_child(dag_node_t **child_p, */ svn_error_t *svn_fs_fs__dag_delete(dag_node_t *parent, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -384,7 +394,7 @@ svn_error_t *svn_fs_fs__dag_make_dir(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -495,7 +505,7 @@ svn_error_t *svn_fs_fs__dag_make_file(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); @@ -522,33 +532,31 @@ svn_error_t *svn_fs_fs__dag_copy(dag_node_t *to_node, svn_boolean_t preserve_history, svn_revnum_t from_rev, const char *from_path, - const char *txn_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); /* Comparison */ -/* Find out what is the same between two nodes. +/* Find out what is the same between two nodes. If STRICT is FALSE, + this function may report false positives, i.e. report changes even + if the resulting contents / props are equal. If PROPS_CHANGED is non-null, set *PROPS_CHANGED to 1 if the two nodes have different property lists, or to 0 if same. If CONTENTS_CHANGED is non-null, set *CONTENTS_CHANGED to 1 if the - two nodes have different contents, or to 0 if same. For files, - file contents are compared; for directories, the entries lists are - compared. If one is a file and the other is a directory, the one's - contents will be compared to the other's entries list. (Not - terribly useful, I suppose, but that's the caller's business.) - - ### todo: This function only compares rep keys at the moment. This - may leave us with a slight chance of a false positive, though I - don't really see how that would happen in practice. Nevertheless, - it should probably be fixed. + two nodes have different contents, or to 0 if same. NODE1 and NODE2 + must refer to files from the same filesystem. + + Use POOL for temporary allocations. */ svn_error_t *svn_fs_fs__dag_things_different(svn_boolean_t *props_changed, svn_boolean_t *contents_changed, dag_node_t *node1, - dag_node_t *node2); + dag_node_t *node2, + svn_boolean_t strict, + apr_pool_t *pool); /* Set *REV and *PATH to the copyroot revision and path of node NODE, or diff --git a/contrib/subversion/subversion/libsvn_fs_fs/dump-index.c b/contrib/subversion/subversion/libsvn_fs_fs/dump-index.c new file mode 100644 index 000000000..c97be10a4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/dump-index.c @@ -0,0 +1,90 @@ +/* dump-index.c -- implements the svn_fs_fs__dump_index private API + * + * ==================================================================== + * 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_pools.h" +#include "private/svn_fs_fs_private.h" + +#include "index.h" +#include "rev_file.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +svn_error_t * +svn_fs_fs__dump_index(svn_fs_t *fs, + svn_revnum_t revision, + svn_fs_fs__dump_index_func_t callback_func, + void *callback_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_fs_fs__revision_file_t *rev_file; + int i; + apr_off_t offset, max_offset; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Check the FS format. */ + if (! svn_fs_fs__use_log_addressing(fs)) + return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, NULL); + + /* Revision & index file access object. */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, revision, + scratch_pool, iterpool)); + + /* Offset range to cover. */ + SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs, rev_file, revision, + scratch_pool)); + + /* Walk through all P2L index entries in offset order. */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + + /* Read entries for the next block. There will be no overlaps since + * we start at the first offset not covered. */ + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, revision, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + + /* Print entries for this block, one line per entry. */ + for (i = 0; i < entries->nelts && offset < max_offset; ++i) + { + const svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t); + offset = entry->offset + entry->size; + + /* Cancellation support */ + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Invoke processing callback. */ + SVN_ERR(callback_func(entry, callback_baton, iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/fs.c b/contrib/subversion/subversion/libsvn_fs_fs/fs.c index d0ba7342a..77c5d221c 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/fs.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/fs.c @@ -37,11 +37,17 @@ #include "fs_fs.h" #include "tree.h" #include "lock.h" +#include "hotcopy.h" #include "id.h" +#include "pack.h" +#include "recovery.h" #include "rep-cache.h" +#include "revprops.h" +#include "transaction.h" +#include "util.h" +#include "verify.h" #include "svn_private_config.h" #include "private/svn_fs_util.h" -#include "private/svn_subr_private.h" #include "../libsvn_fs/fs-loader.h" @@ -51,6 +57,9 @@ +/* Initialize the part of FS that requires global serialization across all + instances. The caller is responsible of ensuring that serialization. + Use COMMON_POOL for process-wide and POOL for temporary allocations. */ static svn_error_t * fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) { @@ -64,20 +73,29 @@ fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) each separate repository opened during the lifetime of the svn_fs_initialize pool. It's unlikely that anyone will notice the modest expenditure; the alternative is to allocate each structure - in a subpool, add a reference-count, and add a serialized deconstructor + in a subpool, add a reference-count, and add a serialized destructor to the FS vtable. That's more machinery than it's worth. - Using the uuid to obtain the lock creates a corner case if a - caller uses svn_fs_set_uuid on the repository in a process where - other threads might be using the same repository through another - FS object. The only real-world consumer of svn_fs_set_uuid is - "svnadmin load", so this is a low-priority problem, and we don't - know of a better way of associating such data with the - repository. */ + Picking an appropriate key for the shared data is tricky, because, + unfortunately, a filesystem UUID is not really unique. It is implicitly + shared between hotcopied (1), dump / loaded (2) or naively copied (3) + filesystems. We tackle this problem by using a combination of the UUID + and an instance ID as the key. This allows us to avoid key clashing + in (1) and (2) for formats >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT, which + do support instance IDs. For old formats the shared data (locks, shared + transaction data, ...) will still clash. + + Speaking of (3), there is not so much we can do about it, except maybe + provide a convenient way of fixing things. Naively copied filesystems + have identical filesystem UUIDs *and* instance IDs. With the key being + a combination of these two, clashes can be fixed by changing either of + them (or both), e.g. with svn_fs_set_uuid(). */ SVN_ERR_ASSERT(fs->uuid); - key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid, - (char *) NULL); + SVN_ERR_ASSERT(ffd->instance_id); + + key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, + fs->uuid, ":", ffd->instance_id, SVN_VA_NULL); status = apr_pool_userdata_get(&val, key, common_pool); if (status) return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data")); @@ -94,15 +112,17 @@ fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); + /* ... the pack lock ... */ + SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock, + SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); + /* ... not to mention locking the txn-current file. */ SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); /* We also need a mutex for synchronizing access to the active - transaction list and free transaction pointer. This one is - enabled unconditionally. */ - SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, - TRUE, common_pool)); + transaction list and free transaction pointer. */ + SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool)); key = apr_pstrdup(common_pool, key); status = apr_pool_userdata_set(ffsd, key, NULL, common_pool); @@ -143,9 +163,21 @@ fs_freeze_body(void *baton, SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool)); if (exists) - SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool)); + SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs, + b->freeze_func, b->freeze_baton, + pool)); + else + SVN_ERR(b->freeze_func(b->freeze_baton, pool)); + + return SVN_NO_ERROR; +} - SVN_ERR(b->freeze_func(b->freeze_baton, pool)); +static svn_error_t * +fs_freeze_body2(void *baton, + apr_pool_t *pool) +{ + struct fs_freeze_baton_t *b = baton; + SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool)); return SVN_NO_ERROR; } @@ -156,6 +188,7 @@ fs_freeze(svn_fs_t *fs, void *freeze_baton, apr_pool_t *pool) { + fs_fs_data_t *ffd = fs->fsap_data; struct fs_freeze_baton_t b; b.fs = fs; @@ -163,20 +196,52 @@ fs_freeze(svn_fs_t *fs, b.freeze_baton = freeze_baton; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool)); + + if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) + SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool)); + else + SVN_ERR(fs_freeze_body2(&b, pool)); return SVN_NO_ERROR; } +static svn_error_t * +fs_info(const void **fsfs_info, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info)); + info->fs_type = SVN_FS_TYPE_FSFS; + info->shard_size = ffd->max_files_per_dir; + info->min_unpacked_rev = ffd->min_unpacked_rev; + info->log_addressing = ffd->use_log_addressing; + *fsfs_info = info; + return SVN_NO_ERROR; +} + +/* Wrapper around svn_fs_fs__set_uuid() adapting between function + signatures. */ +static svn_error_t * +fs_set_uuid(svn_fs_t *fs, + const char *uuid, + apr_pool_t *pool) +{ + /* Whenever we set a new UUID, imply that FS will also be a different + * instance (on formats that support this). */ + return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool)); +} + /* The vtable associated with a specific open filesystem. */ static fs_vtable_t fs_vtable = { svn_fs_fs__youngest_rev, svn_fs_fs__revision_prop, - svn_fs_fs__revision_proplist, + svn_fs_fs__get_revision_proplist, svn_fs_fs__change_rev_prop, - svn_fs_fs__set_uuid, + fs_set_uuid, svn_fs_fs__revision_root, svn_fs_fs__begin_txn, svn_fs_fs__open_txn, @@ -188,6 +253,9 @@ static fs_vtable_t fs_vtable = { svn_fs_fs__unlock, svn_fs_fs__get_lock, svn_fs_fs__get_locks, + svn_fs_fs__info_format, + svn_fs_fs__info_config_files, + fs_info, svn_fs_fs__verify_root, fs_freeze, fs_set_errcall @@ -201,17 +269,31 @@ static svn_error_t * initialize_fs_struct(svn_fs_t *fs) { fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); + ffd->use_log_addressing = FALSE; + fs->vtable = &fs_vtable; fs->fsap_data = ffd; return SVN_NO_ERROR; } +/* Reset vtable and fsap_data fields in FS such that the FS is basically + * closed now. Note that FS must not hold locks when you call this. */ +static void +uninitialize_fs_struct(svn_fs_t *fs) +{ + fs->vtable = NULL; + fs->fsap_data = NULL; +} + /* This implements the fs_library_vtable_t.create() API. Create a new fsfs-backed Subversion filesystem at path PATH and link it into *FS. Perform temporary allocations in POOL, and fs-global allocations - in COMMON_POOL. */ + in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */ static svn_error_t * -fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, +fs_create(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { SVN_ERR(svn_fs__check_fs(fs, FALSE)); @@ -221,7 +303,10 @@ fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, SVN_ERR(svn_fs_fs__create(fs, path, pool)); SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); - return fs_serialized_init(fs, common_pool, pool); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + fs_serialized_init(fs, common_pool, pool)); + + return SVN_NO_ERROR; } @@ -231,17 +316,30 @@ fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, /* This implements the fs_library_vtable_t.open() API. Open an FSFS Subversion filesystem located at PATH, set *FS to point to the correct vtable for the filesystem. Use POOL for any temporary - allocations, and COMMON_POOL for fs-global allocations. */ + allocations, and COMMON_POOL for fs-global allocations. + The latter must be serialized using COMMON_POOL_LOCK. */ static svn_error_t * -fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, +fs_open(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { + apr_pool_t *subpool = svn_pool_create(pool); + + SVN_ERR(svn_fs__check_fs(fs, FALSE)); + SVN_ERR(initialize_fs_struct(fs)); - SVN_ERR(svn_fs_fs__open(fs, path, pool)); + SVN_ERR(svn_fs_fs__open(fs, path, subpool)); - SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); - return fs_serialized_init(fs, common_pool, pool); + SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool)); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + fs_serialized_init(fs, common_pool, subpool)); + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; } @@ -250,40 +348,79 @@ fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, static svn_error_t * fs_open_for_recovery(svn_fs_t *fs, const char *path, - apr_pool_t *pool, apr_pool_t *common_pool) + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, + apr_pool_t *common_pool) { + svn_error_t * err; + svn_revnum_t youngest_rev; + apr_pool_t * subpool = svn_pool_create(pool); + /* Recovery for FSFS is currently limited to recreating the 'current' file from the latest revision. */ /* The only thing we have to watch out for is that the 'current' file - might not exist. So we'll try to create it here unconditionally, - and just ignore any errors that might indicate that it's already - present. (We'll need it to exist later anyway as a source for the - new file's permissions). */ + might not exist or contain garbage. So we'll try to read it here + and provide or replace the existing file if we couldn't read it. + (We'll also need it to exist later anyway as a source for the new + file's permissions). */ - /* Use a partly-filled fs pointer first to create 'current'. This will fail - if 'current' already exists, but we don't care about that. */ + /* Use a partly-filled fs pointer first to create 'current'. */ fs->path = apr_pstrdup(fs->pool, path); - svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool), - "0 1 1\n", pool)); + + SVN_ERR(initialize_fs_struct(fs)); + + /* Figure out the repo format and check that we can even handle it. */ + SVN_ERR(svn_fs_fs__read_format_file(fs, subpool)); + + /* Now, read 'current' and try to patch it if necessary. */ + err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool); + if (err) + { + const char *file_path; + + /* 'current' file is missing or contains garbage. Since we are trying + * to recover from whatever problem there is, being picky about the + * error code here won't do us much good. If there is a persistent + * problem that we can't fix, it will show up when we try rewrite the + * file a few lines further below and we will report the failure back + * to the caller. + * + * Start recovery with HEAD = 0. */ + svn_error_clear(err); + file_path = svn_fs_fs__path_current(fs, subpool); + + /* Best effort to ensure the file exists and is valid. + * This may fail for r/o filesystems etc. */ + SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool)); + SVN_ERR(svn_io_file_create_empty(file_path, subpool)); + SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool)); + } + + uninitialize_fs_struct(fs); + svn_pool_destroy(subpool); /* Now open the filesystem properly by calling the vtable method directly. */ - return fs_open(fs, path, pool, common_pool); + return fs_open(fs, path, common_pool_lock, pool, common_pool); } /* This implements the fs_library_vtable_t.upgrade_fs() API. */ static svn_error_t * -fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, +fs_upgrade(svn_fs_t *fs, + const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, apr_pool_t *common_pool) { - SVN_ERR(svn_fs__check_fs(fs, FALSE)); - SVN_ERR(initialize_fs_struct(fs)); - SVN_ERR(svn_fs_fs__open(fs, path, pool)); - SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); - SVN_ERR(fs_serialized_init(fs, common_pool, pool)); - return svn_fs_fs__upgrade(fs, pool); + SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); + return svn_fs_fs__upgrade(fs, notify_func, notify_baton, + cancel_func, cancel_baton, pool); } static svn_error_t * @@ -294,14 +431,11 @@ fs_verify(svn_fs_t *fs, const char *path, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool) { - SVN_ERR(svn_fs__check_fs(fs, FALSE)); - SVN_ERR(initialize_fs_struct(fs)); - SVN_ERR(svn_fs_fs__open(fs, path, pool)); - SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); - SVN_ERR(fs_serialized_init(fs, common_pool, pool)); + SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton, cancel_func, cancel_baton, pool); } @@ -313,14 +447,11 @@ fs_pack(svn_fs_t *fs, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_mutex__t *common_pool_lock, apr_pool_t *pool, apr_pool_t *common_pool) { - SVN_ERR(svn_fs__check_fs(fs, FALSE)); - SVN_ERR(initialize_fs_struct(fs)); - SVN_ERR(svn_fs_fs__open(fs, path, pool)); - SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); - SVN_ERR(fs_serialized_init(fs, common_pool, pool)); + SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); return svn_fs_fs__pack(fs, notify_func, notify_baton, cancel_func, cancel_baton, pool); } @@ -333,7 +464,8 @@ fs_pack(svn_fs_t *fs, DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to re-copy data which already exists in DST_FS. The CLEAN_LOGS argument is ignored and included for Subversion - 1.0.x compatibility. Perform all temporary allocations in POOL. */ + 1.0.x compatibility. Indicate progress via the optional NOTIFY_FUNC + callback using NOTIFY_BATON. Perform all temporary allocations in POOL. */ static svn_error_t * fs_hotcopy(svn_fs_t *src_fs, svn_fs_t *dst_fs, @@ -341,24 +473,36 @@ fs_hotcopy(svn_fs_t *src_fs, const char *dst_path, svn_boolean_t clean_logs, svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool) + svn_mutex__t *common_pool_lock, + apr_pool_t *pool, + apr_pool_t *common_pool) { - SVN_ERR(svn_fs__check_fs(src_fs, FALSE)); - SVN_ERR(initialize_fs_struct(src_fs)); - SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool)); - SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool)); - SVN_ERR(fs_serialized_init(src_fs, pool, pool)); - - SVN_ERR(svn_fs__check_fs(dst_fs, FALSE)); + /* Open the source repo as usual. */ + SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool)); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Test target repo when in INCREMENTAL mode, initialize it when not. + * For this, we need our FS internal data structures to be temporarily + * available. */ SVN_ERR(initialize_fs_struct(dst_fs)); - /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS. - Otherwise, it's not an FS yet --- possibly just an empty dir --- so - can't be opened. - */ - return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path, - incremental, cancel_func, cancel_baton, pool); + SVN_ERR(svn_fs_fs__hotcopy_prepare_target(src_fs, dst_fs, dst_path, + incremental, pool)); + uninitialize_fs_struct(dst_fs); + + /* Now, the destination repo should open just fine. */ + SVN_ERR(fs_open(dst_fs, dst_path, common_pool_lock, pool, common_pool)); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Now, we may copy data as needed ... */ + return svn_fs_fs__hotcopy(src_fs, dst_fs, incremental, + notify_func, notify_baton, + cancel_func, cancel_baton, pool); } @@ -389,7 +533,7 @@ fs_delete_fs(const char *path, apr_pool_t *pool) { /* Remove everything. */ - return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); + return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); } static const svn_version_t * @@ -409,6 +553,7 @@ fs_set_svn_fs_open(svn_fs_t *fs, svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, + apr_pool_t *, apr_pool_t *)) { fs_fs_data_t *ffd = fs->fsap_data; @@ -416,6 +561,15 @@ fs_set_svn_fs_open(svn_fs_t *fs, return SVN_NO_ERROR; } +static void * +fs_info_dup(const void *fsfs_info_void, + apr_pool_t *result_pool) +{ + /* All fields are either ints or static strings. */ + const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void; + return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info)); +} + /* Base FS library vtable, used by the FS loader library. */ @@ -433,7 +587,8 @@ static fs_library_vtable_t library_vtable = { fs_pack, fs_logfiles, NULL /* parse_id */, - fs_set_svn_fs_open + fs_set_svn_fs_open, + fs_info_dup }; svn_error_t * @@ -444,6 +599,7 @@ svn_fs_fs__init(const svn_version_t *loader_version, { { "svn_subr", svn_subr_version }, { "svn_delta", svn_delta_version }, + { "svn_fs_util", svn_fs_util__version }, { NULL, NULL } }; diff --git a/contrib/subversion/subversion/libsvn_fs_fs/fs.h b/contrib/subversion/subversion/libsvn_fs_fs/fs.h index 5cdc27033..c75eafbd5 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/fs.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/fs.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "svn_fs.h" #include "svn_config.h" @@ -34,7 +36,8 @@ #include "private/svn_fs_private.h" #include "private/svn_sqlite.h" #include "private/svn_mutex.h" -#include "private/svn_named_atomic.h" + +#include "id.h" #ifdef __cplusplus extern "C" { @@ -51,9 +54,11 @@ extern "C" { #define PATH_UUID "uuid" /* Contains UUID */ #define PATH_CURRENT "current" /* Youngest revision */ #define PATH_LOCK_FILE "write-lock" /* Revision lock file */ +#define PATH_PACK_LOCK_FILE "pack-lock" /* Pack lock file */ #define PATH_REVS_DIR "revs" /* Directory of revisions */ #define PATH_REVPROPS_DIR "revprops" /* Directory of revprops */ -#define PATH_TXNS_DIR "transactions" /* Directory of transactions */ +#define PATH_TXNS_DIR "transactions" /* Directory of transactions in + repos w/o log addressing */ #define PATH_NODE_ORIGINS_DIR "node-origins" /* Lazy node-origin cache */ #define PATH_TXN_PROTOS_DIR "txn-protorevs" /* Directory of proto-revs */ #define PATH_TXN_CURRENT "txn-current" /* File with next txn key */ @@ -67,12 +72,18 @@ extern "C" { #define PATH_PACKED "pack" /* Packed revision data file */ #define PATH_EXT_PACKED_SHARD ".pack" /* Extension for packed shards */ +#define PATH_EXT_L2P_INDEX ".l2p" /* extension of the log- + to-phys index */ +#define PATH_EXT_P2L_INDEX ".p2l" /* extension of the phys- + to-log index */ /* If you change this, look at tests/svn_test_fs.c(maybe_install_fsfs_conf) */ #define PATH_CONFIG "fsfs.conf" /* Configuration */ /* Names of special files and file extensions for transactions */ #define PATH_CHANGES "changes" /* Records changes made so far */ #define PATH_TXN_PROPS "props" /* Transaction properties */ +#define PATH_TXN_PROPS_FINAL "props-final" /* Final transaction properties + before moving to revprops */ #define PATH_NEXT_IDS "next-ids" /* Next temporary ID assignments */ #define PATH_PREFIX_NODE "node." /* Prefix for node filename */ #define PATH_EXT_TXN ".txn" /* Extension of txn dir */ @@ -80,6 +91,10 @@ extern "C" { #define PATH_EXT_PROPS ".props" /* Extension for node props */ #define PATH_EXT_REV ".rev" /* Extension of protorev file */ #define PATH_EXT_REV_LOCK ".rev-lock" /* Extension of protorev lock file */ +#define PATH_TXN_ITEM_INDEX "itemidx" /* File containing the current item + index number */ +#define PATH_INDEX "index" /* name of index files w/o ext */ + /* Names of files in legacy FS formats */ #define PATH_REV "rev" /* Proto rev file */ #define PATH_REV_LOCK "rev-lock" /* Proto rev (write) lock file */ @@ -94,14 +109,25 @@ extern "C" { #define CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION "enable-props-deltification" #define CONFIG_OPTION_MAX_DELTIFICATION_WALK "max-deltification-walk" #define CONFIG_OPTION_MAX_LINEAR_DELTIFICATION "max-linear-deltification" +#define CONFIG_OPTION_COMPRESSION_LEVEL "compression-level" #define CONFIG_SECTION_PACKED_REVPROPS "packed-revprops" #define CONFIG_OPTION_REVPROP_PACK_SIZE "revprop-pack-size" #define CONFIG_OPTION_COMPRESS_PACKED_REVPROPS "compress-packed-revprops" +#define CONFIG_SECTION_IO "io" +#define CONFIG_OPTION_BLOCK_SIZE "block-size" +#define CONFIG_OPTION_L2P_PAGE_SIZE "l2p-page-size" +#define CONFIG_OPTION_P2L_PAGE_SIZE "p2l-page-size" +#define CONFIG_SECTION_DEBUG "debug" +#define CONFIG_OPTION_PACK_AFTER_COMMIT "pack-after-commit" /* The format number of this filesystem. This is independent of the repository format number, and - independent of any other FS back ends. */ -#define SVN_FS_FS__FORMAT_NUMBER 6 + independent of any other FS back ends. + + Note: If you bump this, please update the switch statement in + svn_fs_fs__create() as well. + */ +#define SVN_FS_FS__FORMAT_NUMBER 7 /* The minimum format number that supports svndiff version 1. */ #define SVN_FS_FS__MIN_SVNDIFF1_FORMAT 2 @@ -144,9 +170,31 @@ extern "C" { /* The minimum format number that supports packed revprops. */ #define SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT 6 +/* The minimum format number that supports packed revprops. */ +#define SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT 7 + +/* Minimum format number that providing a separate lock file for pack ops */ +#define SVN_FS_FS__MIN_PACK_LOCK_FORMAT 7 + +/* Minimum format number that stores mergeinfo-mode flag in changed paths */ +#define SVN_FS_FS__MIN_MERGEINFO_IN_CHANGED_FORMAT 7 + +/* Minimum format number that supports per-instance filesystem IDs. */ +#define SVN_FS_FS__MIN_INSTANCE_ID_FORMAT 7 + /* The minimum format number that supports a configuration file (fsfs.conf) */ #define SVN_FS_FS__MIN_CONFIG_FILE 4 +/* On most operating systems apr implements file locks per process, not + per file. On Windows apr implements the locking as per file handle + locks, so we don't have to add our own mutex for just in-process + synchronization. */ +#if APR_HAS_THREADS && !defined(WIN32) +#define SVN_FS_FS__USE_LOCK_MUTEX 1 +#else +#define SVN_FS_FS__USE_LOCK_MUTEX 0 +#endif + /* Private FSFS-specific data shared between all svn_txn_t objects that relate to a particular transaction in a filesystem (as identified by transaction id and filesystem UUID). Objects of this type are @@ -157,13 +205,8 @@ typedef struct fs_fs_shared_txn_data_t transaction. */ struct fs_fs_shared_txn_data_t *next; - /* This transaction's ID. For repositories whose format is less - than SVN_FS_FS__MIN_TXN_CURRENT_FORMAT, the ID is in the form - -, where runs from 0-99999 (see - create_txn_dir_pre_1_5() in fs_fs.c). For newer repositories, - the form is -<200 digit base 36 number> (see - create_txn_dir() in fs_fs.c). */ - char txn_id[SVN_FS__TXN_MAX_LEN+1]; + /* ID of this transaction. */ + svn_fs_fs__id_part_t txn_id; /* Whether the transaction's prototype revision file is locked for writing by any thread in this process (including the current @@ -176,17 +219,6 @@ typedef struct fs_fs_shared_txn_data_t apr_pool_t *pool; } fs_fs_shared_txn_data_t; -/* On most operating systems apr implements file locks per process, not - per file. On Windows apr implements the locking as per file handle - locks, so we don't have to add our own mutex for just in-process - synchronization. */ -/* Compare ../libsvn_subr/named_atomic.c:USE_THREAD_MUTEX */ -#if APR_HAS_THREADS && !defined(WIN32) -#define SVN_FS_FS__USE_LOCK_MUTEX 1 -#else -#define SVN_FS_FS__USE_LOCK_MUTEX 0 -#endif - /* Private FSFS-specific data shared between all svn_fs_t objects that relate to a particular filesystem, as identified by filesystem UUID. Objects of this type are allocated in the common pool. */ @@ -202,6 +234,12 @@ typedef struct fs_fs_shared_data_t Access to this object is synchronised under TXN_LIST_LOCK. */ fs_fs_shared_txn_data_t *free_txn; + /* The following lock must be taken out in reverse order of their + declaration here. Any subset may be acquired and held at any given + time but their relative acquisition order must not change. + + (lock 'txn-current' before 'pack' before 'write' before 'txn-list') */ + /* A lock for intra-process synchronization when accessing the TXNS list. */ svn_mutex__t *txn_list_lock; @@ -209,6 +247,10 @@ typedef struct fs_fs_shared_data_t repository write lock. */ svn_mutex__t *fs_write_lock; + /* A lock for intra-process synchronization when grabbing the + repository pack operation lock. */ + svn_mutex__t *fs_pack_lock; + /* A lock for intra-process synchronization when locking the txn-current file. */ svn_mutex__t *txn_current_lock; @@ -221,34 +263,76 @@ typedef struct fs_fs_shared_data_t /* Data structure for the 1st level DAG node cache. */ typedef struct fs_fs_dag_cache_t fs_fs_dag_cache_t; -/* Key type for all caches that use revision + offset / counter as key. */ +/* Key type for all caches that use revision + offset / counter as key. + + Note: Cache keys should be 16 bytes for best performance and there + should be no padding. */ typedef struct pair_cache_key_t { - svn_revnum_t revision; + /* The object's revision. Use the 64 data type to prevent padding. */ + apr_int64_t revision; + /* Sub-address: item index, revprop generation, packed flag, etc. */ apr_int64_t second; } pair_cache_key_t; +/* Key type that identifies a txdelta window. + + Note: Cache keys should require no padding. */ +typedef struct window_cache_key_t +{ + /* The object's revision. Use the 64 data type to prevent padding. */ + apr_int64_t revision; + + /* Window number within that representation. */ + apr_int64_t chunk_index; + + /* Item index of the representation */ + apr_uint64_t item_index; +} window_cache_key_t; + /* Private (non-shared) FSFS-specific data for each svn_fs_t object. Any caches in here may be NULL. */ typedef struct fs_fs_data_t { /* The format number of this FS. */ int format; + /* The maximum number of files to store per directory (for sharded layouts) or zero (for linear layouts). */ int max_files_per_dir; + /* If set, this FS is using logical addressing. Otherwise, it is using + physical addressing. */ + svn_boolean_t use_log_addressing; + + /* Rev / pack file read granularity in bytes. */ + apr_int64_t block_size; + + /* Capacity in entries of log-to-phys index pages */ + apr_int64_t l2p_page_size; + + /* Rev / pack file granularity (in bytes) covered by a single phys-to-log + * index page. */ + apr_int64_t p2l_page_size; + + /* If set, parse and cache *all* data of each block that we read + * (not just the one bit that we need, atm). */ + svn_boolean_t use_block_read; + /* The revision that was youngest, last time we checked. */ svn_revnum_t youngest_rev_cache; - /* The fsfs.conf file, parsed. Allocated in FS->pool. */ - svn_config_t *config; - - /* Caches of immutable data. (Note that if these are created with - svn_cache__create_memcache, the data can be shared between + /* Caches of immutable data. (Note that these may be shared between multiple svn_fs_t's for the same filesystem.) */ + /* Access to the configured memcached instances. May be NULL. */ + svn_memcache_t *memcache; + + /* If TRUE, don't ignore any cache-related errors. If FALSE, errors from + e.g. memcached may be ignored as caching is an optional feature. */ + svn_boolean_t fail_stop; + /* A cache of revision root IDs, mapping from (svn_revnum_t *) to (svn_fs_id_t *). (Not threadsafe.) */ svn_cache__t *rev_root_id_cache; @@ -269,21 +353,6 @@ typedef struct fs_fs_data_t rep key (revision/offset) to svn_stringbuf_t. */ svn_cache__t *fulltext_cache; - /* Access object to the atomics namespace used by revprop caching. - Will be NULL until the first access. */ - svn_atomic_namespace__t *revprop_namespace; - - /* Access object to the revprop "generation". Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_generation; - - /* Access object to the revprop update timeout. Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_timeout; - - /* Revision property cache. Maps from (rev,generation) to apr_hash_t. */ - svn_cache__t *revprop_cache; - /* Node properties cache. Maps from rep key to apr_hash_t. */ svn_cache__t *properties_cache; @@ -293,20 +362,28 @@ typedef struct fs_fs_data_t respective pack file. */ svn_cache__t *packed_offset_cache; - /* Cache for txdelta_window_t objects; the key is (revFilePath, offset) */ + /* Cache for svn_fs_fs__raw_cached_window_t objects; the key is + window_cache_key_t. */ + svn_cache__t *raw_window_cache; + + /* Cache for txdelta_window_t objects; the key is window_cache_key_t */ svn_cache__t *txdelta_window_cache; /* Cache for combined windows as svn_stringbuf_t objects; - the key is (revFilePath, offset) */ + the key is window_cache_key_t */ svn_cache__t *combined_window_cache; - /* Cache for node_revision_t objects; the key is (revision, id offset) */ + /* Cache for node_revision_t objects; the key is (revision, item_index) */ svn_cache__t *node_revision_cache; /* Cache for change lists as APR arrays of change_t * objects; the key is the revision */ svn_cache__t *changes_cache; + /* Cache for svn_fs_fs__rep_header_t objects; the key is a + (revision, item index) pair */ + svn_cache__t *rep_header_cache; + /* Cache for svn_mergeinfo_t objects; the key is a combination of revision, inheritance flags and path. */ svn_cache__t *mergeinfo_cache; @@ -316,6 +393,23 @@ typedef struct fs_fs_data_t if the node has mergeinfo, "0" if it doesn't. */ svn_cache__t *mergeinfo_existence_cache; + /* Cache for l2p_header_t objects; the key is (revision, is-packed). + Will be NULL for pre-format7 repos */ + svn_cache__t *l2p_header_cache; + + /* Cache for l2p_page_t objects; the key is svn_fs_fs__page_cache_key_t. + Will be NULL for pre-format7 repos */ + svn_cache__t *l2p_page_cache; + + /* Cache for p2l_header_t objects; the key is (revision, is-packed). + Will be NULL for pre-format7 repos */ + svn_cache__t *p2l_header_cache; + + /* Cache for apr_array_header_t objects containing svn_fs_fs__p2l_entry_t + elements; the key is svn_fs_fs__page_cache_key_t. + Will be NULL for pre-format7 repos */ + svn_cache__t *p2l_page_cache; + /* TRUE while the we hold a lock on the write lock file. */ svn_boolean_t has_write_lock; @@ -363,19 +457,27 @@ typedef struct fs_fs_data_t * deltification history after which skip deltas will be used. */ apr_int64_t max_linear_deltification; + /* Compression level to use with txdelta storage format in new revs. */ + int delta_compression_level; + + /* Pack after every commit. */ + svn_boolean_t pack_after_commit; + + /* Per-instance filesystem ID, which provides an additional level of + uniqueness for filesystems that share the same UUID, but should + still be distinguishable (e.g. backups produced by svn_fs_hotcopy() + or dump / load cycles). */ + const char *instance_id; + /* Pointer to svn_fs_open. */ svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, - apr_pool_t *); + apr_pool_t *, apr_pool_t *); } fs_fs_data_t; /*** Filesystem Transaction ***/ typedef struct transaction_t { - /* property list (const char * name, svn_string_t * value). - may be NULL if there are no properties. */ - apr_hash_t *proplist; - /* node revision id of the root node. */ const svn_fs_id_t *root_id; @@ -395,26 +497,26 @@ typedef struct transaction_t * svn_fs_fs__rep_copy. */ typedef struct representation_t { - /* Checksums for the contents produced by this representation. + /* Checksums digests for the contents produced by this representation. This checksum is for the contents the rep shows to consumers, regardless of how the rep stores the data under the hood. It is independent of the storage (fulltext, delta, whatever). - If checksum is NULL, then for compatibility behave as though this + If has_sha1 is FALSE, then for compatibility behave as though this checksum matches the expected checksum. The md5 checksum is always filled, unless this is rep which was retrieved from the rep-cache. The sha1 checksum is only computed on - a write, for use with rep-sharing; it may be read from an existing - representation, but otherwise it is NULL. */ - svn_checksum_t *md5_checksum; - svn_checksum_t *sha1_checksum; + a write, for use with rep-sharing. */ + svn_boolean_t has_sha1; + unsigned char sha1_digest[APR_SHA1_DIGESTSIZE]; + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; /* Revision where this representation is located. */ svn_revnum_t revision; - /* Offset into the revision file where it is located. */ - apr_off_t offset; + /* Item index with the the revision. */ + apr_uint64_t item_index; /* The size of the representation in bytes as seen in the revision file. */ @@ -424,17 +526,21 @@ typedef struct representation_t * the fulltext size is equal to representation size in the rev file, */ svn_filesize_t expanded_size; - /* Is this representation a transaction? */ - const char *txn_id; + /* Is this a representation (still) within a transaction? */ + svn_fs_fs__id_part_t txn_id; /* For rep-sharing, we need a way of uniquifying node-revs which share the same representation (see svn_fs_fs__noderev_same_rep_key() ). So, we store the original txn of the node rev (not the rep!), along with some - intra-node uniqification content. - - May be NULL, in which case, it is considered to match other NULL - values.*/ - const char *uniquifier; + intra-node uniqification content. */ + struct + { + /* unique context, i.e. txn ID, in which the noderev (!) got created */ + svn_fs_fs__id_part_t noderev_txn_id; + + /* unique value within that txn */ + apr_uint64_t number; + } uniquifier; } representation_t; @@ -494,25 +600,10 @@ typedef struct node_revision_t typedef struct change_t { /* Path of the change. */ - const char *path; - - /* Node revision ID of the change. */ - const svn_fs_id_t *noderev_id; - - /* The kind of change. */ - svn_fs_path_change_kind_t kind; - - /* Text or property mods? */ - svn_boolean_t text_mod; - svn_boolean_t prop_mod; - - /* Node kind (possibly svn_node_unknown). */ - svn_node_kind_t node_kind; - - /* Copyfrom revision and path. */ - svn_revnum_t copyfrom_rev; - const char * copyfrom_path; + svn_string_t path; + /* API compatible change description */ + svn_fs_path_change2_t info; } change_t; diff --git a/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.c b/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.c index 82acb3c96..1d32672b8 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.c @@ -20,59 +20,35 @@ * ==================================================================== */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include +#include "fs_fs.h" + #include -#include -#include -#include -#include -#include - -#include "svn_pools.h" -#include "svn_fs.h" -#include "svn_dirent_uri.h" -#include "svn_path.h" + +#include "svn_private_config.h" + +#include "svn_checksum.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_dirent_uri.h" +#include "svn_sorts.h" #include "svn_version.h" -#include "fs.h" -#include "tree.h" -#include "lock.h" -#include "key-gen.h" -#include "fs_fs.h" +#include "cached_data.h" #include "id.h" +#include "index.h" #include "rep-cache.h" -#include "temp_serializer.h" +#include "revprops.h" +#include "transaction.h" +#include "tree.h" +#include "util.h" -#include "private/svn_string_private.h" #include "private/svn_fs_util.h" +#include "private/svn_io_private.h" +#include "private/svn_string_private.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 @@ -96,53 +72,6 @@ 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 @@ -155,59 +84,13 @@ 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); +get_youngest(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *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) { @@ -226,822 +109,393 @@ 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) + +/* 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) { - return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool); + return svn_error_trace(svn_io__file_lock_autocreate(lock_filename, pool)); } -static APR_INLINE const char * -path_lock(svn_fs_t *fs, apr_pool_t *pool) +/* 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) { - return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool); + fs_fs_data_t *ffd = baton_void; + ffd->has_write_lock = FALSE; + return APR_SUCCESS; } -static const char * -path_revprop_generation(svn_fs_t *fs, apr_pool_t *pool) -{ - return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool); -} +/* Structure defining a file system lock to be acquired and the function + to be executed while the lock is held. -static const char * -path_rev_packed(svn_fs_t *fs, svn_revnum_t rev, const char *kind, - apr_pool_t *pool) + Instances of this structure may be nested to allow for multiple locks to + be taken out before executing the user-provided body. In that case, BODY + and BATON of the outer instances will be with_lock and a with_lock_baton_t + instance (transparently, no special treatment is required.). It is + illegal to attempt to acquire the same lock twice within the same lock + chain or via nesting calls using separate lock chains. + + All instances along the chain share the same LOCK_POOL such that only one + pool needs to be created and cleared for all locks. We also allocate as + much data from that lock pool as possible to minimize memory usage in + caller pools. */ +typedef struct with_lock_baton_t { - fs_fs_data_t *ffd = fs->fsap_data; + /* The filesystem we operate on. Same for all instances along the chain. */ + svn_fs_t *fs; - assert(ffd->max_files_per_dir); - assert(is_packed_rev(fs, rev)); + /* Mutex to complement the lock file in an APR threaded process. + No-op object for non-threaded processes but never NULL. */ + svn_mutex__t *mutex; - 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); -} + /* Path to the file to lock. */ + const char *lock_path; -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; + /* If true, set FS->HAS_WRITE_LOCK after we acquired the lock. */ + svn_boolean_t is_global_lock; - 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); -} + /* Function body to execute after we acquired the lock. + This may be user-provided or a nested call to with_lock(). */ + svn_error_t *(*body)(void *baton, + apr_pool_t *pool); -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; + /* Baton to pass to BODY; possibly NULL. + This may be user-provided or a nested lock baton instance. */ + void *baton; - assert(! is_packed_rev(fs, rev)); + /* Pool for all allocations along the lock chain and BODY. Will hold the + file locks and gets destroyed after the outermost BODY returned, + releasing all file locks. + Same for all instances along the chain. */ + apr_pool_t *lock_pool; - if (ffd->max_files_per_dir) - { - return svn_dirent_join(path_rev_shard(fs, rev, pool), - apr_psprintf(pool, "%ld", rev), - pool); - } + /* TRUE, iff BODY is the user-provided body. */ + svn_boolean_t is_inner_most_lock; - return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, - apr_psprintf(pool, "%ld", rev), NULL); -} + /* TRUE, iff this is not a nested lock. + Then responsible for destroying LOCK_POOL. */ + svn_boolean_t is_outer_most_lock; +} with_lock_baton_t; -svn_error_t * -svn_fs_fs__path_rev_absolute(const char **path, - svn_fs_t *fs, - svn_revnum_t rev, - apr_pool_t *pool) +/* Obtain a write lock on the file BATON->LOCK_PATH and call BATON->BODY + with BATON->BATON. If this is the outermost lock call, release all file + locks after the body returned. If BATON->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(with_lock_baton_t *baton) { - fs_fs_data_t *ffd = fs->fsap_data; + apr_pool_t *pool = baton->lock_pool; + svn_error_t *err = get_lock_on_filesystem(baton->lock_path, pool); - if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT - || ! is_packed_rev(fs, rev)) - { - *path = path_rev(fs, rev, pool); - } - else + if (!err) { - *path = path_rev_packed(fs, rev, PATH_PACKED, pool); + svn_fs_t *fs = baton->fs; + fs_fs_data_t *ffd = fs->fsap_data; + + if (baton->is_global_lock) + { + /* set the "got the lock" flag and register reset function */ + apr_pool_cleanup_register(pool, + 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 (baton->is_inner_most_lock) + { + if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) + err = svn_fs_fs__update_min_unpacked_rev(fs, pool); + if (!err) + err = get_youngest(&ffd->youngest_rev_cache, fs, pool); + } + + if (!err) + err = baton->body(baton->baton, pool); } - return SVN_NO_ERROR; + if (baton->is_outer_most_lock) + svn_pool_destroy(pool); + + return svn_error_trace(err); } -static const char * -path_revprops_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +/* Wraps with_some_lock_file, protecting it with BATON->MUTEX. + + POOL is unused here and only provided for signature compatibility with + WITH_LOCK_BATON_T.BODY. */ +static svn_error_t * +with_lock(void *baton, + apr_pool_t *pool) { - fs_fs_data_t *ffd = fs->fsap_data; + with_lock_baton_t *lock_baton = baton; + SVN_MUTEX__WITH_LOCK(lock_baton->mutex, with_some_lock_file(lock_baton)); - 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); + return SVN_NO_ERROR; } -static const char * -path_revprops_pack_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +/* Enum identifying a filesystem lock. */ +typedef enum lock_id_t { - 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); -} + write_lock, + txn_lock, + pack_lock +} lock_id_t; -static const char * -path_revprops(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +/* Initialize BATON->MUTEX, BATON->LOCK_PATH and BATON->IS_GLOBAL_LOCK + according to the LOCK_ID. All other members of BATON must already be + valid. */ +static void +init_lock_baton(with_lock_baton_t *baton, + lock_id_t lock_id) { - fs_fs_data_t *ffd = fs->fsap_data; + fs_fs_data_t *ffd = baton->fs->fsap_data; + fs_fs_shared_data_t *ffsd = ffd->shared; - if (ffd->max_files_per_dir) + switch (lock_id) { - return svn_dirent_join(path_revprops_shard(fs, rev, pool), - apr_psprintf(pool, "%ld", rev), - pool); - } + case write_lock: + baton->mutex = ffsd->fs_write_lock; + baton->lock_path = svn_fs_fs__path_lock(baton->fs, baton->lock_pool); + baton->is_global_lock = TRUE; + break; - return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, - apr_psprintf(pool, "%ld", rev), NULL); -} + case txn_lock: + baton->mutex = ffsd->txn_current_lock; + baton->lock_path = svn_fs_fs__path_txn_current_lock(baton->fs, + baton->lock_pool); + baton->is_global_lock = FALSE; + break; -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); + case pack_lock: + baton->mutex = ffsd->fs_pack_lock; + baton->lock_path = svn_fs_fs__path_pack_lock(baton->fs, + baton->lock_pool); + baton->is_global_lock = FALSE; + break; + } } -/* Return the name of the sha1->rep mapping file in transaction TXN_ID - * within FS for the given SHA1 checksum. Use POOL for allocations. +/* Return the baton for the innermost lock of a (potential) lock chain. + The baton shall take out LOCK_ID from FS and execute BODY with BATON + while the lock is being held. Allocate the result in a sub-pool of POOL. */ -static APR_INLINE const char * -path_txn_sha1(svn_fs_t *fs, const char *txn_id, svn_checksum_t *sha1, - apr_pool_t *pool) +static with_lock_baton_t * +create_lock_baton(svn_fs_t *fs, + lock_id_t lock_id, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool) { - return svn_dirent_join(path_txn_dir(fs, txn_id, pool), - svn_checksum_to_cstring(sha1, pool), - pool); -} + /* Allocate everything along the lock chain into a single sub-pool. + This minimizes memory usage and cleanup overhead. */ + apr_pool_t *lock_pool = svn_pool_create(pool); + with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); -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); -} + /* Store parameters. */ + result->fs = fs; + result->body = body; + result->baton = baton; -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); -} + /* File locks etc. will use this pool as well for easy cleanup. */ + result->lock_pool = lock_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); + /* Right now, we are the first, (only, ) and last struct in the chain. */ + result->is_inner_most_lock = TRUE; + result->is_outer_most_lock = TRUE; + + /* Select mutex and lock file path depending on LOCK_ID. + Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ + init_lock_baton(result, lock_id); + + return result; } -static APR_INLINE const char * -path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool) +/* Return a baton that wraps NESTED and requests LOCK_ID as additional lock. + * + * That means, when you create a lock chain, start with the last / innermost + * lock to take out and add the first / outermost lock last. + */ +static with_lock_baton_t * +chain_lock_baton(lock_id_t lock_id, + with_lock_baton_t *nested) { - return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool); -} + /* Use the same pool for batons along the lock chain. */ + apr_pool_t *lock_pool = nested->lock_pool; + with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); + /* All locks along the chain operate on the same FS. */ + result->fs = nested->fs; -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); -} + /* Execution of this baton means acquiring the nested lock and its + execution. */ + result->body = with_lock; + result->baton = nested; -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); -} + /* Shared among all locks along the chain. */ + result->lock_pool = 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); + /* We are the new outermost lock but surely not the innermost lock. */ + result->is_inner_most_lock = FALSE; + result->is_outer_most_lock = TRUE; + nested->is_outer_most_lock = FALSE; - return svn_dirent_join(path_txn_dir(fs, txn_id, pool), name, pool); -} + /* Select mutex and lock file path depending on LOCK_ID. + Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ + init_lock_baton(result, lock_id); -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); + return result; } -static APR_INLINE const char * -path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool) +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) { - return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), - PATH_EXT_CHILDREN, (char *)NULL); + return svn_error_trace( + with_lock(create_lock_baton(fs, write_lock, body, baton, pool), + pool)); } -static APR_INLINE const char * -path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool) +svn_error_t * +svn_fs_fs__with_pack_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + 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); + return svn_error_trace( + with_lock(create_lock_baton(fs, pack_lock, body, baton, pool), + pool)); } -static APR_INLINE const char * -path_and_offset_of(apr_file_t *file, apr_pool_t *pool) +svn_error_t * +svn_fs_fs__with_txn_current_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + 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); + return svn_error_trace( + with_lock(create_lock_baton(fs, txn_lock, body, baton, pool), + pool)); } - - -/* 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) +svn_error_t * +svn_fs_fs__with_all_locks(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; - 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; - } + /* Be sure to use the correct lock ordering as documented in + fs_fs_shared_data_t. The lock chain is being created in + innermost (last to acquire) -> outermost (first to acquire) order. */ + with_lock_baton_t *lock_baton + = create_lock_baton(fs, write_lock, body, baton, pool); - assert(strlen(txn_id) < sizeof(txn->txn_id)); - apr_cpystrn(txn->txn_id, txn_id, sizeof(txn->txn_id)); - txn->being_written = FALSE; + if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) + lock_baton = chain_lock_baton(pack_lock, lock_baton); - /* 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; + if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) + lock_baton = chain_lock_baton(txn_lock, lock_baton); - return txn; + return svn_error_trace(with_lock(lock_baton, pool)); } -/* 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; +/* 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. - /* 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); + 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 svn_fs_fs__check_file_buffer_numeric(buf, offset, path, "Format", + 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. */ +/* 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 * -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) +check_format(int format) { - fs_fs_data_t *ffd = fs->fsap_data; - fs_fs_shared_data_t *ffsd = ffd->shared; + /* 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); - SVN_MUTEX__WITH_LOCK(ffsd->txn_list_lock, - body(fs, baton, pool)); + /* We support all formats from 1-current simultaneously */ + if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER) + return SVN_NO_ERROR; - 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, *MAX_FILES_PER_DIR and + USE_LOG_ADDRESSIONG respectively. -/* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */ + *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_LOG_ADDRESSIONG is obtained from the 'addressing' format option, + and will be set to FALSE for physical addressing. + + Use POOL for temporary allocation. */ static svn_error_t * -get_lock_on_filesystem(const char *lock_filename, - apr_pool_t *pool) +read_format(int *pformat, + int *max_files_per_dir, + svn_boolean_t *use_log_addressing, + const char *path, + apr_pool_t *pool) { - svn_error_t *err = svn_io_file_lock2(lock_filename, TRUE, FALSE, 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)) { - /* No lock file? No big deal; these are just empty files - anyway. Create it and try again. */ + /* 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); - err = NULL; + *pformat = 1; + *max_files_per_dir = 0; - SVN_ERR(svn_io_file_create(lock_filename, "", pool)); - SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool)); + return SVN_NO_ERROR; } - - 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); + SVN_ERR(err); stream = svn_stream_from_stringbuf(content, pool); SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool)); @@ -1062,6 +516,7 @@ read_format(int *pformat, int *max_files_per_dir, /* Set the default values for anything that can be set via an option. */ *max_files_per_dir = 0; + *use_log_addressing = FALSE; /* Read any options. */ while (!eos) @@ -1088,38 +543,75 @@ read_format(int *pformat, int *max_files_per_dir, } } + if (*pformat >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT && + strncmp(buf->data, "addressing ", 11) == 0) + { + if (strcmp(buf->data + 11, "physical") == 0) + { + *use_log_addressing = FALSE; + continue; + } + + if (strcmp(buf->data + 11, "logical") == 0) + { + *use_log_addressing = TRUE; + 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); } + /* Non-sharded repositories never use logical addressing. + * If the format file is inconsistent in that respect, something + * probably went wrong. + */ + if (*use_log_addressing && !*max_files_per_dir) + return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, + _("'%s' specifies logical addressing for a non-sharded repository"), + svn_dirent_local_style(path, pool)); + 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. +/* Write the format number, maximum number of files per directory and + the addressing scheme 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_error_t * +svn_fs_fs__write_format(svn_fs_t *fs, + svn_boolean_t overwrite, + apr_pool_t *pool) { svn_stringbuf_t *sb; + fs_fs_data_t *ffd = fs->fsap_data; + const char *path = path_format(fs, pool); - SVN_ERR_ASSERT(1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER); + SVN_ERR_ASSERT(1 <= ffd->format + && ffd->format <= SVN_FS_FS__FORMAT_NUMBER); - sb = svn_stringbuf_createf(pool, "%d\n", format); + sb = svn_stringbuf_createf(pool, "%d\n", ffd->format); - if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) + if (ffd->format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) { - if (max_files_per_dir) + if (ffd->max_files_per_dir) svn_stringbuf_appendcstr(sb, apr_psprintf(pool, "layout sharded %d\n", - max_files_per_dir)); + ffd->max_files_per_dir)); else svn_stringbuf_appendcstr(sb, "layout linear\n"); } + if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) + { + if (ffd->use_log_addressing) + svn_stringbuf_appendcstr(sb, "addressing logical\n"); + else + svn_stringbuf_appendcstr(sb, "addressing physical\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. */ @@ -1130,15 +622,8 @@ write_format(const char *path, int format, int max_files_per_dir, } 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)); + SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len, + NULL /* copy_perms_path */, pool)); } /* And set the perms to make it read only */ @@ -1152,21 +637,68 @@ svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs) return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT; } +/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within + * the range of what the current system may address in RAM and it is a + * power of 2. Assume that the element size within the block is ITEM_SIZE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +verify_block_size(apr_int64_t block_size, + apr_size_t item_size, + const char *name, + apr_pool_t *scratch_pool + ) +{ + /* Limit range. */ + if (block_size <= 0) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too small for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + if (block_size > SVN_MAX_OBJECT_SIZE / item_size) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too large for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + /* Ensure it is a power of two. + * For positive X, X & (X-1) will reset the lowest bit set. + * If the result is 0, at most one bit has been set. */ + if (0 != (block_size & (block_size - 1))) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is invalid for fsfs.conf setting '%s' " + "because it is not a power of 2."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + return SVN_NO_ERROR; +} + /* Read the configuration information of the file system at FS_PATH - * and set the respective values in FFD. Use POOL for allocations. + * and set the respective values in FFD. Use pools as usual. */ static svn_error_t * read_config(fs_fs_data_t *ffd, const char *fs_path, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - SVN_ERR(svn_config_read3(&ffd->config, - svn_dirent_join(fs_path, PATH_CONFIG, pool), - FALSE, FALSE, FALSE, pool)); + svn_config_t *config; + + SVN_ERR(svn_config_read3(&config, + svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool), + FALSE, FALSE, FALSE, scratch_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, + SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed, CONFIG_SECTION_REP_SHARING, CONFIG_OPTION_ENABLE_REP_SHARING, TRUE)); else @@ -1175,22 +707,32 @@ read_config(fs_fs_data_t *ffd, /* 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, + apr_int64_t compression_level; + + SVN_ERR(svn_config_get_bool(config, &ffd->deltify_directories, CONFIG_SECTION_DELTIFICATION, CONFIG_OPTION_ENABLE_DIR_DELTIFICATION, - FALSE)); - SVN_ERR(svn_config_get_bool(ffd->config, &ffd->deltify_properties, + TRUE)); + SVN_ERR(svn_config_get_bool(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, + TRUE)); + SVN_ERR(svn_config_get_int64(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, + SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification, CONFIG_SECTION_DELTIFICATION, CONFIG_OPTION_MAX_LINEAR_DELTIFICATION, SVN_FS_FS_MAX_LINEAR_DELTIFICATION)); + + SVN_ERR(svn_config_get_int64(config, &compression_level, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_COMPRESSION_LEVEL, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); + ffd->delta_compression_level + = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level), + SVN_DELTA_COMPRESSION_LEVEL_MAX); } else { @@ -1198,21 +740,22 @@ read_config(fs_fs_data_t *ffd, ffd->deltify_properties = FALSE; ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK; ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION; + ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; } /* 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, + SVN_ERR(svn_config_get_bool(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, + SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size, CONFIG_SECTION_PACKED_REVPROPS, CONFIG_OPTION_REVPROP_PACK_SIZE, ffd->compress_packed_revprops - ? 0x100 - : 0x40)); + ? 0x10 + : 0x4)); ffd->revprop_pack_size *= 1024; } @@ -1222,6 +765,64 @@ read_config(fs_fs_data_t *ffd, ffd->compress_packed_revprops = FALSE; } + if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) + { + SVN_ERR(svn_config_get_int64(config, &ffd->block_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_BLOCK_SIZE, + 64)); + SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_L2P_PAGE_SIZE, + 0x2000)); + SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_P2L_PAGE_SIZE, + 0x400)); + + /* Don't accept unreasonable or illegal values. + * Block size and P2L page size are in kbytes; + * L2P blocks are arrays of apr_off_t. */ + SVN_ERR(verify_block_size(ffd->block_size, 0x400, + CONFIG_OPTION_BLOCK_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400, + CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t), + CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool)); + + /* convert kBytes to bytes */ + ffd->block_size *= 0x400; + ffd->p2l_page_size *= 0x400; + /* L2P pages are in entries - not in (k)Bytes */ + } + else + { + /* should be irrelevant but we initialize them anyway */ + ffd->block_size = 0x1000; /* Matches default APR file buffer size. */ + ffd->l2p_page_size = 0x2000; /* Matches above default. */ + ffd->p2l_page_size = 0x100000; /* Matches above default in bytes. */ + } + + if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) + { + SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit, + CONFIG_SECTION_DEBUG, + CONFIG_OPTION_PACK_AFTER_COMMIT, + FALSE)); + } + else + { + ffd->pack_after_commit = FALSE; + } + + /* memcached configuration */ + SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config, + result_pool, scratch_pool)); + + SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop, + CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, + FALSE)); + return SVN_NO_ERROR; } @@ -1286,20 +887,20 @@ write_config(svn_fs_t *fs, "###" 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 +"### should be enabled consistently over the lifetime 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 +"### In rarely accessed repositories, the I/O overhead may be significant" NL +"### as caches will most likely be low." NL +"### directory deltification is enabled by default." NL +"# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = true" 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 +"### property deltification is enabled by default." NL +"# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = true" 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 @@ -1331,6 +932,24 @@ write_config(svn_fs_t *fs, "### 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 +"### After deltification, we compress the data through zlib to minimize on-" NL +"### disk size. That can be an expensive and ineffective process. This" NL +"### setting controls the usage of zlib in future revisions." NL +"### Revisions with highly compressible data in them may shrink in size" NL +"### if the setting is increased but may take much longer to commit. The" NL +"### time taken to uncompress that data again is widely independent of the" NL +"### compression level." NL +"### Compression will be ineffective if the incoming content is already" NL +"### highly compressed. In that case, disabling the compression entirely" NL +"### will speed up commits as well as reading the data. Repositories with" NL +"### many small compressible files (source code) but also a high percentage" NL +"### of large incompressible ones (artwork) may benefit from compression" NL +"### levels lowered to e.g. 1." NL +"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL +"### and 0 disabling it altogether." NL +"### The default value is 5." NL +"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5" NL "" NL "[" CONFIG_SECTION_PACKED_REVPROPS "]" NL "### This parameter controls the size (in kBytes) of packed revprop files." NL @@ -1340,96 +959,174 @@ write_config(svn_fs_t *fs, "### 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 +"### latency and CPU usage reading and changing individual revprops." NL +"### Values smaller than 4 kByte will not improve latency any further and " NL +"### quickly render revprop packing ineffective." NL +"### revprop-pack-size is 4 kBytes by default for non-compressed revprop" NL +"### pack files and 16 kBytes when compression has been enabled." NL +"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4" 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 +"### even more so writing, become significantly more CPU intensive." NL "### Compressing packed revprops is disabled by default." NL "# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false" NL +"" NL +"[" CONFIG_SECTION_IO "]" NL +"### Parameters in this section control the data access granularity in" NL +"### format 7 repositories and later. The defaults should translate into" NL +"### decent performance over a wide range of setups." NL +"###" NL +"### When a specific piece of information needs to be read from disk, a" NL +"### data block is being read at once and its contents are being cached." NL +"### If the repository is being stored on a RAID, the block size should be" NL +"### either 50% or 100% of RAID block size / granularity. Also, your file" NL +"### system blocks/clusters should be properly aligned and sized. In that" NL +"### setup, each access will hit only one disk (minimizes I/O load) but" NL +"### uses all the data provided by the disk in a single access." NL +"### For SSD-based storage systems, slightly lower values around 16 kB" NL +"### may improve latency while still maximizing throughput. If block-read" NL +"### has not been enabled, this will be capped to 4 kBytes." NL +"### Can be changed at any time but must be a power of 2." NL +"### block-size is given in kBytes and with a default of 64 kBytes." NL +"# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL +"###" NL +"### The log-to-phys index maps data item numbers to offsets within the" NL +"### rev or pack file. This index is organized in pages of a fixed maximum" NL +"### capacity. To access an item, the page table and the respective page" NL +"### must be read." NL +"### This parameter only affects revisions with thousands of changed paths." NL +"### If you have several extremely large revisions (~1 mio changes), think" NL +"### about increasing this setting. Reducing the value will rarely result" NL +"### in a net speedup." NL +"### This is an expert setting. Must be a power of 2." NL +"### l2p-page-size is 8192 entries by default." NL +"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL +"###" NL +"### The phys-to-log index maps positions within the rev or pack file to" NL +"### to data items, i.e. describes what piece of information is being" NL +"### stored at any particular offset. The index describes the rev file" NL +"### in chunks (pages) and keeps a global list of all those pages. Large" NL +"### pages mean a shorter page table but a larger per-page description of" NL +"### data items in it. The latency sweetspot depends on the change size" NL +"### distribution but covers a relatively wide range." NL +"### If the repository contains very large files, i.e. individual changes" NL +"### of tens of MB each, increasing the page size will shorten the index" NL +"### file at the expense of a slightly increased latency in sections with" NL +"### smaller changes." NL +"### For source code repositories, this should be about 16x the block-size." NL +"### Must be a power of 2." NL +"### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL +"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL ; #undef NL return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool), fsfs_conf_contents, pool); } +/* Read / Evaluate the global configuration in FS->CONFIG to set up + * parameters in FS. */ static svn_error_t * -read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, - const char *path, - apr_pool_t *pool) +read_global_config(svn_fs_t *fs) { - char buf[80]; - apr_file_t *file; - apr_size_t len; + fs_fs_data_t *ffd = fs->fsap_data; + + /* Providing a config hash is optional. */ + if (fs->config) + ffd->use_block_read = svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_FSFS_BLOCK_READ, + FALSE); + else + ffd->use_block_read = FALSE; - 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)); + /* Ignore the user-specified larger block size if we don't use block-read. + Defaulting to 4k gives us the same access granularity in format 7 as in + older formats. */ + if (!ffd->use_block_read) + ffd->block_size = MIN(0x1000, ffd->block_size); - *min_unpacked_rev = SVN_STR_TO_REV(buf); return SVN_NO_ERROR; } +/* Read FS's UUID file and store the data in the FS struct. */ static svn_error_t * -update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool) +read_uuid(svn_fs_t *fs, + apr_pool_t *scratch_pool) { fs_fs_data_t *ffd = fs->fsap_data; + apr_file_t *uuid_file; + char buf[APR_UUID_FORMATTED_LENGTH + 2]; + apr_size_t limit; + + /* Read the repository uuid. */ + SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + limit = sizeof(buf); + SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool)); + fs->uuid = apr_pstrdup(fs->pool, buf); + + /* Read the instance ID. */ + if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) + { + limit = sizeof(buf); + SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, + scratch_pool)); + ffd->instance_id = apr_pstrdup(fs->pool, buf); + } + else + { + ffd->instance_id = fs->uuid; + } - SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT); + SVN_ERR(svn_io_file_close(uuid_file, scratch_pool)); - return read_min_unpacked_rev(&ffd->min_unpacked_rev, - path_min_unpacked_rev(fs, pool), - pool); + return SVN_NO_ERROR; } svn_error_t * -svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool) +svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_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); + svn_boolean_t use_log_addressing; - /* Read the FS format number. */ - SVN_ERR(read_format(&format, &max_files_per_dir, - path_format(fs, pool), pool)); + /* Read info from format file. */ + SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, + path_format(fs, scratch_pool), scratch_pool)); - /* Now we've got a format number no matter what. */ + /* Now that we've got *all* info, store / update values in FFD. */ ffd->format = format; ffd->max_files_per_dir = max_files_per_dir; + ffd->use_log_addressing = use_log_addressing; - /* 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)); + return SVN_NO_ERROR; +} - limit = sizeof(buf); - SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, pool)); - fs->uuid = apr_pstrdup(fs->pool, buf); +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; + fs->path = apr_pstrdup(fs->pool, path); + + /* Read the FS format file. */ + SVN_ERR(svn_fs_fs__read_format_file(fs, pool)); - SVN_ERR(svn_io_file_close(uuid_file, pool)); + /* Read in and cache the repository uuid. */ + SVN_ERR(read_uuid(fs, pool)); /* Read the min unpacked revision. */ if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) - SVN_ERR(update_min_unpacked_rev(fs, pool)); + SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); /* Read the configuration file. */ - SVN_ERR(read_config(ffd, fs->path, pool)); + SVN_ERR(read_config(ffd, fs->path, fs->pool, pool)); + + /* Global configuration options. */ + SVN_ERR(read_global_config(fs)); - return get_youngest(&(ffd->youngest_rev_cache), path, pool); + return get_youngest(&(ffd->youngest_rev_cache), fs, pool); } /* Wrapper around svn_io_file_create which ignores EEXIST. */ @@ -1447,125 +1144,32 @@ create_file_ignore_eexist(const char *file, 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) +/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying + * parameters over between them. */ +struct upgrade_baton_t { - 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; -} + svn_fs_t *fs; + svn_fs_upgrade_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_baton; +}; static svn_error_t * upgrade_body(void *baton, apr_pool_t *pool) { - svn_fs_t *fs = baton; + struct upgrade_baton_t *upgrade_baton = baton; + svn_fs_t *fs = upgrade_baton->fs; + fs_fs_data_t *ffd = fs->fsap_data; int format, max_files_per_dir; + svn_boolean_t use_log_addressing; 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)); + SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, + 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), @@ -1589,29 +1193,30 @@ upgrade_body(void *baton, apr_pool_t *pool) if (format == SVN_FS_FS__FORMAT_NUMBER) return SVN_NO_ERROR; - /* If our filesystem predates the existance of the 'txn-current + /* If our filesystem predates the existence 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)); + SVN_ERR(create_file_ignore_eexist( + svn_fs_fs__path_txn_current(fs, pool), "0\n", + pool)); + SVN_ERR(create_file_ignore_eexist( + svn_fs_fs__path_txn_current_lock(fs, pool), "", + pool)); } - /* If our filesystem predates the existance of the 'txn-protorevs' + /* If our filesystem predates the existence 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)); + svn_fs_fs__path_txn_proto_revs(fs, 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)); + SVN_ERR(svn_io_file_create(svn_fs_fs__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 @@ -1622,16 +1227,46 @@ upgrade_body(void *baton, apr_pool_t *pool) && max_files_per_dir > 0) { needs_revprop_shard_cleanup = TRUE; - SVN_ERR(upgrade_pack_revprops(fs, pool)); - } + SVN_ERR(svn_fs_fs__upgrade_pack_revprops(fs, + upgrade_baton->notify_func, + upgrade_baton->notify_baton, + upgrade_baton->cancel_func, + upgrade_baton->cancel_baton, + pool)); + } + + /* We will need the UUID info shortly ... + Read it before the format bump as the UUID file still uses the old + format. */ + SVN_ERR(read_uuid(fs, pool)); + + /* Update the format info in the FS struct. Upgrade steps further + down will use the format from FS to create missing info. */ + ffd->format = SVN_FS_FS__FORMAT_NUMBER; + ffd->max_files_per_dir = max_files_per_dir; + ffd->use_log_addressing = use_log_addressing; + + /* Always add / bump the instance ID such that no form of caching + accidentally uses outdated information. Keep the UUID. */ + SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool)); /* Bump the format file. */ - SVN_ERR(write_format(format_path, SVN_FS_FS__FORMAT_NUMBER, - max_files_per_dir, TRUE, pool)); + SVN_ERR(svn_fs_fs__write_format(fs, TRUE, pool)); + + if (upgrade_baton->notify_func) + SVN_ERR(upgrade_baton->notify_func(upgrade_baton->notify_baton, + SVN_FS_FS__FORMAT_NUMBER, + svn_fs_upgrade_format_bumped, + pool)); /* Now, it is safe to remove the redundant revprop files. */ if (needs_revprop_shard_cleanup) - SVN_ERR(upgrade_cleanup_pack_revprops(fs, pool)); + SVN_ERR(svn_fs_fs__upgrade_cleanup_pack_revprops(fs, + upgrade_baton->notify_func, + upgrade_baton->notify_baton, + upgrade_baton->cancel_func, + upgrade_baton->cancel_baton, + pool)); /* Done */ return SVN_NO_ERROR; @@ -1639,121 +1274,21 @@ upgrade_body(void *baton, apr_pool_t *pool) 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) +svn_fs_fs__upgrade(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + 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)); + struct upgrade_baton_t baton; + baton.fs = fs; + baton.notify_func = notify_func; + baton.notify_baton = notify_baton; + baton.cancel_func = cancel_func; + baton.cancel_baton = cancel_baton; - return SVN_NO_ERROR; + return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool); } /* Find the youngest revision in a repository at path FS_PATH and @@ -1761,15 +1296,11 @@ read_content(svn_stringbuf_t **content, const char *fname, apr_pool_t *pool) POOL. */ static svn_error_t * get_youngest(svn_revnum_t *youngest_p, - const char *fs_path, + svn_fs_t *fs, 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); - + apr_uint64_t dummy; + SVN_ERR(svn_fs_fs__read_current(youngest_p, &dummy, &dummy, fs, pool)); return SVN_NO_ERROR; } @@ -1781,88 +1312,37 @@ svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p, { fs_fs_data_t *ffd = fs->fsap_data; - SVN_ERR(get_youngest(youngest_p, fs->path, pool)); + SVN_ERR(get_youngest(youngest_p, fs, 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) +int +svn_fs_fs__shard_size(svn_fs_t *fs) { - *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; + fs_fs_data_t *ffd = fs->fsap_data; - 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); - } + return ffd->max_files_per_dir; +} - value = header_str->data + i; +svn_error_t * +svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, + svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; - /* header_str is safely in our pool, so we can use bits of it as - key and value. */ - svn_hash_sets(*headers, name, value); - } + SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); + *min_unpacked = ffd->min_unpacked_rev; 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) +svn_error_t * +svn_fs_fs__ensure_revision_exists(svn_revnum_t rev, + svn_fs_t *fs, + apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; @@ -1876,7 +1356,7 @@ ensure_revision_exists(svn_fs_t *fs, if (rev <= ffd->youngest_rev_cache) return SVN_NO_ERROR; - SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs->path, pool)); + SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs, pool)); /* Check again. */ if (rev <= ffd->youngest_rev_cache) @@ -1887,9778 +1367,838 @@ ensure_revision_exists(svn_fs_t *fs, } 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) +svn_fs_fs__file_length(svn_filesize_t *length, + node_revision_t *noderev, + 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 + representation_t *data_rep = noderev->data_rep; + if (!data_rep) { - 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); + /* Treat "no representation" as "empty file". */ + *length = 0; + } + else if (data_rep->expanded_size) + { + /* Standard case: a non-empty file. */ + *length = data_rep->expanded_size; + } + else + { + /* Work around a FSFS format quirk (see issue #4554). - /* 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); + A plain representation may specify its EXPANDED LENGTH as "0" + in which case, the SIZE value is what we want. - /* We failed for the first time. Refresh cache & retry. */ - SVN_ERR(update_min_unpacked_rev(fs, pool)); + Because EXPANDED_LENGTH will also be 0 for empty files, while + SIZE is non-null, we need to check wether the content is + actually empty. We simply compare with the MD5 checksum of + empty content (sha-1 is not always available). + */ + svn_checksum_t *empty_md5 + = svn_checksum_empty_checksum(svn_checksum_md5, pool); - retry = TRUE; - } - else - { - svn_error_clear(err); - return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, - _("No such revision %ld"), rev); - } + if (memcmp(empty_md5->digest, data_rep->md5_digest, + sizeof(data_rep->md5_digest))) + { + /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the + actual file length. */ + *length = data_rep->size; } else { - retry = FALSE; + /* Contents is empty. */ + *length = 0; } } - while (retry); - return svn_error_trace(err); + return SVN_NO_ERROR; } -/* 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_boolean_t +svn_fs_fs__noderev_same_rep_key(representation_t *a, + representation_t *b) { - svn_stringbuf_t *sb; - svn_boolean_t eof; - svn_error_t *err; + if (a == b) + return TRUE; - 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 (a == NULL || b == NULL) + return FALSE; - 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); - } + if (a->item_index != b->item_index) + return FALSE; - return SVN_NO_ERROR; -} + if (a->revision != b->revision) + return FALSE; -/* 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)); + return memcmp(&a->uniquifier, &b->uniquifier, sizeof(a->uniquifier)) == 0; +} - if (is_cached) +svn_error_t * +svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + node_revision_t *a, + node_revision_t *b, + apr_pool_t *scratch_pool) +{ + svn_stream_t *contents_a, *contents_b; + representation_t *rep_a = a->data_rep; + representation_t *rep_b = b->data_rep; + svn_boolean_t a_empty = !rep_a; + svn_boolean_t b_empty = !rep_b; + + /* This makes sure that neither rep will be NULL later on */ + if (a_empty && b_empty) + { + *equal = TRUE; 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) + /* Same path in same rev or txn? */ + if (svn_fs_fs__id_eq(a->id, b->id)) { - svn_boolean_t eof; - apr_int64_t val; + *equal = TRUE; + return SVN_NO_ERROR; + } - svn_pool_clear(iterpool); - SVN_ERR(read_number_from_stream(&val, &eof, manifest_stream, iterpool)); - if (eof) - break; + /* Beware of the combination NULL rep and possibly empty rep. + * Due to EXPANDED_SIZE not being reliable, we can't easily detect empty + * reps. So, we can only take further shortcuts if both reps are given. */ + if (!a_empty && !b_empty) + { + /* File text representations always know their checksums - + * even in a txn. */ + if (memcmp(rep_a->md5_digest, rep_b->md5_digest, + sizeof(rep_a->md5_digest))) + { + *equal = FALSE; + return SVN_NO_ERROR; + } - APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val; + /* Paranoia. Compare SHA1 checksums because that's the level of + confidence we require for e.g. the working copy. */ + if (rep_a->has_sha1 && rep_b->has_sha1) + { + *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest, + sizeof(rep_a->sha1_digest)) == 0; + return SVN_NO_ERROR; + } } - svn_pool_destroy(iterpool); - *rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir, - apr_off_t); + SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE, + scratch_pool)); + SVN_ERR(svn_fs_fs__get_contents(&contents_b, fs, rep_b, TRUE, + scratch_pool)); + SVN_ERR(svn_stream_contents_same2(equal, contents_a, contents_b, + scratch_pool)); - /* 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); + return SVN_NO_ERROR; } -/* 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)); +svn_error_t * +svn_fs_fs__prop_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + node_revision_t *a, + node_revision_t *b, + apr_pool_t *scratch_pool) +{ + representation_t *rep_a = a->prop_rep; + representation_t *rep_b = b->prop_rep; + apr_hash_t *proplist_a; + apr_hash_t *proplist_b; - if (is_packed_rev(fs, rev)) + /* Mainly for a==b==NULL */ + if (rep_a == rep_b) { - apr_off_t rev_offset; - - SVN_ERR(get_packed_offset(&rev_offset, fs, rev, pool)); - offset += rev_offset; + *equal = TRUE; + return SVN_NO_ERROR; } - 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)); + /* Committed property lists can be compared quickly */ + if ( rep_a && rep_b + && !svn_fs_fs__id_txn_used(&rep_a->txn_id) + && !svn_fs_fs__id_txn_used(&rep_b->txn_id)) + { + /* MD5 must be given. Having the same checksum is good enough for + accepting the prop lists as equal. */ + *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest, + sizeof(rep_a->md5_digest)) == 0; + return SVN_NO_ERROR; + } - offset = rep->offset; - SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool)); + /* Same path in same txn? */ + if (svn_fs_fs__id_eq(a->id, b->id)) + { + *equal = TRUE; + return SVN_NO_ERROR; + } - *file = rev_file; + /* At least one of the reps has been modified in a txn. + Fetch and compare them. */ + SVN_ERR(svn_fs_fs__get_proplist(&proplist_a, fs, a, scratch_pool)); + SVN_ERR(svn_fs_fs__get_proplist(&proplist_b, fs, b, scratch_pool)); + *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool); 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) +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) { - representation_t *rep; - char *str; - apr_int64_t val; + *checksum = NULL; - 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) + if (noderev->data_rep) { - 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_checksum_t temp; + temp.kind = kind; - SVN_ERR(svn_cstring_atoi64(&val, str)); - rep->offset = (apr_off_t)val; + switch(kind) + { + case svn_checksum_md5: + temp.digest = noderev->data_rep->md5_digest; + break; - 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")); + case svn_checksum_sha1: + if (! noderev->data_rep->has_sha1) + return SVN_NO_ERROR; - SVN_ERR(svn_cstring_atoi64(&val, str)); - rep->size = (svn_filesize_t)val; + temp.digest = noderev->data_rep->sha1_digest; + break; - 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")); + default: + return SVN_NO_ERROR; + } - SVN_ERR(svn_cstring_atoi64(&val, str)); - rep->expanded_size = (svn_filesize_t)val; + *checksum = svn_checksum_dup(&temp, pool); + } - /* 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")); + return SVN_NO_ERROR; +} - SVN_ERR(svn_checksum_parse_hex(&rep->md5_checksum, svn_checksum_md5, str, - pool)); +representation_t * +svn_fs_fs__rep_copy(representation_t *rep, + apr_pool_t *pool) +{ + if (rep == NULL) + return NULL; - /* The remaining fields are only used for formats >= 4, so check that. */ - str = svn_cstring_tokenize(" ", &string); - if (str == NULL) - return SVN_NO_ERROR; + return apr_pmemdup(pool, rep, sizeof(*rep)); +} - /* 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)); +/* Write out the zeroth revision for filesystem FS. + Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +write_revision_zero(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + /* Use an explicit sub-pool to have full control over temp file lifetimes. + * Since we have it, use it for everything else as well. */ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, subpool); + apr_hash_t *proplist; + svn_string_t date; - /* 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")); + /* Write out a rev file for revision 0. */ + if (svn_fs_fs__use_log_addressing(fs)) + { + apr_array_header_t *index_entries; + svn_fs_fs__p2l_entry_t *entry; + svn_fs_fs__revision_file_t *rev_file; + const char *l2p_proto_index, *p2l_proto_index; + + /* Write a skeleton r0 with no indexes. */ + SVN_ERR(svn_io_file_create(path_revision_zero, + "PLAIN\nEND\nENDREP\n" + "id: 0.0.r0/2\n" + "type: dir\n" + "count: 0\n" + "text: 0 3 4 4 " + "2d2977d1c96f487abe4a1e202dd03b4e\n" + "cpath: /\n" + "\n\n", subpool)); + + /* Construct the index P2L contents: describe the 3 items we have. + Be sure to create them in on-disk order. */ + index_entries = apr_array_make(subpool, 3, sizeof(entry)); + + entry = apr_pcalloc(subpool, sizeof(*entry)); + entry->offset = 0; + entry->size = 17; + entry->type = SVN_FS_FS__ITEM_TYPE_DIR_REP; + entry->item.revision = 0; + entry->item.number = SVN_FS_FS__ITEM_INDEX_FIRST_USER; + APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; + + entry = apr_pcalloc(subpool, sizeof(*entry)); + entry->offset = 17; + entry->size = 89; + entry->type = SVN_FS_FS__ITEM_TYPE_NODEREV; + entry->item.revision = 0; + entry->item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE; + APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; + + entry = apr_pcalloc(subpool, sizeof(*entry)); + entry->offset = 106; + entry->size = 1; + entry->type = SVN_FS_FS__ITEM_TYPE_CHANGES; + entry->item.revision = 0; + entry->item.number = SVN_FS_FS__ITEM_INDEX_CHANGES; + APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; + + /* Now re-open r0, create proto-index files from our entries and + rewrite the index section of r0. */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0, + subpool, subpool)); + SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs, + rev_file, index_entries, + subpool, subpool)); + SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs, + index_entries, + subpool, subpool)); + SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index, + p2l_proto_index, 0, subpool)); + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + } + else + 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", subpool)); + + SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, subpool)); - rep->uniquifier = apr_pstrdup(pool, str); + /* Set a date on revision 0. */ + date.data = svn_time_to_cstring(apr_time_now(), subpool); + date.len = strlen(date.data); + proplist = apr_hash_make(subpool); + svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date); + SVN_ERR(svn_fs_fs__set_revision_proplist(fs, 0, proplist, subpool)); + svn_pool_destroy(subpool); 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 * +svn_fs_fs__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + svn_boolean_t use_log_addressing, + apr_pool_t *pool) { - svn_error_t *err; - const char *txn_id; + fs_fs_data_t *ffd = fs->fsap_data; - if (noderev_id) - txn_id = svn_fs_fs__id_txn_id(noderev_id); + fs->path = apr_pstrdup(fs->pool, path); + ffd->format = format; + + /* Use an appropriate sharding mode if supported by the format. */ + if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) + ffd->max_files_per_dir = shard_size; else - txn_id = NULL; + ffd->max_files_per_dir = 0; - 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); - } + /* Select the addressing mode depending on the format. */ + if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) + ffd->use_log_addressing = use_log_addressing; else - return SVN_NO_ERROR; -} + ffd->use_log_addressing = FALSE; -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); -} + /* Create the revision data directories. */ + if (ffd->max_files_per_dir) + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_rev_shard(fs, 0, + pool), + pool)); + else + SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR, + pool), + pool)); -/* 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; - } + /* Create the revprops directory. */ + if (ffd->max_files_per_dir) + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_revprops_shard(fs, 0, + pool), + pool)); 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)); - } + SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, + PATH_REVPROPS_DIR, + pool), + pool)); - return SVN_NO_ERROR; -} + /* Create the transaction directory. */ + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txns_dir(fs, pool), + pool)); -/* 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; + /* Create the protorevs directory. */ + if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) + SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txn_proto_revs(fs, + pool), + pool)); - 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); - } + /* Create the 'current' file. */ + SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_current(fs, pool), pool)); + SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, pool)); - return SVN_NO_ERROR; -} + /* Create the 'uuid' file. */ + SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(fs, pool), pool)); + SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, NULL, pool)); -/* 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; + /* 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)); - /* 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; + SVN_ERR(read_config(ffd, fs->path, fs->pool, pool)); - 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); - } + /* Global configuration options. */ + SVN_ERR(read_global_config(fs)); - if (err) - { - if (APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_clear(err); - return svn_error_trace(err_dangling_id(fs, id)); - } + /* Add revision 0. */ + SVN_ERR(write_revision_zero(fs, pool)); - return svn_error_trace(err); - } + /* Create the min unpacked rev file. */ + if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) + SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool), + "0\n", pool)); - SVN_ERR(svn_fs_fs__read_noderev(noderev_p, - svn_stream_from_aprfile2(revision_file, FALSE, - pool), - 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(svn_fs_fs__path_txn_current(fs, pool), + "0\n", pool)); + SVN_ERR(svn_io_file_create_empty( + svn_fs_fs__path_txn_current_lock(fs, 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); + ffd->youngest_rev_cache = 0; + return SVN_NO_ERROR; } svn_error_t * -svn_fs_fs__read_noderev(node_revision_t **noderev_p, - svn_stream_t *stream, - apr_pool_t *pool) +svn_fs_fs__create(svn_fs_t *fs, + const char *path, + apr_pool_t *pool) { - apr_hash_t *headers; - node_revision_t *noderev; - char *value; - const char *noderev_id; + int format = SVN_FS_FS__FORMAT_NUMBER; + int shard_size = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR; + svn_boolean_t log_addressing; - SVN_ERR(read_header_block(&headers, stream, pool)); + /* Process the given filesystem config. */ + if (fs->config) + { + svn_version_t *compatible_version; + const char *shard_size_str; + SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, + pool)); - noderev = apr_pcalloc(pool, sizeof(*noderev)); + /* select format number */ + switch(compatible_version->minor) + { + case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, + _("FSFS is not compatible with Subversion prior to 1.1")); - /* 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")); + case 1: + case 2: + case 3: format = 1; + break; - SVN_ERR(svn_stream_close(stream)); + case 4: format = 2; + break; - noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool); - noderev_id = value; /* for error messages later */ + case 5: format = 3; + break; - /* Read the type. */ - value = svn_hash_gets(headers, HEADER_TYPE); + case 6: + case 7: format = 4; + break; - 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); + case 8: format = 6; + break; - noderev->kind = (strcmp(value, KIND_FILE) == 0) ? svn_node_file - : svn_node_dir; + default:format = SVN_FS_FS__FORMAT_NUMBER; + } - /* 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; + shard_size_str = svn_hash_gets(fs->config, SVN_FS_CONFIG_FSFS_SHARD_SIZE); + if (shard_size_str) + { + apr_int64_t val; + SVN_ERR(svn_cstring_strtoi64(&val, shard_size_str, 0, + APR_INT32_MAX, 10)); - /* 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)); + shard_size = (int) val; + } } - /* 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)); - } + log_addressing = svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_FSFS_LOG_ADDRESSING, + TRUE); - /* 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); - } + /* Actual FS creation. */ + SVN_ERR(svn_fs_fs__create_file_tree(fs, path, format, shard_size, + log_addressing, pool)); - /* 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); + /* This filesystem is ready. Stamp it with a format number. */ + SVN_ERR(svn_fs_fs__write_format(fs, FALSE, 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; + return SVN_NO_ERROR; +} - 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); +svn_error_t * +svn_fs_fs__set_uuid(svn_fs_t *fs, + const char *uuid, + const char *instance_id, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + const char *uuid_path = path_uuid(fs, pool); + svn_stringbuf_t *contents = svn_stringbuf_create_empty(pool); - noderev->copyroot_rev = SVN_STR_TO_REV(str); + if (! uuid) + uuid = svn_uuid_generate(pool); - 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); - } + if (! instance_id) + instance_id = svn_uuid_generate(pool); - /* Get the copyfrom. */ - value = svn_hash_gets(headers, HEADER_COPYFROM); - if (value == NULL) - { - noderev->copyfrom_path = NULL; - noderev->copyfrom_rev = SVN_INVALID_REVNUM; - } - else + svn_stringbuf_appendcstr(contents, uuid); + svn_stringbuf_appendcstr(contents, "\n"); + + if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) { - 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); + svn_stringbuf_appendcstr(contents, instance_id); + svn_stringbuf_appendcstr(contents, "\n"); } - /* 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; + /* We use the permissions of the 'current' file, because the 'uuid' + file does not exist during repository creation. */ + SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len, + svn_fs_fs__path_current(fs, pool) /* perms */, + pool)); - /* Get whether *this* node has mergeinfo. */ - value = svn_hash_gets(headers, HEADER_MINFO_HERE); - noderev->has_mergeinfo = (value != NULL); + fs->uuid = apr_pstrdup(fs->pool, uuid); - *noderev_p = noderev; + if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) + ffd->instance_id = apr_pstrdup(fs->pool, instance_id); + else + ffd->instance_id = fs->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__get_node_revision(node_revision_t **noderev_p, - svn_fs_t *fs, - const svn_fs_id_t *id, +svn_fs_fs__ensure_dir_exists(const char *path, + const char *fs_path, 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_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); + if (err && APR_STATUS_IS_EEXIST(err->apr_err)) { - 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); + svn_error_clear(err); + return SVN_NO_ERROR; } - 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_ERR(err); + /* We successfully created a new directory. Dup the permissions + from FS->path. */ + return svn_io_copy_perms(fs_path, path, pool); } - -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) +/* 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) { - 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)); + apr_file_t *fd; + svn_error_t *err; + svn_stream_t *stream; - 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) + *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)) { - 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)); + svn_error_clear(err); + return SVN_NO_ERROR; } + SVN_ERR(err); - return SVN_NO_ERROR; + stream = svn_stream_from_aprfile2(fd, FALSE, pool); + *node_origins = apr_hash_make(pool); + err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool); + if (err) + return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"), + node_origins_file); + return svn_stream_close(stream); } - -/* 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) +svn_error_t * +svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *node_id, + 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; + apr_hash_t *node_origins; - if (strcmp(buffer, REP_PLAIN) == 0) + *origin_id = NULL; + SVN_ERR(get_node_origins_from_file(fs, &node_origins, + svn_fs_fs__path_node_origin(fs, node_id, + pool), + pool)); + if (node_origins) { - *rep_args_p = rep_args; - return SVN_NO_ERROR; - } + char node_id_ptr[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number); + svn_string_t *origin_id_str + = apr_hash_get(node_origins, node_id_ptr, len); - 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; + if (origin_id_str) + SVN_ERR(svn_fs_fs__id_parse(origin_id, + apr_pstrdup(pool, origin_id_str->data), + pool)); } - - 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. */ + +/* 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 * -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) +set_node_origins_for_file(svn_fs_t *fs, + const char *node_origins_path, + const svn_fs_fs__id_part_t *node_id, + svn_string_t *node_rev_id, + apr_pool_t *pool) { - svn_fs_id_t *id; - apr_hash_t *headers; - const char *node_id_str; + const char *path_tmp; + svn_stream_t *stream; + apr_hash_t *origins_hash; + svn_string_t *old_node_rev_id; - SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool)); + /* the hash serialization functions require strings as keys */ + char node_id_ptr[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number); - SVN_ERR(read_header_block(&headers, - svn_stream_from_aprfile2(rev_file, TRUE, pool), - pool)); + SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path, + PATH_NODE_ORIGINS_DIR, + pool), + fs->path, pool)); - /* In error messages, the offset is relative to the pack file, - not to the rev file. */ + /* 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); - node_id_str = svn_hash_gets(headers, HEADER_ID); + old_node_rev_id = apr_hash_get(origins_hash, node_id_ptr, len); - if (node_id_str == NULL) + if (old_node_rev_id && !svn_string_compare(node_rev_id, old_node_rev_id)) 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); + _("Node origin for '%s' exists with a different " + "value (%s) than what we were about to store " + "(%s)"), + node_id_ptr, old_node_rev_id->data, + node_rev_id->data); - 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)); + apr_hash_set(origins_hash, node_id_ptr, len, node_rev_id); - *id_p = 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. */ - /* ### assert that the txn_id is REV/OFFSET ? */ + /* 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)); - return SVN_NO_ERROR; + /* Rename the temp file as the real destination */ + return svn_io_file_rename(path_tmp, node_origins_path, pool); } -/* 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) +svn_error_t * +svn_fs_fs__set_node_origin(svn_fs_t *fs, + const svn_fs_fs__id_part_t *node_id, + const svn_fs_id_t *node_rev_id, + 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; - } + svn_error_t *err; + const char *filename = svn_fs_fs__path_node_origin(fs, node_id, pool); - if (i < 0) + 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)) { - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file (r%ld) longer " - "than 64 characters"), - rev); + /* It's just a cache; stop trying if I can't write. */ + svn_error_clear(err); + err = NULL; } + return svn_error_trace(err); +} - 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]; + +/*** Revisions ***/ - /* find the next newline */ - for ( ; i < num_bytes; i++) - if (buf[i] == '\n') - break; +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; - if (changes_offset) - { - apr_int64_t val; + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, pool)); - buf[i] = '\0'; - SVN_ERR(svn_cstring_atoi64(&val, str)); - *changes_offset = rev_offset + (apr_off_t)val; - } + *value_p = svn_hash_gets(table, propname); 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. */ +/* 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 * -move_into_place(const char *old_filename, - const char *new_filename, - const char *perms_reference, - apr_pool_t *pool) +change_rev_prop_body(void *baton, apr_pool_t *pool) { - svn_error_t *err; + struct change_rev_prop_baton *cb = baton; + apr_hash_t *table; - SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool)); + SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, 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)) + if (cb->old_value_p) { - 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)); + 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. */ } - 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 + svn_hash_sets(table, cb->name, cb->value); - return SVN_NO_ERROR; + return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool); } 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) +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) { - 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)); + struct change_rev_prop_baton cb; - 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(svn_fs__check_fs(fs, TRUE)); - 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)); + cb.fs = fs; + cb.rev = rev; + cb.name = name; + cb.old_value_p = old_value_p; + cb.value = value; - SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev, - root_offset, pool)); + return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool); +} - SVN_ERR(svn_io_file_close(revision_file, pool)); + +svn_error_t * +svn_fs_fs__info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + *fs_format = ffd->format; + *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); - SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, pool)); + (*supports_version)->major = SVN_VER_MAJOR; + (*supports_version)->minor = 1; + (*supports_version)->patch = 0; + (*supports_version)->tag = ""; - *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(¤t, ffd->revprop_generation)); - if (current == 0) - { - SVN_ERR(read_revprop_generation_file(¤t, 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(¤t, 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 = ¤t; - 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(¤t, - 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(¤t, - 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_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. 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, - 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 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 *FILE that is - * allocated in POOL. - */ -static svn_error_t * -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; - 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; - - /* 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)); - - 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_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, - 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_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, - file, pool)); - } - - if (left_count + right_count < revprops->sizes->nelts) - { - 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, - file, pool)); - } - - if (right_count) - { - 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, file, - pool)); - } - - /* write the new manifest */ - *final_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, 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_io_file_write_full(file, filename, strlen(filename), - NULL, pool)); - SVN_ERR(svn_io_file_putc('\n', file, pool)); - } - - 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. - 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 && 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 "/-.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; - - 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); - - 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 = 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 . - */ - 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 - ? 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, 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)); + switch (ffd->format) + { + case 1: + break; + case 2: + (*supports_version)->minor = 4; + break; + case 3: + (*supports_version)->minor = 5; + break; + case 4: + (*supports_version)->minor = 6; + break; + case 6: + (*supports_version)->minor = 8; + break; + case 7: + (*supports_version)->minor = 9; + break; +#ifdef SVN_DEBUG +# if SVN_FS_FS__FORMAT_NUMBER != 7 +# error "Need to add a 'case' statement here" +# endif +#endif + } 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) +svn_fs_fs__info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_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)); - + *files = apr_array_make(result_pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG, + result_pool); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.h b/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.h index c09f861f4..b6c94c7e7 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/fs_fs.h @@ -25,6 +25,11 @@ #include "fs.h" +/* Read the 'format' file of fsfs filesystem FS and store its info in FS. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool); + /* Open the fsfs filesystem pointed to by PATH and associate it with filesystem object FS. Use POOL for temporary allocations. @@ -34,166 +39,41 @@ svn_error_t *svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool); -/* Upgrade the fsfs filesystem FS. Use POOL for temporary allocations. */ -svn_error_t *svn_fs_fs__upgrade(svn_fs_t *fs, - apr_pool_t *pool); - -/* Verify metadata in fsfs filesystem FS. Limit the checks to revisions - * START to END where possible. Indicate progress via the optional +/* Upgrade the fsfs filesystem FS. Indicate progress via the optional * NOTIFY_FUNC callback using NOTIFY_BATON. The optional CANCEL_FUNC * will periodically be called with CANCEL_BATON to allow for preemption. * Use POOL for temporary allocations. */ -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); - -/* Copy the fsfs filesystem SRC_FS at SRC_PATH into a new copy DST_FS at - * DST_PATH. If INCREMENTAL is TRUE, do not re-copy data which already - * exists in DST_FS. Use POOL for temporary allocations. */ -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); - -/* Recover the fsfs associated with filesystem FS. - Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. - Use POOL for temporary allocations. */ -svn_error_t *svn_fs_fs__recover(svn_fs_t *fs, +svn_error_t *svn_fs_fs__upgrade(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool); -/* Set *NODEREV_P to the node-revision for the node ID in FS. Do any - allocations in POOL. */ -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); - -/* Store NODEREV as the node-revision for the node whose id is ID in - FS, after setting its is_fresh_txn_root to FRESH_TXN_ROOT. Do any - necessary temporary allocation in POOL. */ -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); - -/* Write the node-revision NODEREV into the stream OUTFILE, compatible with - filesystem format FORMAT. Only write mergeinfo-related metadata if - INCLUDE_MERGEINFO is true. Temporary allocations are from POOL. */ -/* ### Currently used only by fs_fs.c */ -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); - -/* Read a node-revision from STREAM. Set *NODEREV to the new structure, - allocated in POOL. */ -/* ### Currently used only by fs_fs.c */ -svn_error_t * -svn_fs_fs__read_noderev(node_revision_t **noderev, - svn_stream_t *stream, - apr_pool_t *pool); - - /* Set *YOUNGEST to the youngest revision in filesystem FS. Do any temporary allocation in POOL. */ svn_error_t *svn_fs_fs__youngest_rev(svn_revnum_t *youngest, svn_fs_t *fs, apr_pool_t *pool); -/* Return an error iff REV does not exist in FS. */ -svn_error_t * -svn_fs_fs__revision_exists(svn_revnum_t rev, - svn_fs_t *fs, - apr_pool_t *pool); - -/* Set *ROOT_ID to the node-id for the root of revision REV in - filesystem FS. Do any allocations in POOL. */ -svn_error_t *svn_fs_fs__rev_get_root(svn_fs_id_t **root_id, - svn_fs_t *fs, - svn_revnum_t rev, - apr_pool_t *pool); +/* Return the shard size of filesystem FS. Return 0 for non-shared ones. */ +int +svn_fs_fs__shard_size(svn_fs_t *fs); -/* Set *ENTRIES to an apr_hash_t of dirent structs that contain the - directory entries of node-revision NODEREV in filesystem FS. The - returned table (and its keys and values) is allocated in POOL, - which is also used for temporary allocations. */ -svn_error_t *svn_fs_fs__rep_contents_dir(apr_hash_t **entries, - svn_fs_t *fs, - node_revision_t *noderev, - apr_pool_t *pool); - -/* Set *DIRENT to the entry identified by NAME in the directory given - by NODEREV in filesystem FS. If no such entry exits, *DIRENT will - be NULL. The returned object is allocated in RESULT_POOL; SCRATCH_POOL - used for temporary allocations. */ +/* Set *MIN_UNPACKED to the oldest non-packed revision in filesystem FS. + Do any temporary allocation in POOL. */ 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); - -/* Set *CONTENTS to be a readable svn_stream_t that receives the text - representation of node-revision NODEREV as seen in filesystem FS. - Use POOL for temporary allocations. */ -svn_error_t *svn_fs_fs__get_contents(svn_stream_t **contents, - svn_fs_t *fs, - node_revision_t *noderev, - apr_pool_t *pool); +svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, + svn_fs_t *fs, + apr_pool_t *pool); -/* Attempt to fetch the text representation of node-revision NODEREV as - seen in filesystem FS and pass it along with the BATON to the PROCESSOR. - Set *SUCCESS only of the data could be provided and the processing - had been called. - Use POOL for all allocations. - */ +/* Return SVN_ERR_FS_NO_SUCH_REVISION if the given revision REV is newer + than the current youngest revision in FS or is simply not a valid + revision number, else return success. */ 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); - -/* Set *STREAM_P to a delta stream turning the contents of the file SOURCE into - the contents of the file TARGET, allocated in POOL. - If SOURCE is null, the empty string will be used. */ -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); - -/* Set *PROPLIST to be an apr_hash_t containing the property list of - node-revision NODEREV as seen in filesystem FS. Use POOL for - temporary allocations. */ -svn_error_t *svn_fs_fs__get_proplist(apr_hash_t **proplist, - svn_fs_t *fs, - node_revision_t *noderev, - apr_pool_t *pool); - -/* Set *PROPLIST to be an apr_hash_t containing the property list of - revision REV as seen in filesystem FS. Use POOL for temporary - allocations. */ -svn_error_t *svn_fs_fs__revision_proplist(apr_hash_t **proplist, - svn_fs_t *fs, - svn_revnum_t rev, - apr_pool_t *pool); +svn_fs_fs__ensure_revision_exists(svn_revnum_t rev, + svn_fs_t *fs, + apr_pool_t *pool); /* Set *LENGTH to the be fulltext length of the node revision specified by NODEREV. Use POOL for temporary allocations. */ @@ -206,6 +86,26 @@ svn_error_t *svn_fs_fs__file_length(svn_filesize_t *length, svn_boolean_t svn_fs_fs__noderev_same_rep_key(representation_t *a, representation_t *b); +/* Set *EQUAL to TRUE if the text representations in A and B within FS + have equal contents, else set it to FALSE. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + node_revision_t *a, + node_revision_t *b, + apr_pool_t *scratch_pool); + +/* Set *EQUAL to TRUE if the property representations in A and B within FS + have equal contents, else set it to FALSE. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__prop_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + node_revision_t *a, + node_revision_t *b, + apr_pool_t *scratch_pool); + /* Return a copy of the representation REP allocated from POOL. */ representation_t *svn_fs_fs__rep_copy(representation_t *rep, @@ -220,152 +120,27 @@ svn_error_t *svn_fs_fs__file_checksum(svn_checksum_t **checksum, svn_checksum_kind_t kind, apr_pool_t *pool); -/* Find the paths which were changed in revision REV of filesystem FS - and store them in *CHANGED_PATHS_P. Cached copyfrom information - will be stored in *COPYFROM_CACHE. Get any temporary allocations - from POOL. */ -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); - -/* Create a new transaction in filesystem FS, based on revision REV, - and store it in *TXN_P. Allocate all necessary variables from - POOL. */ -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); - -/* Set the transaction property NAME to the value VALUE in transaction - TXN. Perform temporary allocations from POOL. */ -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); - -/* Change transaction properties in transaction TXN based on PROPS. - Perform temporary allocations from 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); - /* Return whether or not the given FS supports mergeinfo metadata. */ svn_boolean_t svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs); -/* Store a transaction record in *TXN_P for the transaction identified - by TXN_ID in filesystem FS. Allocate everything from 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); - -/* Abort the existing transaction TXN, performing any temporary - allocations in POOL. */ -svn_error_t *svn_fs_fs__abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool); - -/* Create an entirely new mutable node in the filesystem FS, whose - node-revision is NODEREV. Set *ID_P to the new node revision's ID. - Use POOL for any temporary allocation. COPY_ID is the copy_id to - use in the node revision ID. TXN_ID is the Subversion transaction - under which this occurs. */ -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); - -/* Remove all references to the transaction TXN_ID from filesystem FS. - Temporary allocations are from POOL. */ -svn_error_t *svn_fs_fs__purge_txn(svn_fs_t *fs, - const char *txn_id, - apr_pool_t *pool); - -/* Add or set in filesystem FS, transaction TXN_ID, in directory - PARENT_NODEREV a directory entry for NAME pointing to ID of type - KIND. Allocations are done in POOL. */ -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); - -/* Add a change to the changes record for filesystem FS in transaction - TXN_ID. Mark path PATH, having node-id ID, as changed according to - the type in CHANGE_KIND. If the text representation was changed - set TEXT_MOD to TRUE, and likewise for PROP_MOD. If this change - was the result of a copy, set COPYFROM_REV and COPYFROM_PATH to the - revision and path of the copy source, otherwise they should be set - to SVN_INVALID_REVNUM and NULL. Perform any temporary allocations - from 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); - -/* Return a writable stream in *STREAM that allows storing the text - representation of node-revision NODEREV in filesystem FS. - Allocations are from POOL. */ -svn_error_t *svn_fs_fs__set_contents(svn_stream_t **stream, - svn_fs_t *fs, - node_revision_t *noderev, - apr_pool_t *pool); - -/* Create a node revision in FS which is an immediate successor of - OLD_ID, whose contents are NEW_NR. Set *NEW_ID_P to the new node - revision's ID. Use POOL for any temporary allocation. - - COPY_ID, if non-NULL, is a key into the `copies' table, and - indicates that this new node is being created as the result of a - copy operation, and specifically which operation that was. If - COPY_ID is NULL, then re-use the copy ID from the predecessor node. - - TXN_ID is the Subversion transaction under which this occurs. - - After this call, the deltification code assumes that the new node's - contents will change frequently, and will avoid representing other - nodes as deltas against this node's contents. */ -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); - -/* Write a new property list PROPLIST for node-revision NODEREV in - filesystem FS. Perform any temporary allocations in POOL. */ -svn_error_t *svn_fs_fs__set_proplist(svn_fs_t *fs, - node_revision_t *noderev, - apr_hash_t *proplist, - apr_pool_t *pool); - -/* Commit the transaction TXN in filesystem FS and return its new - revision number in *REV. If the transaction is out of date, return - the error SVN_ERR_FS_TXN_OUT_OF_DATE. Use POOL for temporary - allocations. */ -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); - -/* Return the next available copy_id in *COPY_ID for the transaction - TXN_ID in filesystem FS. Allocate space in POOL. */ -svn_error_t *svn_fs_fs__reserve_copy_id(const char **copy_id, - svn_fs_t *fs, - const char *txn_id, - apr_pool_t *pool); +/* Under the repository db PATH, create a FSFS repository with FORMAT, + * the given SHARD_SIZE. If USE_LOG_ADDRESSING is non-zero, repository + * will use logical addressing. If not supported by the respective format, + * the latter two parameters will be ignored. FS will be updated. + * + * The only file not being written is the 'format' file. This allows + * callers such as hotcopy to modify the contents before turning the + * tree into an accessible repository. + * + * Use POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + svn_boolean_t use_log_addressing, + apr_pool_t *pool); /* Create a fs_fs fileysystem referenced by FS at path PATH. Get any temporary allocations from POOL. @@ -376,70 +151,29 @@ svn_error_t *svn_fs_fs__create(svn_fs_t *fs, const char *path, apr_pool_t *pool); -/* Set the uuid of repository FS to UUID, if UUID is not NULL; - otherwise, set the uuid of FS to a newly generated UUID. Perform - temporary allocations in POOL. */ +/* Set the uuid of repository FS to UUID and the instance ID to INSTANCE_ID. + If any of them is NULL, use a newly generated UUID / ID instead. Ignore + INSTANCE_ID whenever instance IDs are not supported by the FS format. + Perform temporary allocations in POOL. */ svn_error_t *svn_fs_fs__set_uuid(svn_fs_t *fs, const char *uuid, + const char *instance_id, apr_pool_t *pool); -/* Set *NAMES_P to an array of names which are all the active - transactions in filesystem FS. Allocate the array from POOL. */ -svn_error_t *svn_fs_fs__list_transactions(apr_array_header_t **names_p, - svn_fs_t *fs, - apr_pool_t *pool); - -/* Open the transaction named NAME in filesystem FS. Set *TXN_P to - * the transaction. If there is no such transaction, return -` * SVN_ERR_FS_NO_SUCH_TRANSACTION. Allocate the new transaction in - * POOL. */ -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); - -/* Return the property list from transaction TXN and store it in - *PROPLIST. Allocate the property list from POOL. */ -svn_error_t *svn_fs_fs__txn_proplist(apr_hash_t **proplist, - svn_fs_txn_t *txn, - apr_pool_t *pool); - -/* Delete the mutable node-revision referenced by ID, along with any - mutable props or directory contents associated with it. Perform - temporary allocations in POOL. */ -svn_error_t *svn_fs_fs__delete_node_revision(svn_fs_t *fs, - const svn_fs_id_t *id, - apr_pool_t *pool); - - -/* Find the paths which were changed in transaction TXN_ID of - filesystem FS and store them in *CHANGED_PATHS_P. - Get any temporary allocations from POOL. */ -svn_error_t *svn_fs_fs__txn_changes_fetch(apr_hash_t **changes, - svn_fs_t *fs, - const char *txn_id, - apr_pool_t *pool); - - -/* Set *PATH to the path of REV in FS, whether in a pack file or not. - Allocate *PATH in POOL. - - Note: If the caller does not have the write lock on FS, then the path is - not guaranteed to be correct or to remain correct after the function - returns, because the revision might become packed before or after this - call. If a file exists at that path, then it is correct; if not, then - the caller should call update_min_unpacked_rev() and re-try once. */ -svn_error_t * -svn_fs_fs__path_rev_absolute(const char **path, - svn_fs_t *fs, - svn_revnum_t rev, - apr_pool_t *pool); - /* Return the path to the 'current' file in FS. Perform allocation in POOL. */ const char * svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool); +/* Write the format number and maximum number of files per directory + for FS, possibly expecting to overwrite a previously existing file. + + Use POOL for temporary allocation. */ +svn_error_t * +svn_fs_fs__write_format(svn_fs_t *fs, + svn_boolean_t overwrite, + apr_pool_t *pool); + /* Obtain a write lock on the filesystem FS in a subpool of POOL, call BODY with BATON and that subpool, destroy the subpool (releasing the write lock) and return what BODY returned. */ @@ -450,6 +184,38 @@ svn_fs_fs__with_write_lock(svn_fs_t *fs, void *baton, apr_pool_t *pool); +/* Obtain a pack operation lock on the filesystem FS in a subpool of POOL, + call BODY with BATON and that subpool, destroy the subpool (releasing the + write lock) and return what BODY returned. */ +svn_error_t * +svn_fs_fs__with_pack_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); + +/* Run BODY (with BATON and POOL) while the txn-current file + of FS is locked. */ +svn_error_t * +svn_fs_fs__with_txn_current_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); + +/* Obtain all locks on the filesystem FS in a subpool of POOL, call BODY + with BATON and that subpool, destroy the subpool (releasing the locks) + and return what BODY returned. + + This combines svn_fs_fs__with_write_lock, svn_fs_fs__with_pack_lock, + and svn_fs_fs__with_txn_current_lock, ensuring correct lock ordering. */ +svn_error_t * +svn_fs_fs__with_all_locks(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); + /* Find the value of the property named PROPNAME in transaction TXN. Return the contents in *VALUE_P. The contents will be allocated from POOL. */ @@ -469,38 +235,6 @@ svn_error_t *svn_fs_fs__change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const svn_string_t *value, apr_pool_t *pool); -/* Retrieve information about the Subversion transaction SVN_TXN from - the `transactions' table of FS, allocating from POOL. Set - *ROOT_ID_P to the ID of the transaction's root directory. Set - *BASE_ROOT_ID_P to the ID of the root directory of the - transaction's base revision. - - If there is no such transaction, SVN_ERR_FS_NO_SUCH_TRANSACTION is - the error returned. - - Returns SVN_ERR_FS_TRANSACTION_NOT_MUTABLE if TXN_NAME refers to a - transaction that has already been committed. - - Allocate *ROOT_ID_P and *BASE_ROOT_ID_P in POOL. */ -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); - -/* Begin a new transaction in filesystem FS, based on existing - revision REV. The new transaction is returned in *TXN_P. Allocate - the new transaction structure from POOL. */ -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); - -/* Find the value of the property named PROPNAME in transaction TXN. - Return the contents in *VALUE_P. The contents will be allocated - from POOL. */ -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); - /* 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, @@ -516,7 +250,7 @@ svn_error_t *svn_fs_fs__ensure_dir_exists(const char *path, */ svn_error_t * svn_fs_fs__set_node_origin(svn_fs_t *fs, - const char *node_id, + const svn_fs_fs__id_part_t *node_id, const svn_fs_id_t *node_rev_id, apr_pool_t *pool); @@ -530,12 +264,12 @@ svn_fs_fs__set_node_origin(svn_fs_t *fs, svn_error_t * svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id, svn_fs_t *fs, - const char *node_id, + const svn_fs_fs__id_part_t *node_id, apr_pool_t *pool); /* Initialize all session-local caches in FS according to the global - cache settings. Use POOL for allocations. + cache settings. Use POOL for temporary allocations. Please note that it is permissible for this function to set some or all of these caches to NULL, regardless of any setting. */ @@ -558,18 +292,4 @@ svn_fs_fs__initialize_txn_caches(svn_fs_t *fs, void svn_fs_fs__reset_txn_caches(svn_fs_t *fs); -/* Possibly pack the repository at PATH. This just take full shards, and - combines all the revision files into a single one, with a manifest header. - Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. - - Existing filesystem references need not change. */ -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); - - #endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.c b/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.c new file mode 100644 index 000000000..43f513e35 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.c @@ -0,0 +1,1097 @@ +/* hotcopy.c --- FS hotcopy functionality for FSFS + * + * ==================================================================== + * 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_pools.h" +#include "svn_path.h" +#include "svn_dirent_uri.h" + +#include "fs_fs.h" +#include "hotcopy.h" +#include "util.h" +#include "recovery.h" +#include "revprops.h" +#include "rep-cache.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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. + * Set *SKIPPED_P to FALSE only if the file was copied, do not change + * the value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not + * required. */ +static svn_error_t * +hotcopy_io_dir_file_copy(svn_boolean_t *skipped_p, + 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; + } + + if (skipped_p) + *skipped_p = FALSE; + + 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. Set *SKIPPED_P to FALSE only if at least one + * file was copied, do not change the value in *SKIPPED_P otherwise. + * SKIPPED_P may be NULL if not required. */ +static svn_error_t * +hotcopy_io_copy_dir_recursively(svn_boolean_t *skipped_p, + 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(skipped_p, 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(skipped_p, + 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. + * Set *SKIPPED_P to FALSE only if the file was copied, do not change the + * value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not required. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +hotcopy_copy_shard_file(svn_boolean_t *skipped_p, + 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(skipped_p, + 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. + * Set *SKIPPED_P to FALSE only if at least one part of the shard + * was copied, do not change the value in *SKIPPED_P otherwise. + * SKIPPED_P may be NULL if not required. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +hotcopy_copy_packed_shard(svn_boolean_t *skipped_p, + 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(skipped_p, 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(skipped_p, 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(skipped_p, 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(skipped_p, + 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(svn_fs_fs__write_min_unpacked_rev(dst_fs, + *dst_min_unpacked_rev, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Remove file PATH, if it exists - even if it is read-only. + * Use POOL for temporary allocations. */ +static svn_error_t * +hotcopy_remove_file(const char *path, + apr_pool_t *pool) +{ + /* Make the rev file writable and remove it. */ + SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool)); + SVN_ERR(svn_io_remove_file2(path, TRUE, pool)); + + 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++) + { + 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 */ + SVN_ERR(hotcopy_remove_file(svn_dirent_join(dst_subdir_shard, + apr_psprintf(iterpool, + "%ld", rev), + iterpool), + 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); +} + +/* Copy the revision and revprop files (possibly sharded / packed) from + * SRC_FS to DST_FS. Do not re-copy data which already exists in DST_FS. + * When copying packed or unpacked shards, checkpoint the result in DST_FS + * for every shard by updating the 'current' file if necessary. Assume + * the >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT filesystem format without + * global next-ID counters. Indicate progress via the optional NOTIFY_FUNC + * callback using NOTIFY_BATON. Use POOL for temporary allocations. + */ +static svn_error_t * +hotcopy_revisions(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_revnum_t src_youngest, + svn_revnum_t dst_youngest, + svn_boolean_t incremental, + const char *src_revs_dir, + const char *dst_revs_dir, + const char *src_revprops_dir, + const char *dst_revprops_dir, + svn_fs_hotcopy_notify_t notify_func, + void* notify_baton, + svn_cancel_func_t cancel_func, + void* cancel_baton, + apr_pool_t *pool) +{ + fs_fs_data_t *src_ffd = src_fs->fsap_data; + fs_fs_data_t *dst_ffd = dst_fs->fsap_data; + int max_files_per_dir = src_ffd->max_files_per_dir; + svn_revnum_t src_min_unpacked_rev; + svn_revnum_t dst_min_unpacked_rev; + svn_revnum_t rev; + apr_pool_t *iterpool; + + /* Copy the min unpacked rev, and read its value. */ + if (src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) + { + SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&src_min_unpacked_rev, + src_fs, pool)); + SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&dst_min_unpacked_rev, + dst_fs, 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. + */ + + iterpool = svn_pool_create(pool); + /* First, copy packed shards. */ + for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir) + { + svn_boolean_t skipped = TRUE; + svn_revnum_t pack_end_rev; + + svn_pool_clear(iterpool); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Copy the packed shard. */ + SVN_ERR(hotcopy_copy_packed_shard(&skipped, &dst_min_unpacked_rev, + src_fs, dst_fs, + rev, max_files_per_dir, + iterpool)); + + pack_end_rev = rev + max_files_per_dir - 1; + + /* Whenever this pack did not previously exist in the destination, + * update 'current' to the most recent packed rev (so readers can see + * new revisions which arrived in this pack). */ + if (pack_end_rev > dst_youngest) + { + SVN_ERR(svn_fs_fs__write_current(dst_fs, pack_end_rev, 0, 0, + iterpool)); + } + + /* When notifying about packed shards, make things simpler by either + * reporting a full revision range, i.e [pack start, pack end] or + * reporting nothing. There is one case when this approach might not + * be exact (incremental hotcopy with a pack replacing last unpacked + * revisions), but generally this is good enough. */ + if (notify_func && !skipped) + notify_func(notify_baton, rev, pack_end_rev, 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(svn_fs_fs__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(svn_fs_fs__path_revprops_shard(dst_fs, rev, + iterpool), + cancel_func, cancel_baton, iterpool)); + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + SVN_ERR_ASSERT(rev == src_min_unpacked_rev); + SVN_ERR_ASSERT(src_min_unpacked_rev == dst_min_unpacked_rev); + + /* Now, copy pairs of non-packed revisions and revprop files. + * If necessary, update 'current' after copying all files from a shard. */ + for (; rev <= src_youngest; rev++) + { + svn_boolean_t skipped = TRUE; + + svn_pool_clear(iterpool); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Copying non-packed revisions is racy in case the source repository is + * being packed concurrently with this hotcopy operation. The race can + * happen with FS formats prior to SVN_FS_FS__MIN_PACK_LOCK_FORMAT that + * support packed revisions. With the pack lock, however, the race is + * impossible, because hotcopy and pack operations block each other. + * + * We assume that all revisions coming after 'min-unpacked-rev' really + * are unpacked and that's not necessarily true with concurrent packing. + * Don't try to be smart in this edge case, because handling it properly + * might require copying *everything* from the start. Just abort the + * hotcopy with an ENOENT (revision file moved to a pack, so it is no + * longer where we expect it to be). */ + + /* Copy the rev file. */ + SVN_ERR(hotcopy_copy_shard_file(&skipped, + src_revs_dir, dst_revs_dir, rev, + max_files_per_dir, + iterpool)); + /* Copy the revprop file. */ + SVN_ERR(hotcopy_copy_shard_file(&skipped, + src_revprops_dir, dst_revprops_dir, + rev, max_files_per_dir, + iterpool)); + + /* Whenever this revision did not previously exist in the destination, + * checkpoint the progress via 'current' (do that once per full shard + * in order not to slow things down). */ + if (rev > dst_youngest) + { + if (max_files_per_dir && (rev % max_files_per_dir == 0)) + { + SVN_ERR(svn_fs_fs__write_current(dst_fs, rev, 0, 0, + iterpool)); + } + } + + if (notify_func && !skipped) + notify_func(notify_baton, rev, rev, iterpool); + } + svn_pool_destroy(iterpool); + + /* 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); + + return SVN_NO_ERROR; +} + +/* Shortcut for the revision and revprop copying for old (1 or 2) format + * filesystems without sharding and packing. Copy the non-sharded revision + * and revprop files from SRC_FS to DST_FS. Do not re-copy data which + * already exists in DST_FS. Do not somehow checkpoint the results in + * the 'current' file in DST_FS. Indicate progress via the optional + * NOTIFY_FUNC callback using NOTIFY_BATON. Use POOL for temporary + * allocations. Also see hotcopy_revisions(). + */ +static svn_error_t * +hotcopy_revisions_old(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_revnum_t src_youngest, + const char *src_revs_dir, + const char *dst_revs_dir, + const char *src_revprops_dir, + const char *dst_revprops_dir, + svn_fs_hotcopy_notify_t notify_func, + void* notify_baton, + svn_cancel_func_t cancel_func, + void* cancel_baton, + apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); + svn_revnum_t rev; + + for (rev = 0; rev <= src_youngest; rev++) + { + svn_boolean_t skipped = TRUE; + + svn_pool_clear(iterpool); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + SVN_ERR(hotcopy_io_dir_file_copy(&skipped, src_revs_dir, dst_revs_dir, + apr_psprintf(iterpool, "%ld", rev), + iterpool)); + SVN_ERR(hotcopy_io_dir_file_copy(&skipped, src_revprops_dir, + dst_revprops_dir, + apr_psprintf(iterpool, "%ld", rev), + iterpool)); + + if (notify_func && !skipped) + notify_func(notify_baton, rev, rev, iterpool); + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Baton for hotcopy_body(). */ +struct hotcopy_body_baton { + svn_fs_t *src_fs; + svn_fs_t *dst_fs; + svn_boolean_t incremental; + svn_fs_hotcopy_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_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; + svn_boolean_t incremental = hbb->incremental; + svn_fs_hotcopy_notify_t notify_func = hbb->notify_func; + void* notify_baton = hbb->notify_baton; + svn_cancel_func_t cancel_func = hbb->cancel_func; + void* cancel_baton = hbb->cancel_baton; + svn_revnum_t src_youngest; + apr_uint64_t src_next_node_id; + apr_uint64_t src_next_copy_id; + svn_revnum_t dst_youngest; + const char *src_revprops_dir; + const char *dst_revprops_dir; + const char *src_revs_dir; + const char *dst_revs_dir; + const char *src_subdir; + const char *dst_subdir; + 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 *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); + + return svn_error_quick_wrapf(err, + _("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); + } + 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(svn_fs_fs__read_current(&src_youngest, &src_next_node_id, + &src_next_copy_id, src_fs, pool)); + if (incremental) + { + SVN_ERR(svn_fs_fs__youngest_rev(&dst_youngest, dst_fs, 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; + + src_revs_dir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool); + dst_revs_dir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool); + src_revprops_dir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool); + dst_revprops_dir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool); + + /* Ensure that the required folders exist in the destination + * before actually copying the revisions and revprops. */ + SVN_ERR(svn_io_make_dir_recursively(dst_revs_dir, pool)); + SVN_ERR(svn_io_make_dir_recursively(dst_revprops_dir, pool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Split the logic for new and old FS formats. The latter is much simpler + * due to the absense of sharding and packing. However, it requires special + * care when updating the 'current' file (which contains not just the + * revision number, but also the next-ID counters). */ + if (src_ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + SVN_ERR(hotcopy_revisions(src_fs, dst_fs, src_youngest, dst_youngest, + incremental, src_revs_dir, dst_revs_dir, + src_revprops_dir, dst_revprops_dir, + notify_func, notify_baton, + cancel_func, cancel_baton, pool)); + SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, 0, 0, pool)); + } + else + { + SVN_ERR(hotcopy_revisions_old(src_fs, dst_fs, src_youngest, + src_revs_dir, dst_revs_dir, + src_revprops_dir, dst_revprops_dir, + notify_func, notify_baton, + cancel_func, cancel_baton, pool)); + SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, src_next_node_id, + src_next_copy_id, 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(NULL, 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 + * that did not make it into the destination. */ + 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)); + + /* The source might have r/o flags set on it - which would be + carried over to the copy. */ + SVN_ERR(svn_io_set_file_read_write(dst_subdir, FALSE, pool)); + SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, src_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)); + + return SVN_NO_ERROR; +} + +/* 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; + + /* Create the DST_FS repository with the same layout as SRC_FS. */ + SVN_ERR(svn_fs_fs__create_file_tree(dst_fs, dst_path, src_ffd->format, + src_ffd->max_files_per_dir, + src_ffd->use_log_addressing, + pool)); + + /* Copy the UUID. Hotcopy destination receives a new instance ID, but + * has the same filesystem UUID as the source. */ + SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, NULL, pool)); + + /* Remove revision 0 contents. Otherwise, it may not get overwritten + * due to having a newer timestamp. */ + SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_rev(dst_fs, 0, pool), pool)); + SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_revprops(dst_fs, 0, pool), + pool)); + + /* This filesystem is ready. Stamp it with a format number. Fail if + * the 'format' file should already exist. */ + SVN_ERR(svn_fs_fs__write_format(dst_fs, FALSE, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__hotcopy_prepare_target(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *dst_path, + svn_boolean_t incremental, + apr_pool_t *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)); + } + } + 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)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + struct hotcopy_body_baton hbb; + + hbb.src_fs = src_fs; + hbb.dst_fs = dst_fs; + hbb.incremental = incremental; + hbb.notify_func = notify_func; + hbb.notify_baton = notify_baton; + hbb.cancel_func = cancel_func; + hbb.cancel_baton = cancel_baton; + SVN_ERR(svn_fs_fs__with_all_locks(dst_fs, hotcopy_body, &hbb, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.h b/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.h new file mode 100644 index 000000000..ddd62183a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/hotcopy.h @@ -0,0 +1,51 @@ +/* hotcopy.h : interface to the native filesystem layer + * + * ==================================================================== + * 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_FS__HOTCOPY_H +#define SVN_LIBSVN_FS__HOTCOPY_H + +#include "fs.h" + +/* Create an empty copy of the fsfs filesystem SRC_FS into a new DST_FS at + * DST_PATH. If INCREMENTAL is TRUE, perform a few pre-checks only if + * a repo already exists at DST_PATH. Use POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__hotcopy_prepare_target(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *dst_path, + svn_boolean_t incremental, + apr_pool_t *pool); + +/* Copy the fsfs filesystem SRC_FS into DST_FS. If INCREMENTAL is TRUE, do + * not re-copy data which already exists in DST_FS. Indicate progress via + * the optional NOTIFY_FUNC callback using NOTIFY_BATON. Use POOL for + * temporary allocations. */ +svn_error_t * svn_fs_fs__hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/id.c b/contrib/subversion/subversion/libsvn_fs_fs/id.c index 131782969..bd505e06c 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/id.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/id.c @@ -24,90 +24,311 @@ #include #include "id.h" +#include "index.h" + #include "../libsvn_fs/fs-loader.h" #include "private/svn_temp_serializer.h" #include "private/svn_string_private.h" -typedef struct id_private_t { - const char *node_id; - const char *copy_id; - const char *txn_id; - svn_revnum_t rev; - apr_off_t offset; -} id_private_t; +typedef struct fs_fs__id_t +{ + /* API visible part */ + svn_fs_id_t generic_id; + + /* private members */ + struct + { + svn_fs_fs__id_part_t node_id; + svn_fs_fs__id_part_t copy_id; + svn_fs_fs__id_part_t txn_id; + svn_fs_fs__id_part_t rev_item; + } private_id; +} fs_fs__id_t; -/* Accessing ID Pieces. */ -const char * -svn_fs_fs__id_node_id(const svn_fs_id_t *id) +/** Like strtol but with a fixed base of 10, locale independent and limited + * to non-negative values. Overflows are indicated by a FALSE return value + * in which case *RESULT_P will not be modified. + * + * This allows the compiler to generate massively faster code. + * (E.g. Avoiding locale specific processing). ID parsing is one of the + * most CPU consuming parts of FSFS data access. Better be quick. + */ +static svn_boolean_t +locale_independent_strtol(long *result_p, + const char* buffer, + const char** end) +{ + /* We allow positive values only. We use unsigned arithmetics to get + * well-defined overflow behavior. It also happens to allow for a wider + * range of compiler-side optimizations. */ + unsigned long result = 0; + while (1) + { + unsigned long c = (unsigned char)*buffer - (unsigned char)'0'; + unsigned long next; + + /* This implies the NUL check. */ + if (c > 9) + break; + + /* Overflow check. Passing this, NEXT can be no more than ULONG_MAX+9 + * before being truncated to ULONG but it still covers 0 .. ULONG_MAX. + */ + if (result > ULONG_MAX / 10) + return FALSE; + + next = result * 10 + c; + + /* Overflow check. In case of an overflow, NEXT is 0..9. + * In the non-overflow case, RESULT is either >= 10 or RESULT and NEXT + * are both 0. */ + if (next < result) + return FALSE; + + result = next; + ++buffer; + } + + *end = buffer; + if (result > LONG_MAX) + return FALSE; + + *result_p = (long)result; + + return TRUE; +} + +/* Parse the NUL-terminated ID part at DATA and write the result into *PART. + * Return TRUE if no errors were detected. */ +static svn_boolean_t +part_parse(svn_fs_fs__id_part_t *part, + const char *data) { - id_private_t *pvt = id->fsap_data; + const char *end; + + /* special case: ID inside some transaction */ + if (data[0] == '_') + { + part->revision = SVN_INVALID_REVNUM; + part->number = svn__base36toui64(&data, data + 1); + return *data == '\0'; + } + + /* special case: 0 / default ID */ + if (data[0] == '0' && data[1] == '\0') + { + part->revision = 0; + part->number = 0; + return TRUE; + } - return pvt->node_id; + /* read old style / new style ID */ + part->number = svn__base36toui64(&data, data); + if (data[0] != '-') + { + part->revision = 0; + return *data == '\0'; + } + + return locale_independent_strtol(&part->revision, data+1, &end); } +/* Parse the transaction id in DATA and store the result in *TXN_ID. + * Return FALSE if there was some problem. + */ +static svn_boolean_t +txn_id_parse(svn_fs_fs__id_part_t *txn_id, + const char *data) +{ + const char *end; + if (!locale_independent_strtol(&txn_id->revision, data, &end)) + return FALSE; + + data = end; + if (*data != '-') + return FALSE; -const char * -svn_fs_fs__id_copy_id(const svn_fs_id_t *id) + ++data; + txn_id->number = svn__base36toui64(&data, data); + return *data == '\0'; +} + +/* Write the textual representation of *PART into P and return a pointer + * to the first position behind that string. + */ +static char * +unparse_id_part(char *p, + const svn_fs_fs__id_part_t *part) +{ + if (SVN_IS_VALID_REVNUM(part->revision)) + { + /* ordinary old style / new style ID */ + p += svn__ui64tobase36(p, part->number); + if (part->revision > 0) + { + *(p++) = '-'; + p += svn__i64toa(p, part->revision); + } + } + else + { + /* in txn: mark with "_" prefix */ + *(p++) = '_'; + p += svn__ui64tobase36(p, part->number); + } + + *(p++) = '.'; + + return p; +} + + + +/* Operations on ID parts */ + +svn_boolean_t +svn_fs_fs__id_part_is_root(const svn_fs_fs__id_part_t* part) +{ + return part->revision == 0 && part->number == 0; +} + +svn_boolean_t +svn_fs_fs__id_part_eq(const svn_fs_fs__id_part_t *lhs, + const svn_fs_fs__id_part_t *rhs) +{ + return lhs->revision == rhs->revision && lhs->number == rhs->number; +} + +svn_boolean_t +svn_fs_fs__id_txn_used(const svn_fs_fs__id_part_t *txn_id) { - id_private_t *pvt = id->fsap_data; + return SVN_IS_VALID_REVNUM(txn_id->revision) || (txn_id->number != 0); +} - return pvt->copy_id; +void +svn_fs_fs__id_txn_reset(svn_fs_fs__id_part_t *txn_id) +{ + txn_id->revision = SVN_INVALID_REVNUM; + txn_id->number = 0; } +svn_error_t * +svn_fs_fs__id_txn_parse(svn_fs_fs__id_part_t *txn_id, + const char *data) +{ + if (! txn_id_parse(txn_id, data)) + return svn_error_createf(SVN_ERR_FS_MALFORMED_TXN_ID, NULL, + "malformed txn id '%s'", data); + + return SVN_NO_ERROR; +} const char * -svn_fs_fs__id_txn_id(const svn_fs_id_t *id) +svn_fs_fs__id_txn_unparse(const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) { - id_private_t *pvt = id->fsap_data; + char string[2 * SVN_INT64_BUFFER_SIZE + 1]; + char *p = string; - return pvt->txn_id; + p += svn__i64toa(p, txn_id->revision); + *(p++) = '-'; + p += svn__ui64tobase36(p, txn_id->number); + + return apr_pstrmemdup(pool, string, p - string); } + + +/* Accessing ID Pieces. */ -svn_revnum_t -svn_fs_fs__id_rev(const svn_fs_id_t *id) +const svn_fs_fs__id_part_t * +svn_fs_fs__id_node_id(const svn_fs_id_t *fs_id) { - id_private_t *pvt = id->fsap_data; + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; - return pvt->rev; + return &id->private_id.node_id; } -apr_off_t -svn_fs_fs__id_offset(const svn_fs_id_t *id) +const svn_fs_fs__id_part_t * +svn_fs_fs__id_copy_id(const svn_fs_id_t *fs_id) { - id_private_t *pvt = id->fsap_data; + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; - return pvt->offset; + return &id->private_id.copy_id; } +const svn_fs_fs__id_part_t * +svn_fs_fs__id_txn_id(const svn_fs_id_t *fs_id) +{ + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + return &id->private_id.txn_id; +} + + +const svn_fs_fs__id_part_t * +svn_fs_fs__id_rev_item(const svn_fs_id_t *fs_id) +{ + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + return &id->private_id.rev_item; +} + +svn_revnum_t +svn_fs_fs__id_rev(const svn_fs_id_t *fs_id) +{ + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + return id->private_id.rev_item.revision; +} + +apr_uint64_t +svn_fs_fs__id_item(const svn_fs_id_t *fs_id) +{ + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + return id->private_id.rev_item.number; +} + +svn_boolean_t +svn_fs_fs__id_is_txn(const svn_fs_id_t *fs_id) +{ + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + return svn_fs_fs__id_txn_used(&id->private_id.txn_id); +} + svn_string_t * -svn_fs_fs__id_unparse(const svn_fs_id_t *id, +svn_fs_fs__id_unparse(const svn_fs_id_t *fs_id, apr_pool_t *pool) { - id_private_t *pvt = id->fsap_data; + char string[6 * SVN_INT64_BUFFER_SIZE + 10]; + const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id; + + char *p = unparse_id_part(string, &id->private_id.node_id); + p = unparse_id_part(p, &id->private_id.copy_id); - if ((! pvt->txn_id)) + if (svn_fs_fs__id_txn_used(&id->private_id.txn_id)) { - char rev_string[SVN_INT64_BUFFER_SIZE]; - char offset_string[SVN_INT64_BUFFER_SIZE]; - - svn__i64toa(rev_string, pvt->rev); - svn__i64toa(offset_string, pvt->offset); - return svn_string_createf(pool, "%s.%s.r%s/%s", - pvt->node_id, pvt->copy_id, - rev_string, offset_string); + *(p++) = 't'; + p += svn__i64toa(p, id->private_id.txn_id.revision); + *(p++) = '-'; + p += svn__ui64tobase36(p, id->private_id.txn_id.number); } else { - return svn_string_createf(pool, "%s.%s.t%s", - pvt->node_id, pvt->copy_id, - pvt->txn_id); + *(p++) = 'r'; + p += svn__i64toa(p, id->private_id.rev_item.revision); + *(p++) = '/'; + p += svn__i64toa(p, id->private_id.rev_item.number); } + + return svn_string_ncreate(string, p - string, pool); } @@ -117,23 +338,20 @@ svn_boolean_t svn_fs_fs__id_eq(const svn_fs_id_t *a, const svn_fs_id_t *b) { - id_private_t *pvta = a->fsap_data, *pvtb = b->fsap_data; + const fs_fs__id_t *id_a = (const fs_fs__id_t *)a; + const fs_fs__id_t *id_b = (const fs_fs__id_t *)b; if (a == b) return TRUE; - if (strcmp(pvta->node_id, pvtb->node_id) != 0) - return FALSE; - if (strcmp(pvta->copy_id, pvtb->copy_id) != 0) - return FALSE; - if ((pvta->txn_id == NULL) != (pvtb->txn_id == NULL)) - return FALSE; - if (pvta->txn_id && pvtb->txn_id && strcmp(pvta->txn_id, pvtb->txn_id) != 0) - return FALSE; - if (pvta->rev != pvtb->rev) - return FALSE; - if (pvta->offset != pvtb->offset) - return FALSE; - return TRUE; + + return svn_fs_fs__id_part_eq(&id_a->private_id.node_id, + &id_b->private_id.node_id) + && svn_fs_fs__id_part_eq(&id_a->private_id.copy_id, + &id_b->private_id.copy_id) + && svn_fs_fs__id_part_eq(&id_a->private_id.txn_id, + &id_b->private_id.txn_id) + && svn_fs_fs__id_part_eq(&id_a->private_id.rev_item, + &id_b->private_id.rev_item); } @@ -141,30 +359,54 @@ svn_boolean_t svn_fs_fs__id_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b) { - id_private_t *pvta = a->fsap_data, *pvtb = b->fsap_data; + const fs_fs__id_t *id_a = (const fs_fs__id_t *)a; + const fs_fs__id_t *id_b = (const fs_fs__id_t *)b; if (a == b) return TRUE; - /* If both node_ids start with _ and they have differing transaction - IDs, then it is impossible for them to be related. */ - if (pvta->node_id[0] == '_') + + /* If both node_ids have been created within _different_ transactions + (and are still uncommitted), then it is impossible for them to be + related. + + Due to our txn-local temporary IDs, however, they might have been + given the same temporary node ID. We need to detect that case. + */ + if ( id_a->private_id.node_id.revision == SVN_INVALID_REVNUM + && id_b->private_id.node_id.revision == SVN_INVALID_REVNUM) { - if (pvta->txn_id && pvtb->txn_id && - (strcmp(pvta->txn_id, pvtb->txn_id) != 0)) + if (!svn_fs_fs__id_part_eq(&id_a->private_id.txn_id, + &id_b->private_id.txn_id)) return FALSE; + + /* At this point, matching node_ids implies relatedness. */ } - return (strcmp(pvta->node_id, pvtb->node_id) == 0); + return svn_fs_fs__id_part_eq(&id_a->private_id.node_id, + &id_b->private_id.node_id); } -int +svn_fs_node_relation_t svn_fs_fs__id_compare(const svn_fs_id_t *a, const svn_fs_id_t *b) { if (svn_fs_fs__id_eq(a, b)) - return 0; - return (svn_fs_fs__id_check_related(a, b) ? 1 : -1); + return svn_fs_node_unchanged; + return (svn_fs_fs__id_check_related(a, b) ? svn_fs_node_common_ancestor + : svn_fs_node_unrelated); +} + +int +svn_fs_fs__id_part_compare(const svn_fs_fs__id_part_t *a, + const svn_fs_fs__id_part_t *b) +{ + if (a->revision < b->revision) + return -1; + if (a->revision > b->revision) + return 1; + + return a->number < b->number ? -1 : a->number == b->number ? 0 : 1; } @@ -176,87 +418,102 @@ static id_vtable_t id_vtable = { svn_fs_fs__id_compare }; - svn_fs_id_t * -svn_fs_fs__id_txn_create(const char *node_id, - const char *copy_id, - const char *txn_id, - apr_pool_t *pool) +svn_fs_fs__id_txn_create_root(const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) { - svn_fs_id_t *id = apr_palloc(pool, sizeof(*id)); - id_private_t *pvt = apr_palloc(pool, sizeof(*pvt)); + fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id)); + + /* node ID and copy ID are "0" */ + + id->private_id.txn_id = *txn_id; + id->private_id.rev_item.revision = SVN_INVALID_REVNUM; - pvt->node_id = apr_pstrdup(pool, node_id); - pvt->copy_id = apr_pstrdup(pool, copy_id); - pvt->txn_id = apr_pstrdup(pool, txn_id); - pvt->rev = SVN_INVALID_REVNUM; - pvt->offset = -1; + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; - id->vtable = &id_vtable; - id->fsap_data = pvt; - return id; + return (svn_fs_id_t *)id; } +svn_fs_id_t *svn_fs_fs__id_create_root(const svn_revnum_t revision, + apr_pool_t *pool) +{ + fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id)); + + id->private_id.txn_id.revision = SVN_INVALID_REVNUM; + id->private_id.rev_item.revision = revision; + id->private_id.rev_item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE; + + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; + + return (svn_fs_id_t *)id; +} svn_fs_id_t * -svn_fs_fs__id_rev_create(const char *node_id, - const char *copy_id, - svn_revnum_t rev, - apr_off_t offset, +svn_fs_fs__id_txn_create(const svn_fs_fs__id_part_t *node_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { - svn_fs_id_t *id = apr_palloc(pool, sizeof(*id)); - id_private_t *pvt = apr_palloc(pool, sizeof(*pvt)); + fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id)); - pvt->node_id = apr_pstrdup(pool, node_id); - pvt->copy_id = apr_pstrdup(pool, copy_id); - pvt->txn_id = NULL; - pvt->rev = rev; - pvt->offset = offset; + id->private_id.node_id = *node_id; + id->private_id.copy_id = *copy_id; + id->private_id.txn_id = *txn_id; + id->private_id.rev_item.revision = SVN_INVALID_REVNUM; - id->vtable = &id_vtable; - id->fsap_data = pvt; - return id; + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; + + return (svn_fs_id_t *)id; } svn_fs_id_t * -svn_fs_fs__id_copy(const svn_fs_id_t *id, apr_pool_t *pool) +svn_fs_fs__id_rev_create(const svn_fs_fs__id_part_t *node_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *rev_item, + apr_pool_t *pool) { - svn_fs_id_t *new_id = apr_palloc(pool, sizeof(*new_id)); - id_private_t *new_pvt = apr_palloc(pool, sizeof(*new_pvt)); - id_private_t *pvt = id->fsap_data; + fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id)); - new_pvt->node_id = apr_pstrdup(pool, pvt->node_id); - new_pvt->copy_id = apr_pstrdup(pool, pvt->copy_id); - new_pvt->txn_id = pvt->txn_id ? apr_pstrdup(pool, pvt->txn_id) : NULL; - new_pvt->rev = pvt->rev; - new_pvt->offset = pvt->offset; + id->private_id.node_id = *node_id; + id->private_id.copy_id = *copy_id; + id->private_id.txn_id.revision = SVN_INVALID_REVNUM; + id->private_id.rev_item = *rev_item; - new_id->vtable = &id_vtable; - new_id->fsap_data = new_pvt; - return new_id; + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; + + return (svn_fs_id_t *)id; } svn_fs_id_t * -svn_fs_fs__id_parse(const char *data, - apr_size_t len, - apr_pool_t *pool) +svn_fs_fs__id_copy(const svn_fs_id_t *source, apr_pool_t *pool) { - svn_fs_id_t *id; - id_private_t *pvt; - char *data_copy, *str; + const fs_fs__id_t *id = (const fs_fs__id_t *)source; + fs_fs__id_t *new_id = apr_pmemdup(pool, id, sizeof(*new_id)); + + new_id->generic_id.fsap_data = new_id; + + return (svn_fs_id_t *)new_id; +} - /* Dup the ID data into POOL. Our returned ID will have references - into this memory. */ - data_copy = apr_pstrmemdup(pool, data, len); +/* Return an ID resulting from parsing the string DATA, or NULL if DATA is + an invalid ID string. *DATA will be modified / invalidated by this call. */ +static svn_fs_id_t * +id_parse(char *data, + apr_pool_t *pool) +{ + fs_fs__id_t *id; + char *str; /* Alloc a new svn_fs_id_t structure. */ - id = apr_palloc(pool, sizeof(*id)); - pvt = apr_palloc(pool, sizeof(*pvt)); - id->vtable = &id_vtable; - id->fsap_data = pvt; + id = apr_pcalloc(pool, sizeof(*id)); + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; /* Now, we basically just need to "split" this data on `.' characters. We will use svn_cstring_tokenize, which will put @@ -265,141 +522,120 @@ svn_fs_fs__id_parse(const char *data, string.*/ /* Node Id */ - str = svn_cstring_tokenize(".", &data_copy); + str = svn_cstring_tokenize(".", &data); if (str == NULL) return NULL; - pvt->node_id = str; + if (! part_parse(&id->private_id.node_id, str)) + return NULL; /* Copy Id */ - str = svn_cstring_tokenize(".", &data_copy); + str = svn_cstring_tokenize(".", &data); if (str == NULL) return NULL; - pvt->copy_id = str; + if (! part_parse(&id->private_id.copy_id, str)) + return NULL; /* Txn/Rev Id */ - str = svn_cstring_tokenize(".", &data_copy); + str = svn_cstring_tokenize(".", &data); if (str == NULL) return NULL; if (str[0] == 'r') { apr_int64_t val; + const char *tmp; svn_error_t *err; /* This is a revision type ID */ - pvt->txn_id = NULL; + id->private_id.txn_id.revision = SVN_INVALID_REVNUM; + id->private_id.txn_id.number = 0; - data_copy = str + 1; - str = svn_cstring_tokenize("/", &data_copy); + data = str + 1; + str = svn_cstring_tokenize("/", &data); if (str == NULL) return NULL; - pvt->rev = SVN_STR_TO_REV(str); - - str = svn_cstring_tokenize("/", &data_copy); - if (str == NULL) + if (!locale_independent_strtol(&id->private_id.rev_item.revision, + str, &tmp)) return NULL; - err = svn_cstring_atoi64(&val, str); + + err = svn_cstring_atoi64(&val, data); if (err) { svn_error_clear(err); return NULL; } - pvt->offset = (apr_off_t)val; + id->private_id.rev_item.number = (apr_uint64_t)val; } else if (str[0] == 't') { /* This is a transaction type ID */ - pvt->txn_id = str + 1; - pvt->rev = SVN_INVALID_REVNUM; - pvt->offset = -1; + id->private_id.rev_item.revision = SVN_INVALID_REVNUM; + id->private_id.rev_item.number = 0; + + if (! txn_id_parse(&id->private_id.txn_id, str + 1)) + return NULL; } else return NULL; - return id; + return (svn_fs_id_t *)id; } -/* (de-)serialization support */ - -/* Serialization of the PVT sub-structure within the CONTEXT. - */ -static void -serialize_id_private(svn_temp_serializer__context_t *context, - const id_private_t * const *pvt) +svn_error_t * +svn_fs_fs__id_parse(const svn_fs_id_t **id_p, + char *data, + apr_pool_t *pool) { - const id_private_t *private = *pvt; - - /* serialize the pvt data struct itself */ - svn_temp_serializer__push(context, - (const void * const *)pvt, - sizeof(*private)); + svn_fs_id_t *id = id_parse(data, pool); + if (id == NULL) + return svn_error_createf(SVN_ERR_FS_MALFORMED_NODEREV_ID, NULL, + "Malformed node revision ID string"); - /* append the referenced strings */ - svn_temp_serializer__add_string(context, &private->node_id); - svn_temp_serializer__add_string(context, &private->copy_id); - svn_temp_serializer__add_string(context, &private->txn_id); + *id_p = id; - /* return to caller's nesting level */ - svn_temp_serializer__pop(context); + return SVN_NO_ERROR; } +/* (de-)serialization support */ + /* Serialize an ID within the serialization CONTEXT. */ void svn_fs_fs__id_serialize(svn_temp_serializer__context_t *context, - const struct svn_fs_id_t * const *id) + const svn_fs_id_t * const *in) { + const fs_fs__id_t *id = (const fs_fs__id_t *)*in; + /* nothing to do for NULL ids */ - if (*id == NULL) + if (id == NULL) return; /* serialize the id data struct itself */ - svn_temp_serializer__push(context, - (const void * const *)id, - sizeof(**id)); - - /* serialize the id_private_t data sub-struct */ - serialize_id_private(context, - (const id_private_t * const *)&(*id)->fsap_data); - - /* return to caller's nesting level */ - svn_temp_serializer__pop(context); -} - -/* Deserialization of the PVT sub-structure in BUFFER. - */ -static void -deserialize_id_private(void *buffer, id_private_t **pvt) -{ - /* fixup the reference to the only sub-structure */ - id_private_t *private; - svn_temp_deserializer__resolve(buffer, (void**)pvt); - - /* fixup the sub-structure itself */ - private = *pvt; - svn_temp_deserializer__resolve(private, (void**)&private->node_id); - svn_temp_deserializer__resolve(private, (void**)&private->copy_id); - svn_temp_deserializer__resolve(private, (void**)&private->txn_id); + svn_temp_serializer__add_leaf(context, + (const void * const *)in, + sizeof(fs_fs__id_t)); } /* Deserialize an ID inside the BUFFER. */ void -svn_fs_fs__id_deserialize(void *buffer, svn_fs_id_t **id) +svn_fs_fs__id_deserialize(void *buffer, svn_fs_id_t **in_out) { + fs_fs__id_t *id; + /* The id maybe all what is in the whole buffer. * Don't try to fixup the pointer in that case*/ - if (*id != buffer) - svn_temp_deserializer__resolve(buffer, (void**)id); + if (*in_out != buffer) + svn_temp_deserializer__resolve(buffer, (void**)in_out); + + id = (fs_fs__id_t *)*in_out; /* no id, no sub-structure fixup necessary */ - if (*id == NULL) + if (id == NULL) return; /* the stored vtable is bogus at best -> set the right one */ - (*id)->vtable = &id_vtable; - - /* handle sub-structures */ - deserialize_id_private(*id, (id_private_t **)&(*id)->fsap_data); + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = id; } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/id.h b/contrib/subversion/subversion/libsvn_fs_fs/id.h index 11da46627..d556a1697 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/id.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/id.h @@ -24,30 +24,63 @@ #define SVN_LIBSVN_FS_FS_ID_H #include "svn_fs.h" +#include "private/svn_fs_fs_private.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +/*** Operations on ID parts. ***/ + +/* Return TRUE, if both elements of the PART is 0, i.e. this is the default + * value if e.g. no copies were made of this node. */ +svn_boolean_t svn_fs_fs__id_part_is_root(const svn_fs_fs__id_part_t *part); + +/* Return TRUE, if all element values of *LHS and *RHS match. */ +svn_boolean_t svn_fs_fs__id_part_eq(const svn_fs_fs__id_part_t *lhs, + const svn_fs_fs__id_part_t *rhs); + +/* Return TRUE, if TXN_ID is used, i.e. doesn't contain just the defaults. */ +svn_boolean_t svn_fs_fs__id_txn_used(const svn_fs_fs__id_part_t *txn_id); + +/* Reset TXN_ID to the defaults. */ +void svn_fs_fs__id_txn_reset(svn_fs_fs__id_part_t *txn_id); + +/* Parse the transaction id in DATA and store the result in *TXN_ID */ +svn_error_t *svn_fs_fs__id_txn_parse(svn_fs_fs__id_part_t *txn_id, + const char *data); + +/* Convert the transaction id in *TXN_ID into a textual representation + * allocated in POOL. */ +const char *svn_fs_fs__id_txn_unparse(const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + /*** ID accessor functions. ***/ /* Get the "node id" portion of ID. */ -const char *svn_fs_fs__id_node_id(const svn_fs_id_t *id); +const svn_fs_fs__id_part_t *svn_fs_fs__id_node_id(const svn_fs_id_t *id); /* Get the "copy id" portion of ID. */ -const char *svn_fs_fs__id_copy_id(const svn_fs_id_t *id); +const svn_fs_fs__id_part_t *svn_fs_fs__id_copy_id(const svn_fs_id_t *id); /* Get the "txn id" portion of ID, or NULL if it is a permanent ID. */ -const char *svn_fs_fs__id_txn_id(const svn_fs_id_t *id); +const svn_fs_fs__id_part_t *svn_fs_fs__id_txn_id(const svn_fs_id_t *id); + +/* Get the "rev,item" portion of ID. */ +const svn_fs_fs__id_part_t *svn_fs_fs__id_rev_item(const svn_fs_id_t *id); /* Get the "rev" portion of ID, or SVN_INVALID_REVNUM if it is a transaction ID. */ svn_revnum_t svn_fs_fs__id_rev(const svn_fs_id_t *id); -/* Access the "offset" portion of the ID, or -1 if it is a transaction +/* Access the "item" portion of the ID, or 0 if it is a transaction ID. */ -apr_off_t svn_fs_fs__id_offset(const svn_fs_id_t *id); +apr_uint64_t svn_fs_fs__id_item(const svn_fs_id_t *id); + +/* Return TRUE, if this is a transaction ID. */ +svn_boolean_t svn_fs_fs__id_is_txn(const svn_fs_id_t *id); /* Convert ID into string form, allocated in POOL. */ svn_string_t *svn_fs_fs__id_unparse(const svn_fs_id_t *id, @@ -61,34 +94,48 @@ svn_boolean_t svn_fs_fs__id_eq(const svn_fs_id_t *a, svn_boolean_t svn_fs_fs__id_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b); -/* Return 0 if A and B are equal, 1 if they are related, -1 otherwise. */ -int svn_fs_fs__id_compare(const svn_fs_id_t *a, - const svn_fs_id_t *b); +/* Return the noderev relationship between A and B. */ +svn_fs_node_relation_t svn_fs_fs__id_compare(const svn_fs_id_t *a, + const svn_fs_id_t *b); + +/* Return 0 if A and B are equal, 1 if A is "greater than" B, -1 otherwise. */ +int svn_fs_fs__id_part_compare(const svn_fs_fs__id_part_t *a, + const svn_fs_fs__id_part_t *b); + +/* Create the txn root ID for transaction TXN_ID. Allocate it in POOL. */ +svn_fs_id_t *svn_fs_fs__id_txn_create_root(const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Create the root ID for REVISION (logical addressing node only). + Allocate it in POOL. */ +svn_fs_id_t *svn_fs_fs__id_create_root(const svn_revnum_t revision, + apr_pool_t *pool); /* Create an ID within a transaction based on NODE_ID, COPY_ID, and TXN_ID, allocated in POOL. */ -svn_fs_id_t *svn_fs_fs__id_txn_create(const char *node_id, - const char *copy_id, - const char *txn_id, +svn_fs_id_t *svn_fs_fs__id_txn_create(const svn_fs_fs__id_part_t *node_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); -/* Create a permanent ID based on NODE_ID, COPY_ID, REV, and OFFSET, +/* Create a permanent ID based on NODE_ID, COPY_ID and REV_ITEM, allocated in POOL. */ -svn_fs_id_t *svn_fs_fs__id_rev_create(const char *node_id, - const char *copy_id, - svn_revnum_t rev, - apr_off_t offset, +svn_fs_id_t *svn_fs_fs__id_rev_create(const svn_fs_fs__id_part_t *node_id, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *rev_item, apr_pool_t *pool); /* Return a copy of ID, allocated from POOL. */ svn_fs_id_t *svn_fs_fs__id_copy(const svn_fs_id_t *id, apr_pool_t *pool); -/* Return an ID resulting from parsing the string DATA (with length - LEN), or NULL if DATA is an invalid ID string. */ -svn_fs_id_t *svn_fs_fs__id_parse(const char *data, - apr_size_t len, - apr_pool_t *pool); +/* Return an ID in *ID_P resulting from parsing the string DATA, or an error + if DATA is an invalid ID string. *DATA will be modified / invalidated by + this call. */ +svn_error_t * +svn_fs_fs__id_parse(const svn_fs_id_t **id_p, + char *data, + apr_pool_t *pool); /* (de-)serialization support*/ diff --git a/contrib/subversion/subversion/libsvn_fs_fs/index.c b/contrib/subversion/subversion/libsvn_fs_fs/index.c new file mode 100644 index 000000000..a6695580a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/index.c @@ -0,0 +1,3470 @@ +/* index.c indexing support for FSFS 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. + * ==================================================================== + */ + +#include + +#include "svn_io.h" +#include "svn_pools.h" +#include "svn_sorts.h" + +#include "svn_private_config.h" + +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "index.h" +#include "pack.h" +#include "temp_serializer.h" +#include "util.h" +#include "fs_fs.h" + +#include "../libsvn_fs/fs-loader.h" + +/* maximum length of a uint64 in an 7/8b encoding */ +#define ENCODED_INT_LENGTH 10 + +/* APR is missing an APR_OFF_T_MAX. So, define one. We will use it to + * limit file offsets stored in the indexes. + * + * We assume that everything shorter than 64 bits, it is at least 32 bits. + * We also assume that the type is always signed meaning we only have an + * effective positive range of 63 or 31 bits, respectively. + */ +static +const apr_uint64_t off_t_max = (sizeof(apr_off_t) == sizeof(apr_int64_t)) + ? APR_INT64_MAX + : APR_INT32_MAX; + +/* We store P2L proto-index entries as 6 values, 64 bits each on disk. + * See also svn_fs_fs__p2l_proto_index_add_entry(). + */ +#define P2L_PROTO_INDEX_ENTRY_SIZE (6 * sizeof(apr_uint64_t)) + +/* We put this string in front of the L2P index header. */ +#define L2P_STREAM_PREFIX "L2P-INDEX\n" + +/* We put this string in front of the P2L index header. */ +#define P2L_STREAM_PREFIX "P2L-INDEX\n" + +/* Size of the buffer that will fit the index header prefixes. */ +#define STREAM_PREFIX_LEN MAX(sizeof(L2P_STREAM_PREFIX), \ + sizeof(P2L_STREAM_PREFIX)) + +/* Page tables in the log-to-phys index file exclusively contain entries + * of this type to describe position and size of a given page. + */ +typedef struct l2p_page_table_entry_t +{ + /* global offset on the page within the index file */ + apr_uint64_t offset; + + /* number of mapping entries in that page */ + apr_uint32_t entry_count; + + /* size of the page on disk (in the index file) */ + apr_uint32_t size; +} l2p_page_table_entry_t; + +/* Master run-time data structure of an log-to-phys index. It contains + * the page tables of every revision covered by that index - but not the + * pages themselves. + */ +typedef struct l2p_header_t +{ + /* first revision covered by this index */ + svn_revnum_t first_revision; + + /* number of revisions covered */ + apr_size_t revision_count; + + /* (max) number of entries per page */ + apr_uint32_t page_size; + + /* indexes into PAGE_TABLE that mark the first page of the respective + * revision. PAGE_TABLE_INDEX[REVISION_COUNT] points to the end of + * PAGE_TABLE. + */ + apr_size_t * page_table_index; + + /* Page table covering all pages in the index */ + l2p_page_table_entry_t * page_table; +} l2p_header_t; + +/* Run-time data structure containing a single log-to-phys index page. + */ +typedef struct l2p_page_t +{ + /* number of entries in the OFFSETS array */ + apr_uint32_t entry_count; + + /* global file offsets (item index is the array index) within the + * packed or non-packed rev file. Offset will be -1 for unused / + * invalid item index values. */ + apr_uint64_t *offsets; +} l2p_page_t; + +/* All of the log-to-phys proto index file consist of entries of this type. + */ +typedef struct l2p_proto_entry_t +{ + /* phys offset + 1 of the data container. 0 for "new revision" entries. */ + apr_uint64_t offset; + + /* corresponding item index. 0 for "new revision" entries. */ + apr_uint64_t item_index; +} l2p_proto_entry_t; + +/* Master run-time data structure of an phys-to-log index. It contains + * an array with one offset value for each rev file cluster. + */ +typedef struct p2l_header_t +{ + /* first revision covered by the index (and rev file) */ + svn_revnum_t first_revision; + + /* number of bytes in the rev files covered by each p2l page */ + apr_uint64_t page_size; + + /* number of pages / clusters in that rev file */ + apr_size_t page_count; + + /* number of bytes in the rev file */ + apr_uint64_t file_size; + + /* offsets of the pages / cluster descriptions within the index file */ + apr_off_t *offsets; +} p2l_header_t; + +/* + * packed stream + * + * This is a utility object that will read files containing 7b/8b encoded + * unsigned integers. It decodes them in batches to minimize overhead + * and supports random access to random file locations. + */ + +/* How many numbers we will pre-fetch and buffer in a packed number stream. + */ +enum { MAX_NUMBER_PREFETCH = 64 }; + +/* Prefetched number entry in a packed number stream. + */ +typedef struct value_position_pair_t +{ + /* prefetched number */ + apr_uint64_t value; + + /* number of bytes read, *including* this number, since the buffer start */ + apr_size_t total_len; +} value_position_pair_t; + +/* State of a prefetching packed number stream. It will read compressed + * index data efficiently and present it as a series of non-packed uint64. + */ +struct svn_fs_fs__packed_number_stream_t +{ + /* underlying data file containing the packed values */ + apr_file_t *file; + + /* Offset within FILE at which the stream data starts + * (i.e. which offset will reported as offset 0 by packed_stream_offset). */ + apr_off_t stream_start; + + /* First offset within FILE after the stream data. + * Attempts to read beyond this will cause an "Unexpected End Of Stream" + * error. */ + apr_off_t stream_end; + + /* number of used entries in BUFFER (starting at index 0) */ + apr_size_t used; + + /* index of the next number to read from the BUFFER (0 .. USED). + * If CURRENT == USED, we need to read more data upon get() */ + apr_size_t current; + + /* offset in FILE from which the first entry in BUFFER has been read */ + apr_off_t start_offset; + + /* offset in FILE from which the next number has to be read */ + apr_off_t next_offset; + + /* read the file in chunks of this size */ + apr_size_t block_size; + + /* pool to be used for file ops etc. */ + apr_pool_t *pool; + + /* buffer for prefetched values */ + value_position_pair_t buffer[MAX_NUMBER_PREFETCH]; +}; + +/* Return an svn_error_t * object for error ERR on STREAM with the given + * MESSAGE string. The latter must have a placeholder for the index file + * name ("%s") and the current read offset (e.g. "0x%lx"). + */ +static svn_error_t * +stream_error_create(svn_fs_fs__packed_number_stream_t *stream, + apr_status_t err, + const char *message) +{ + const char *file_name; + apr_off_t offset; + SVN_ERR(svn_io_file_name_get(&file_name, stream->file, + stream->pool)); + SVN_ERR(svn_fs_fs__get_file_offset(&offset, stream->file, stream->pool)); + + return svn_error_createf(err, NULL, message, file_name, + apr_psprintf(stream->pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)offset)); +} + +/* Read up to MAX_NUMBER_PREFETCH numbers from the STREAM->NEXT_OFFSET in + * STREAM->FILE and buffer them. + * + * We don't want GCC and others to inline this (infrequently called) + * function into packed_stream_get() because it prevents the latter from + * being inlined itself. + */ +SVN__PREVENT_INLINE +static svn_error_t * +packed_stream_read(svn_fs_fs__packed_number_stream_t *stream) +{ + unsigned char buffer[MAX_NUMBER_PREFETCH]; + apr_size_t read = 0; + apr_size_t i; + value_position_pair_t *target; + apr_off_t block_start = 0; + apr_off_t block_left = 0; + apr_status_t err; + + /* all buffered data will have been read starting here */ + stream->start_offset = stream->next_offset; + + /* packed numbers are usually not aligned to MAX_NUMBER_PREFETCH blocks, + * i.e. the last number has been incomplete (and not buffered in stream) + * and need to be re-read. Therefore, always correct the file pointer. + */ + SVN_ERR(svn_io_file_aligned_seek(stream->file, stream->block_size, + &block_start, stream->next_offset, + stream->pool)); + + /* prefetch at least one number but, if feasible, don't cross block + * boundaries. This shall prevent jumping back and forth between two + * blocks because the extra data was not actually request _now_. + */ + read = sizeof(buffer); + block_left = stream->block_size - (stream->next_offset - block_start); + if (block_left >= 10 && block_left < read) + read = (apr_size_t)block_left; + + /* Don't read beyond the end of the file section that belongs to this + * index / stream. */ + read = (apr_size_t)MIN(read, stream->stream_end - stream->next_offset); + + err = apr_file_read(stream->file, buffer, &read); + if (err && !APR_STATUS_IS_EOF(err)) + return stream_error_create(stream, err, + _("Can't read index file '%s' at offset 0x%s")); + + /* if the last number is incomplete, trim it from the buffer */ + while (read > 0 && buffer[read-1] >= 0x80) + --read; + + /* we call read() only if get() requires more data. So, there must be + * at least *one* further number. */ + if SVN__PREDICT_FALSE(read == 0) + return stream_error_create(stream, err, + _("Unexpected end of index file %s at offset 0x%s")); + + /* parse file buffer and expand into stream buffer */ + target = stream->buffer; + for (i = 0; i < read;) + { + if (buffer[i] < 0x80) + { + /* numbers < 128 are relatively frequent and particularly easy + * to decode. Give them special treatment. */ + target->value = buffer[i]; + ++i; + target->total_len = i; + ++target; + } + else + { + apr_uint64_t value = 0; + apr_uint64_t shift = 0; + while (buffer[i] >= 0x80) + { + value += ((apr_uint64_t)buffer[i] & 0x7f) << shift; + shift += 7; + ++i; + } + + target->value = value + ((apr_uint64_t)buffer[i] << shift); + ++i; + target->total_len = i; + ++target; + + /* let's catch corrupted data early. It would surely cause + * havoc further down the line. */ + if SVN__PREDICT_FALSE(shift > 8 * sizeof(value)) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Corrupt index: number too large")); + } + } + + /* update stream state */ + stream->used = target - stream->buffer; + stream->next_offset = stream->start_offset + i; + stream->current = 0; + + return SVN_NO_ERROR; +} + +/* Create and open a packed number stream reading from offsets START to + * END in FILE and return it in *STREAM. Access the file in chunks of + * BLOCK_SIZE bytes. Expect the stream to be prefixed by STREAM_PREFIX. + * Allocate *STREAM in RESULT_POOL and use SCRATCH_POOL for temporaries. + */ +static svn_error_t * +packed_stream_open(svn_fs_fs__packed_number_stream_t **stream, + apr_file_t *file, + apr_off_t start, + apr_off_t end, + const char *stream_prefix, + apr_size_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char buffer[STREAM_PREFIX_LEN + 1] = { 0 }; + apr_size_t len = strlen(stream_prefix); + svn_fs_fs__packed_number_stream_t *result; + + /* If this is violated, we forgot to adjust STREAM_PREFIX_LEN after + * changing the index header prefixes. */ + SVN_ERR_ASSERT(len < sizeof(buffer)); + + /* Read the header prefix and compare it with the expected prefix */ + SVN_ERR(svn_io_file_aligned_seek(file, block_size, NULL, start, + scratch_pool)); + SVN_ERR(svn_io_file_read_full2(file, buffer, len, NULL, NULL, + scratch_pool)); + + if (strncmp(buffer, stream_prefix, len)) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index stream header prefix mismatch.\n" + " expected: %s" + " found: %s"), stream_prefix, buffer); + + /* Construct the actual stream object. */ + result = apr_palloc(result_pool, sizeof(*result)); + + result->pool = result_pool; + result->file = file; + result->stream_start = start + len; + result->stream_end = end; + + result->used = 0; + result->current = 0; + result->start_offset = result->stream_start; + result->next_offset = result->stream_start; + result->block_size = block_size; + + *stream = result; + + return SVN_NO_ERROR; +} + +/* + * The forced inline is required for performance reasons: This is a very + * hot code path (called for every item we read) but e.g. GCC would rather + * chose to inline packed_stream_read() here, preventing packed_stream_get + * from being inlined itself. + */ +SVN__FORCE_INLINE +static svn_error_t* +packed_stream_get(apr_uint64_t *value, + svn_fs_fs__packed_number_stream_t *stream) +{ + if (stream->current == stream->used) + SVN_ERR(packed_stream_read(stream)); + + *value = stream->buffer[stream->current].value; + ++stream->current; + + return SVN_NO_ERROR; +} + +/* Navigate STREAM to packed stream offset OFFSET. There will be no checks + * whether the given OFFSET is valid. + */ +static void +packed_stream_seek(svn_fs_fs__packed_number_stream_t *stream, + apr_off_t offset) +{ + apr_off_t file_offset = offset + stream->stream_start; + + if ( stream->used == 0 + || offset < stream->start_offset + || offset >= stream->next_offset) + { + /* outside buffered data. Next get() will read() from OFFSET. */ + stream->start_offset = file_offset; + stream->next_offset = file_offset; + stream->current = 0; + stream->used = 0; + } + else + { + /* Find the suitable location in the stream buffer. + * Since our buffer is small, it is efficient enough to simply scan + * it for the desired position. */ + apr_size_t i; + for (i = 0; i < stream->used; ++i) + if (stream->buffer[i].total_len > file_offset - stream->start_offset) + break; + + stream->current = i; + } +} + +/* Return the packed stream offset of at which the next number in the stream + * can be found. + */ +static apr_off_t +packed_stream_offset(svn_fs_fs__packed_number_stream_t *stream) +{ + apr_off_t file_offset + = stream->current == 0 + ? stream->start_offset + : stream->buffer[stream->current-1].total_len + stream->start_offset; + + return file_offset - stream->stream_start; +} + +/* Encode VALUE as 7/8b into P and return the number of bytes written. + * This will be used when _writing_ packed data. packed_stream_* is for + * read operations only. + */ +static apr_size_t +encode_uint(unsigned char *p, apr_uint64_t value) +{ + unsigned char *start = p; + while (value >= 0x80) + { + *p = (unsigned char)((value % 0x80) + 0x80); + value /= 0x80; + ++p; + } + + *p = (unsigned char)(value % 0x80); + return (p - start) + 1; +} + +/* Encode VALUE as 7/8b into P and return the number of bytes written. + * This maps signed ints onto unsigned ones. + */ +static apr_size_t +encode_int(unsigned char *p, apr_int64_t value) +{ + return encode_uint(p, (apr_uint64_t)(value < 0 ? -1 - 2*value : 2*value)); +} + +/* Append VALUE to STREAM in 7/8b encoding. + */ +static svn_error_t * +stream_write_encoded(svn_stream_t *stream, + apr_uint64_t value) +{ + unsigned char encoded[ENCODED_INT_LENGTH]; + + apr_size_t len = encode_uint(encoded, value); + return svn_error_trace(svn_stream_write(stream, (char *)encoded, &len)); +} + +/* Map unsigned VALUE back to signed integer. + */ +static apr_int64_t +decode_int(apr_uint64_t value) +{ + return (apr_int64_t)(value % 2 ? -1 - value / 2 : value / 2); +} + +/* Write VALUE to the PROTO_INDEX file, using SCRATCH_POOL for temporary + * allocations. + * + * The point of this function is to ensure an architecture-independent + * proto-index file format. All data is written as unsigned 64 bits ints + * in little endian byte order. 64 bits is the largest portable integer + * we have and unsigned values have well-defined conversions in C. + */ +static svn_error_t * +write_uint64_to_proto_index(apr_file_t *proto_index, + apr_uint64_t value, + apr_pool_t *scratch_pool) +{ + apr_byte_t buffer[sizeof(value)]; + int i; + apr_size_t written; + + /* Split VALUE into 8 bytes using LE ordering. */ + for (i = 0; i < sizeof(buffer); ++i) + { + /* Unsigned conversions are well-defined ... */ + buffer[i] = (apr_byte_t)value; + value >>= CHAR_BIT; + } + + /* Write it all to disk. */ + SVN_ERR(svn_io_file_write_full(proto_index, buffer, sizeof(buffer), + &written, scratch_pool)); + SVN_ERR_ASSERT(written == sizeof(buffer)); + + return SVN_NO_ERROR; +} + +/* Read one unsigned 64 bit value from PROTO_INDEX file and return it in + * *VALUE_P. If EOF is NULL, error out when trying to read beyond EOF. + * Use SCRATCH_POOL for temporary allocations. + * + * This function is the inverse to write_uint64_to_proto_index (see there), + * reading the external LE byte order and convert it into host byte order. + */ +static svn_error_t * +read_uint64_from_proto_index(apr_file_t *proto_index, + apr_uint64_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_byte_t buffer[sizeof(*value_p)]; + apr_size_t read; + + /* Read the full 8 bytes or our 64 bit value, unless we hit EOF. + * Assert that we never read partial values. */ + SVN_ERR(svn_io_file_read_full2(proto_index, buffer, sizeof(buffer), + &read, eof, scratch_pool)); + SVN_ERR_ASSERT((eof && *eof) || read == sizeof(buffer)); + + /* If we did not hit EOF, reconstruct the uint64 value and return it. */ + if (!eof || !*eof) + { + int i; + apr_uint64_t value; + + /* This could only overflow if CHAR_BIT had a value that is not + * a divisor of 64. */ + value = 0; + for (i = sizeof(buffer) - 1; i >= 0; --i) + value = (value << CHAR_BIT) + buffer[i]; + + *value_p = value; + } + + return SVN_NO_ERROR; +} + +/* Convenience function similar to read_uint64_from_proto_index, but returns + * an uint32 value in VALUE_P. Return an error if the value does not fit. + */ +static svn_error_t * +read_uint32_from_proto_index(apr_file_t *proto_index, + apr_uint32_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_uint64_t value; + SVN_ERR(read_uint64_from_proto_index(proto_index, &value, eof, + scratch_pool)); + if (!eof || !*eof) + { + if (value > APR_UINT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("UINT32 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + value), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)APR_UINT32_MAX)); + + /* This conversion is not lossy because the value can be represented + * in the target type. */ + *value_p = (apr_uint32_t)value; + } + + return SVN_NO_ERROR; +} + +/* Convenience function similar to read_uint64_from_proto_index, but returns + * an off_t value in VALUE_P. Return an error if the value does not fit. + */ +static svn_error_t * +read_off_t_from_proto_index(apr_file_t *proto_index, + apr_off_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_uint64_t value; + SVN_ERR(read_uint64_from_proto_index(proto_index, &value, eof, + scratch_pool)); + if (!eof || !*eof) + { + if (value > off_t_max) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("File offset 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + value), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + off_t_max)); + + /* Shortening conversion from unsigned to signed int is well-defined + * and not lossy in C because the value can be represented in the + * target type. */ + *value_p = (apr_off_t)value; + } + + return SVN_NO_ERROR; +} + +/* + * log-to-phys index + */ + +/* Append ENTRY to log-to-phys PROTO_INDEX file. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_l2p_entry_to_proto_index(apr_file_t *proto_index, + l2p_proto_entry_t entry, + apr_pool_t *scratch_pool) +{ + SVN_ERR(write_uint64_to_proto_index(proto_index, entry.offset, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry.item_index, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read *ENTRY from log-to-phys PROTO_INDEX file and indicate end-of-file + * in *EOF, or error out in that case if EOF is NULL. *ENTRY is in an + * undefined state if an end-of-file occurred. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +read_l2p_entry_from_proto_index(apr_file_t *proto_index, + l2p_proto_entry_t *entry, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + SVN_ERR(read_uint64_from_proto_index(proto_index, &entry->offset, eof, + scratch_pool)); + SVN_ERR(read_uint64_from_proto_index(proto_index, &entry->item_index, eof, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Write the log-2-phys index page description for the l2p_page_entry_t + * array ENTRIES, starting with element START up to but not including END. + * Write the resulting representation into BUFFER. Use SCRATCH_POOL for + * temporary allocations. + */ +static svn_error_t * +encode_l2p_page(apr_array_header_t *entries, + int start, + int end, + svn_spillbuf_t *buffer, + apr_pool_t *scratch_pool) +{ + unsigned char encoded[ENCODED_INT_LENGTH]; + int i; + const apr_uint64_t *values = (const apr_uint64_t *)entries->elts; + apr_uint64_t last_value = 0; + + /* encode items */ + for (i = start; i < end; ++i) + { + apr_int64_t diff = values[i] - last_value; + last_value = values[i]; + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_int(encoded, diff), scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__l2p_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool) +{ + SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE + | APR_CREATE | APR_APPEND | APR_BUFFERED, + APR_OS_DEFAULT, result_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__l2p_proto_index_add_revision(apr_file_t *proto_index, + apr_pool_t *scratch_pool) +{ + l2p_proto_entry_t entry; + entry.offset = 0; + entry.item_index = 0; + + return svn_error_trace(write_l2p_entry_to_proto_index(proto_index, entry, + scratch_pool)); +} + +svn_error_t * +svn_fs_fs__l2p_proto_index_add_entry(apr_file_t *proto_index, + apr_off_t offset, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + l2p_proto_entry_t entry; + + /* make sure the conversion to uint64 works */ + SVN_ERR_ASSERT(offset >= -1); + + /* we support offset '-1' as a "not used" indication */ + entry.offset = (apr_uint64_t)offset + 1; + + /* make sure we can use item_index as an array index when building the + * final index file */ + SVN_ERR_ASSERT(item_index < UINT_MAX / 2); + entry.item_index = item_index; + + return svn_error_trace(write_l2p_entry_to_proto_index(proto_index, entry, + scratch_pool)); +} + +svn_error_t * +svn_fs_fs__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t * result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_file_t *proto_index = NULL; + svn_stream_t *stream; + int i; + apr_uint64_t entry; + svn_boolean_t eof = FALSE; + + int last_page_count = 0; /* total page count at the start of + the current revision */ + + /* temporary data structures that collect the data which will be moved + to the target file in a second step */ + apr_pool_t *local_pool = svn_pool_create(scratch_pool); + apr_pool_t *iterpool = svn_pool_create(local_pool); + apr_array_header_t *page_counts + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + apr_array_header_t *page_sizes + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + apr_array_header_t *entry_counts + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + + /* collect the item offsets and sub-item value for the current revision */ + apr_array_header_t *entries + = apr_array_make(local_pool, 256, sizeof(apr_uint64_t)); + + /* 64k blocks, spill after 16MB */ + svn_spillbuf_t *buffer + = svn_spillbuf__create(0x10000, 0x1000000, local_pool); + + /* Paranoia check that makes later casting to int32 safe. + * The current implementation is limited to 2G entries per page. */ + if (ffd->l2p_page_size > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("L2P index page size %s" + " exceeds current limit of 2G entries"), + apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, + ffd->l2p_page_size)); + + /* start at the beginning of the source file */ + SVN_ERR(svn_io_file_open(&proto_index, proto_file_name, + APR_READ | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, scratch_pool)); + + /* process all entries until we fail due to EOF */ + for (entry = 0; !eof; ++entry) + { + l2p_proto_entry_t proto_entry; + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_l2p_entry_from_proto_index(proto_index, &proto_entry, + &eof, local_pool)); + + /* handle new revision */ + if ((entry > 0 && proto_entry.offset == 0) || eof) + { + /* dump entries, grouped into pages */ + + int entry_count = 0; + for (i = 0; i < entries->nelts; i += entry_count) + { + /* 1 page with up to L2P_PAGE_SIZE entries. + * fsfs.conf settings validation guarantees this to fit into + * our address space. */ + apr_uint64_t last_buffer_size + = (apr_uint64_t)svn_spillbuf__get_size(buffer); + + svn_pool_clear(iterpool); + + entry_count = ffd->l2p_page_size < entries->nelts - i + ? (int)ffd->l2p_page_size + : entries->nelts - i; + SVN_ERR(encode_l2p_page(entries, i, i + entry_count, + buffer, iterpool)); + + APR_ARRAY_PUSH(entry_counts, apr_uint64_t) = entry_count; + APR_ARRAY_PUSH(page_sizes, apr_uint64_t) + = svn_spillbuf__get_size(buffer) - last_buffer_size; + } + + apr_array_clear(entries); + + /* store the number of pages in this revision */ + APR_ARRAY_PUSH(page_counts, apr_uint64_t) + = page_sizes->nelts - last_page_count; + + last_page_count = page_sizes->nelts; + } + else + { + int idx; + + /* store the mapping in our array */ + if (proto_entry.item_index > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s too large " + "in l2p proto index for revision %ld"), + apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, + proto_entry.item_index), + revision + page_counts->nelts); + + idx = (int)proto_entry.item_index; + while (idx >= entries->nelts) + APR_ARRAY_PUSH(entries, apr_uint64_t) = 0; + + APR_ARRAY_IDX(entries, idx, apr_uint64_t) = proto_entry.offset; + } + } + + /* close the source file */ + SVN_ERR(svn_io_file_close(proto_index, local_pool)); + + /* Paranoia check that makes later casting to int32 safe. + * The current implementation is limited to 2G pages per index. */ + if (page_counts->nelts > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("L2P index page count %d" + " exceeds current limit of 2G pages"), + page_counts->nelts); + + /* open target stream. */ + stream = svn_stream_checksummed2(svn_stream_from_aprfile2(index_file, TRUE, + local_pool), + NULL, checksum, svn_checksum_md5, FALSE, + result_pool); + + + /* write header info */ + SVN_ERR(svn_stream_puts(stream, L2P_STREAM_PREFIX)); + SVN_ERR(stream_write_encoded(stream, revision)); + SVN_ERR(stream_write_encoded(stream, ffd->l2p_page_size)); + SVN_ERR(stream_write_encoded(stream, page_counts->nelts)); + SVN_ERR(stream_write_encoded(stream, page_sizes->nelts)); + + /* write the revision table */ + for (i = 0; i < page_counts->nelts; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(page_counts, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* write the page table */ + for (i = 0; i < page_sizes->nelts; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(page_sizes, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + value = APR_ARRAY_IDX(entry_counts, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* append page contents and implicitly close STREAM */ + SVN_ERR(svn_stream_copy3(svn_stream__from_spillbuf(buffer, local_pool), + stream, NULL, NULL, local_pool)); + + svn_pool_destroy(local_pool); + + return SVN_NO_ERROR; +} + +/* If REV_FILE->L2P_STREAM is NULL, create a new stream for the log-to-phys + * index for REVISION in FS and return it in REV_FILE. + */ +static svn_error_t * +auto_open_l2p_index(svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision) +{ + if (rev_file->l2p_stream == NULL) + { + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + SVN_ERR(packed_stream_open(&rev_file->l2p_stream, + rev_file->file, + rev_file->l2p_offset, + rev_file->p2l_offset, + L2P_STREAM_PREFIX, + (apr_size_t)ffd->block_size, + rev_file->pool, + rev_file->pool)); + } + + return SVN_NO_ERROR; +} + +/* Read the header data structure of the log-to-phys index for REVISION + * in FS and return it in *HEADER, allocated in RESULT_POOL. Use REV_FILE + * to access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_header_body(l2p_header_t **header, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_uint64_t value; + apr_size_t i; + apr_size_t page, page_count; + apr_off_t offset; + l2p_header_t *result = apr_pcalloc(result_pool, sizeof(*result)); + apr_size_t page_table_index; + svn_revnum_t next_rev; + + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(auto_open_l2p_index(rev_file, fs, revision)); + packed_stream_seek(rev_file->l2p_stream, 0); + + /* Read the table sizes. Check the data for plausibility and + * consistency with other bits. */ + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->first_revision = (svn_revnum_t)value; + if (result->first_revision != rev_file->start_revision) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index rev / pack file revision numbers do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->page_size = (apr_uint32_t)value; + if (!result->page_size || (result->page_size & (result->page_size - 1))) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P index page size is not a power of two")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->revision_count = (int)value; + if ( result->revision_count != 1 + && result->revision_count != (apr_uint64_t)ffd->max_files_per_dir) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid number of revisions in L2P index")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + page_count = (apr_size_t)value; + if (page_count < result->revision_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Fewer L2P index pages than revisions")); + if (page_count > (rev_file->p2l_offset - rev_file->l2p_offset) / 2) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P index page count implausibly large")); + + next_rev = result->first_revision + (svn_revnum_t)result->revision_count; + if (result->first_revision > revision || next_rev <= revision) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Corrupt L2P index for r%ld only covers r%ld:%ld"), + revision, result->first_revision, next_rev); + + /* allocate the page tables */ + result->page_table + = apr_pcalloc(result_pool, page_count * sizeof(*result->page_table)); + result->page_table_index + = apr_pcalloc(result_pool, (result->revision_count + 1) + * sizeof(*result->page_table_index)); + + /* read per-revision page table sizes (i.e. number of pages per rev) */ + page_table_index = 0; + result->page_table_index[0] = page_table_index; + + for (i = 0; i < result->revision_count; ++i) + { + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value == 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Revision with no L2P index pages")); + + page_table_index += (apr_size_t)value; + if (page_table_index > page_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P page table exceeded")); + + result->page_table_index[i+1] = page_table_index; + } + + if (page_table_index != page_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Revisions do not cover the full L2P index page table")); + + /* read actual page tables */ + for (page = 0; page < page_count; ++page) + { + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value == 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Empty L2P index page")); + + result->page_table[page].size = (apr_uint32_t)value; + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value > result->page_size) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Page exceeds L2P index page size")); + + result->page_table[page].entry_count = (apr_uint32_t)value; + } + + /* correct the page description offsets */ + offset = packed_stream_offset(rev_file->l2p_stream); + for (page = 0; page < page_count; ++page) + { + result->page_table[page].offset = offset; + offset += result->page_table[page].size; + } + + /* return and cache the header */ + *header = result; + SVN_ERR(svn_cache__set(ffd->l2p_header_cache, &key, result, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Data structure that describes which l2p page info shall be extracted + * from the cache and contains the fields that receive the result. + */ +typedef struct l2p_page_info_baton_t +{ + /* input data: we want the page covering (REVISION,ITEM_INDEX) */ + svn_revnum_t revision; + apr_uint64_t item_index; + + /* out data */ + /* page location and size of the page within the l2p index file */ + l2p_page_table_entry_t entry; + + /* page number within the pages for REVISION (not l2p index global!) */ + apr_uint32_t page_no; + + /* offset of ITEM_INDEX within that page */ + apr_uint32_t page_offset; + + /* revision identifying the l2p index file, also the first rev in that */ + svn_revnum_t first_revision; +} l2p_page_info_baton_t; + + +/* Utility function that copies the info requested by BATON->REVISION and + * BATON->ITEM_INDEX and from HEADER and PAGE_TABLE into the output fields + * of *BATON. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_page_info_copy(l2p_page_info_baton_t *baton, + const l2p_header_t *header, + const l2p_page_table_entry_t *page_table, + const apr_size_t *page_table_index, + apr_pool_t *scratch_pool) +{ + /* revision offset within the index file */ + apr_size_t rel_revision = baton->revision - header->first_revision; + if (rel_revision >= header->revision_count) + return svn_error_createf(SVN_ERR_FS_INDEX_REVISION , NULL, + _("Revision %ld not covered by item index"), + baton->revision); + + /* select the relevant page */ + if (baton->item_index < header->page_size) + { + /* most revs fit well into a single page */ + baton->page_offset = (apr_uint32_t)baton->item_index; + baton->page_no = 0; + baton->entry = page_table[page_table_index[rel_revision]]; + } + else + { + const l2p_page_table_entry_t *first_entry; + const l2p_page_table_entry_t *last_entry; + apr_uint64_t max_item_index; + + /* range of pages for this rev */ + first_entry = page_table + page_table_index[rel_revision]; + last_entry = page_table + page_table_index[rel_revision + 1]; + + /* do we hit a valid index page? */ + max_item_index = (apr_uint64_t)header->page_size + * (last_entry - first_entry); + if (baton->item_index >= max_item_index) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s exceeds l2p limit " + "of %s for revision %ld"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + baton->item_index), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + max_item_index), + baton->revision); + + /* all pages are of the same size and full, except for the last one */ + baton->page_offset = (apr_uint32_t)(baton->item_index % header->page_size); + baton->page_no = (apr_uint32_t)(baton->item_index / header->page_size); + baton->entry = first_entry[baton->page_no]; + } + + baton->first_revision = header->first_revision; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_page_info_baton_t *BATON from l2p_header_t *DATA into the output + * fields in *BATON. + */ +static svn_error_t * +l2p_page_info_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve all pointer values of in-cache data */ + const l2p_header_t *header = data; + const l2p_page_table_entry_t *page_table + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table); + const apr_size_t *page_table_index + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table_index); + + /* copy the info */ + return l2p_page_info_copy(baton, header, page_table, page_table_index, + result_pool); +} + +/* Get the page info requested in *BATON from FS and set the output fields + * in *BATON. Use REV_FILE for on-disk file access. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_page_info(l2p_page_info_baton_t *baton, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + l2p_header_t *result; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* try to find the info in the cache */ + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + SVN_ERR(svn_cache__get_partial((void**)&dummy, &is_cached, + ffd->l2p_header_cache, &key, + l2p_page_info_access_func, baton, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* read from disk, cache and copy the result */ + SVN_ERR(get_l2p_header_body(&result, rev_file, fs, baton->revision, + scratch_pool, scratch_pool)); + SVN_ERR(l2p_page_info_copy(baton, result, result->page_table, + result->page_table_index, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Data request structure used by l2p_page_table_access_func. + */ +typedef struct l2p_page_table_baton_t +{ + /* revision for which to read the page table */ + svn_revnum_t revision; + + /* page table entries (of type l2p_page_table_entry_t). + * Must be created by caller and will be filled by callee. */ + apr_array_header_t *pages; +} l2p_page_table_baton_t; + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_page_baton_t *BATON from l2p_page_t *DATA into BATON->PAGES and *OUT. + */ +static svn_error_t * +l2p_page_table_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve in-cache pointers */ + l2p_page_table_baton_t *table_baton = baton; + const l2p_header_t *header = (const l2p_header_t *)data; + const l2p_page_table_entry_t *page_table + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table); + const apr_size_t *page_table_index + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table_index); + + /* copy the revision's page table into BATON */ + apr_size_t rel_revision = table_baton->revision - header->first_revision; + if (rel_revision < header->revision_count) + { + const l2p_page_table_entry_t *entry + = page_table + page_table_index[rel_revision]; + const l2p_page_table_entry_t *last_entry + = page_table + page_table_index[rel_revision + 1]; + + for (; entry < last_entry; ++entry) + APR_ARRAY_PUSH(table_baton->pages, l2p_page_table_entry_t) + = *entry; + } + + /* set output as a courtesy to the caller */ + *out = table_baton->pages; + + return SVN_NO_ERROR; +} + +/* Read the l2p index page table for REVISION in FS from cache and return + * it in PAGES. The later must be provided by the caller (and can be + * re-used); existing entries will be removed before writing the result. + * If the data cannot be found in the cache, the result will be empty + * (it never can be empty for a valid REVISION if the data is cached). + * Use the info from REV_FILE to determine pack / rev file properties. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_page_table(apr_array_header_t *pages, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + l2p_page_table_baton_t baton; + + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + apr_array_clear(pages); + baton.revision = revision; + baton.pages = pages; + SVN_ERR(svn_cache__get_partial((void**)&pages, &is_cached, + ffd->l2p_header_cache, &key, + l2p_page_table_access_func, &baton, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* From the log-to-phys index file starting at START_REVISION in FS, read + * the mapping page identified by TABLE_ENTRY and return it in *PAGE. + * Use REV_FILE to access on-disk files. + * Use RESULT_POOL for allocations. + */ +static svn_error_t * +get_l2p_page(l2p_page_t **page, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t start_revision, + l2p_page_table_entry_t *table_entry, + apr_pool_t *result_pool) +{ + apr_uint32_t i; + l2p_page_t *result = apr_pcalloc(result_pool, sizeof(*result)); + apr_uint64_t last_value = 0; + + /* open index file and select page */ + SVN_ERR(auto_open_l2p_index(rev_file, fs, start_revision)); + packed_stream_seek(rev_file->l2p_stream, table_entry->offset); + + /* initialize the page content */ + result->entry_count = table_entry->entry_count; + result->offsets = apr_pcalloc(result_pool, result->entry_count + * sizeof(*result->offsets)); + + /* read all page entries (offsets in rev file and container sub-items) */ + for (i = 0; i < result->entry_count; ++i) + { + apr_uint64_t value = 0; + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + last_value += decode_int(value); + result->offsets[i] = last_value - 1; + } + + /* After reading all page entries, the read cursor must have moved by + * TABLE_ENTRY->SIZE bytes. */ + if ( packed_stream_offset(rev_file->l2p_stream) + != table_entry->offset + table_entry->size) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P actual page size does not match page table value.")); + + *page = result; + + return SVN_NO_ERROR; +} + +/* Utility function. Read the l2p index pages for REVISION in FS from + * REV_FILE and put them into the cache. Skip page number EXLCUDED_PAGE_NO + * (use -1 for 'skip none') and pages outside the MIN_OFFSET, MAX_OFFSET + * range in the l2p index file. The index is being identified by + * FIRST_REVISION. PAGES is a scratch container provided by the caller. + * SCRATCH_POOL is used for temporary allocations. + * + * This function may be a no-op if the header cache lookup fails / misses. + */ +static svn_error_t * +prefetch_l2p_pages(svn_boolean_t *end, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t first_revision, + svn_revnum_t revision, + apr_array_header_t *pages, + int exlcuded_page_no, + apr_off_t min_offset, + apr_off_t max_offset, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + int i; + apr_pool_t *iterpool; + svn_fs_fs__page_cache_key_t key = { 0 }; + + /* Parameter check. */ + if (min_offset < 0) + min_offset = 0; + + if (max_offset <= 0) + { + /* Nothing to do */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* get the page table for REVISION from cache */ + *end = FALSE; + SVN_ERR(get_l2p_page_table(pages, fs, rev_file, revision, scratch_pool)); + if (pages->nelts == 0 || rev_file->l2p_stream == NULL) + { + /* not found -> we can't continue without hitting the disk again */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* prefetch pages individually until all are done or we found one in + * the cache */ + iterpool = svn_pool_create(scratch_pool); + assert(revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)revision; + key.is_packed = rev_file->is_packed; + + for (i = 0; i < pages->nelts && !*end; ++i) + { + svn_boolean_t is_cached; + + l2p_page_table_entry_t *entry + = &APR_ARRAY_IDX(pages, i, l2p_page_table_entry_t); + svn_pool_clear(iterpool); + + if (i == exlcuded_page_no) + continue; + + /* skip pages outside the specified index file range */ + if ( entry->offset < (apr_uint64_t)min_offset + || entry->offset + entry->size > (apr_uint64_t)max_offset) + { + *end = TRUE; + continue; + } + + /* page already in cache? */ + key.page = i; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->l2p_page_cache, + &key, iterpool)); + if (!is_cached) + { + /* no in cache -> read from stream (data already buffered in APR) + * and cache the result */ + l2p_page_t *page = NULL; + SVN_ERR(get_l2p_page(&page, rev_file, fs, first_revision, entry, + iterpool)); + + SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page, + iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Request data structure for l2p_entry_access_func. + */ +typedef struct l2p_entry_baton_t +{ + /* in data */ + /* revision. Used for error messages only */ + svn_revnum_t revision; + + /* item index to look up. Used for error messages only */ + apr_uint64_t item_index; + + /* offset within the cached page */ + apr_uint32_t page_offset; + + /* out data */ + /* absolute item or container offset in rev / pack file */ + apr_uint64_t offset; +} l2p_entry_baton_t; + +/* Return the rev / pack file offset of the item at BATON->PAGE_OFFSET in + * OFFSETS of PAGE and write it to *OFFSET. + */ +static svn_error_t * +l2p_page_get_entry(l2p_entry_baton_t *baton, + const l2p_page_t *page, + const apr_uint64_t *offsets, + apr_pool_t *scratch_pool) +{ + /* overflow check */ + if (page->entry_count <= baton->page_offset) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s" + " too large in revision %ld"), + apr_psprintf(scratch_pool, "%" APR_UINT64_T_FMT, + baton->item_index), + baton->revision); + + /* return the result */ + baton->offset = offsets[baton->page_offset]; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_entry_baton_t *BATON from l2p_page_t *DATA into BATON->OFFSET. + * *OUT remains unchanged. + */ +static svn_error_t * +l2p_entry_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve all in-cache pointers */ + const l2p_page_t *page = data; + const apr_uint64_t *offsets + = svn_temp_deserializer__ptr(page, (const void *const *)&page->offsets); + + /* return the requested data */ + return l2p_page_get_entry(baton, page, offsets, result_pool); +} + +/* Using the log-to-phys indexes in FS, find the absolute offset in the + * rev file for (REVISION, ITEM_INDEX) and return it in *OFFSET. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_index_lookup(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + l2p_page_info_baton_t info_baton; + l2p_entry_baton_t page_baton; + l2p_page_t *page = NULL; + svn_fs_fs__page_cache_key_t key = { 0 }; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* read index master data structure and extract the info required to + * access the l2p index page for (REVISION,ITEM_INDEX)*/ + info_baton.revision = revision; + info_baton.item_index = item_index; + SVN_ERR(get_l2p_page_info(&info_baton, rev_file, fs, scratch_pool)); + + /* try to find the page in the cache and get the OFFSET from it */ + page_baton.revision = revision; + page_baton.item_index = item_index; + page_baton.page_offset = info_baton.page_offset; + + assert(revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)revision; + key.is_packed = svn_fs_fs__is_packed_rev(fs, revision); + key.page = info_baton.page_no; + + SVN_ERR(svn_cache__get_partial(&dummy, &is_cached, + ffd->l2p_page_cache, &key, + l2p_entry_access_func, &page_baton, + scratch_pool)); + + if (!is_cached) + { + /* we need to read the info from disk (might already be in the + * APR file buffer, though) */ + apr_array_header_t *pages; + svn_revnum_t prefetch_revision; + svn_revnum_t last_revision + = info_baton.first_revision + + (key.is_packed ? ffd->max_files_per_dir : 1); + svn_boolean_t end; + apr_off_t max_offset + = APR_ALIGN(info_baton.entry.offset + info_baton.entry.size, + ffd->block_size); + apr_off_t min_offset = max_offset - ffd->block_size; + + /* read the relevant page */ + SVN_ERR(get_l2p_page(&page, rev_file, fs, info_baton.first_revision, + &info_baton.entry, scratch_pool)); + + /* cache the page and extract the result we need */ + SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page, scratch_pool)); + SVN_ERR(l2p_page_get_entry(&page_baton, page, page->offsets, + scratch_pool)); + + if (ffd->use_block_read) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* prefetch pages from following and preceding revisions */ + pages = apr_array_make(scratch_pool, 16, + sizeof(l2p_page_table_entry_t)); + end = FALSE; + for (prefetch_revision = revision; + prefetch_revision < last_revision && !end; + ++prefetch_revision) + { + int excluded_page_no = prefetch_revision == revision + ? info_baton.page_no + : -1; + svn_pool_clear(iterpool); + + SVN_ERR(prefetch_l2p_pages(&end, fs, rev_file, + info_baton.first_revision, + prefetch_revision, pages, + excluded_page_no, min_offset, + max_offset, iterpool)); + } + + end = FALSE; + for (prefetch_revision = revision-1; + prefetch_revision >= info_baton.first_revision && !end; + --prefetch_revision) + { + svn_pool_clear(iterpool); + + SVN_ERR(prefetch_l2p_pages(&end, fs, rev_file, + info_baton.first_revision, + prefetch_revision, pages, -1, + min_offset, max_offset, iterpool)); + } + + svn_pool_destroy(iterpool); + } + } + + *offset = page_baton.offset; + + return SVN_NO_ERROR; +} + +/* Using the log-to-phys proto index in transaction TXN_ID in FS, find the + * absolute offset in the proto rev file for the given ITEM_INDEX and return + * it in *OFFSET. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_proto_index_lookup(apr_off_t *offset, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + svn_boolean_t eof = FALSE; + apr_file_t *file = NULL; + SVN_ERR(svn_io_file_open(&file, + svn_fs_fs__path_l2p_proto_index(fs, txn_id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + /* process all entries until we fail due to EOF */ + *offset = -1; + while (!eof) + { + l2p_proto_entry_t entry; + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_l2p_entry_from_proto_index(file, &entry, &eof, + scratch_pool)); + + /* handle new revision */ + if (!eof && entry.item_index == item_index) + { + *offset = (apr_off_t)entry.offset - 1; + break; + } + } + + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the log-to-phys header info of the index covering REVISION from FS + * and return it in *HEADER. REV_FILE provides the pack / rev status. + * Allocate *HEADER in RESULT_POOL, use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +get_l2p_header(l2p_header_t **header, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + + /* first, try cache lookop */ + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->l2p_header_cache, + &key, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* read from disk and cache the result */ + SVN_ERR(get_l2p_header_body(header, rev_file, fs, revision, result_pool, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__l2p_get_max_ids(apr_array_header_t **max_ids, + svn_fs_t *fs, + svn_revnum_t start_rev, + apr_size_t count, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + l2p_header_t *header = NULL; + svn_revnum_t revision; + svn_revnum_t last_rev = (svn_revnum_t)(start_rev + count); + svn_fs_fs__revision_file_t *rev_file; + apr_pool_t *header_pool = svn_pool_create(scratch_pool); + + /* read index master data structure for the index covering START_REV */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start_rev, + header_pool, header_pool)); + SVN_ERR(get_l2p_header(&header, rev_file, fs, start_rev, header_pool, + header_pool)); + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + /* Determine the length of the item index list for each rev. + * Read new index headers as required. */ + *max_ids = apr_array_make(result_pool, (int)count, sizeof(apr_uint64_t)); + for (revision = start_rev; revision < last_rev; ++revision) + { + apr_uint64_t full_page_count; + apr_uint64_t item_count; + apr_size_t first_page_index, last_page_index; + + if (revision >= header->first_revision + header->revision_count) + { + /* need to read the next index. Clear up memory used for the + * previous one. Note that intermittent pack runs do not change + * the number of items in a revision, i.e. there is no consistency + * issue here. */ + svn_pool_clear(header_pool); + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, revision, + header_pool, header_pool)); + SVN_ERR(get_l2p_header(&header, rev_file, fs, revision, + header_pool, header_pool)); + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + } + + /* in a revision with N index pages, the first N-1 index pages are + * "full", i.e. contain HEADER->PAGE_SIZE entries */ + first_page_index + = header->page_table_index[revision - header->first_revision]; + last_page_index + = header->page_table_index[revision - header->first_revision + 1]; + full_page_count = last_page_index - first_page_index - 1; + item_count = full_page_count * header->page_size + + header->page_table[last_page_index - 1].entry_count; + + APR_ARRAY_PUSH(*max_ids, apr_uint64_t) = item_count; + } + + svn_pool_destroy(header_pool); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__item_offset(apr_off_t *absolute_position, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + const svn_fs_fs__id_part_t *txn_id, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = SVN_NO_ERROR; + if (txn_id) + { + if (svn_fs_fs__use_log_addressing(fs)) + { + /* the txn is going to produce a rev with logical addressing. + So, we need to get our info from the (proto) index file. */ + SVN_ERR(l2p_proto_index_lookup(absolute_position, fs, txn_id, + item_index, scratch_pool)); + } + else + { + /* for data in txns, item_index *is* the offset */ + *absolute_position = item_index; + } + } + else if (svn_fs_fs__use_log_addressing(fs)) + { + /* ordinary index lookup */ + SVN_ERR(l2p_index_lookup(absolute_position, fs, rev_file, revision, + item_index, scratch_pool)); + } + else if (rev_file->is_packed) + { + /* pack file with physical addressing */ + apr_off_t rev_offset; + SVN_ERR(svn_fs_fs__get_packed_offset(&rev_offset, fs, revision, + scratch_pool)); + *absolute_position = rev_offset + item_index; + } + else + { + /* for non-packed revs with physical addressing, + item_index *is* the offset */ + *absolute_position = item_index; + } + + return svn_error_trace(err); +} + +/* + * phys-to-log index + */ +svn_error_t * +svn_fs_fs__p2l_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool) +{ + SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE + | APR_CREATE | APR_APPEND | APR_BUFFERED, + APR_OS_DEFAULT, result_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_fs__p2l_proto_index_add_entry(apr_file_t *proto_index, + const svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + apr_uint64_t revision; + + /* Make sure all signed elements of ENTRY have non-negative values. + * + * For file offsets and sizes, this is a given as we use them to describe + * absolute positions and sizes. For revisions, SVN_INVALID_REVNUM is + * valid, hence we have to shift it by 1. + */ + SVN_ERR_ASSERT(entry->offset >= 0); + SVN_ERR_ASSERT(entry->size >= 0); + SVN_ERR_ASSERT( entry->item.revision >= 0 + || entry->item.revision == SVN_INVALID_REVNUM); + + revision = entry->item.revision == SVN_INVALID_REVNUM + ? 0 + : ((apr_uint64_t)entry->item.revision + 1); + + /* Now, all values will nicely convert to uint64. */ + /* Make sure to keep P2L_PROTO_INDEX_ENTRY_SIZE consistent with this: */ + + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->offset, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->size, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->type, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->fnv1_checksum, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, revision, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->item.number, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read *ENTRY from log-to-phys PROTO_INDEX file and indicate end-of-file + * in *EOF, or error out in that case if EOF is NULL. *ENTRY is in an + * undefined state if an end-of-file occurred. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +read_p2l_entry_from_proto_index(apr_file_t *proto_index, + svn_fs_fs__p2l_entry_t *entry, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_uint64_t revision; + + SVN_ERR(read_off_t_from_proto_index(proto_index, &entry->offset, + eof, scratch_pool)); + SVN_ERR(read_off_t_from_proto_index(proto_index, &entry->size, + eof, scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->type, + eof, scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->fnv1_checksum, + eof, scratch_pool)); + SVN_ERR(read_uint64_from_proto_index(proto_index, &revision, + eof, scratch_pool)); + SVN_ERR(read_uint64_from_proto_index(proto_index, &entry->item.number, + eof, scratch_pool)); + + /* Do the inverse REVSION number conversion (see + * svn_fs_fs__p2l_proto_index_add_entry), if we actually read a complete + * record. + */ + if (!eof || !*eof) + { + /* Be careful with the arithmetics here (overflows and wrap-around): */ + if (revision > 0 && revision - 1 > LONG_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("Revision 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + revision), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)LONG_MAX)); + + /* Shortening conversion from unsigned to signed int is well-defined + * and not lossy in C because the value can be represented in the + * target type. Also, cast to 'long' instead of 'svn_revnum_t' here + * to provoke a compiler warning if those types should differ and we + * would need to change the overflow checking logic. + */ + entry->item.revision = revision == 0 + ? SVN_INVALID_REVNUM + : (long)(revision - 1); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__p2l_proto_index_next_offset(apr_off_t *next_offset, + apr_file_t *proto_index, + apr_pool_t *scratch_pool) +{ + apr_off_t offset = 0; + + /* Empty index file? */ + SVN_ERR(svn_io_file_seek(proto_index, APR_END, &offset, scratch_pool)); + if (offset == 0) + { + *next_offset = 0; + } + else + { + /* At least one entry. Read last entry. */ + svn_fs_fs__p2l_entry_t entry; + offset -= P2L_PROTO_INDEX_ENTRY_SIZE; + + SVN_ERR(svn_io_file_seek(proto_index, APR_SET, &offset, scratch_pool)); + SVN_ERR(read_p2l_entry_from_proto_index(proto_index, &entry, + NULL, scratch_pool)); + + /* Return next offset. */ + *next_offset = entry.offset + entry.size; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_uint64_t page_size = ffd->p2l_page_size; + apr_file_t *proto_index = NULL; + svn_stream_t *stream; + int i; + svn_boolean_t eof = FALSE; + unsigned char encoded[ENCODED_INT_LENGTH]; + svn_revnum_t last_revision = revision; + apr_uint64_t last_compound = 0; + + apr_uint64_t last_entry_end = 0; + apr_uint64_t last_page_end = 0; + apr_uint64_t last_buffer_size = 0; /* byte offset in the spill buffer at + the begin of the current revision */ + apr_uint64_t file_size = 0; + + /* temporary data structures that collect the data which will be moved + to the target file in a second step */ + apr_pool_t *local_pool = svn_pool_create(scratch_pool); + apr_array_header_t *table_sizes + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + + /* 64k blocks, spill after 16MB */ + svn_spillbuf_t *buffer + = svn_spillbuf__create(0x10000, 0x1000000, local_pool); + + /* for loop temps ... */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* start at the beginning of the source file */ + SVN_ERR(svn_io_file_open(&proto_index, proto_file_name, + APR_READ | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, scratch_pool)); + + /* process all entries until we fail due to EOF */ + while (!eof) + { + svn_fs_fs__p2l_entry_t entry; + apr_uint64_t entry_end; + svn_boolean_t new_page = svn_spillbuf__get_size(buffer) == 0; + apr_uint64_t compound; + apr_int64_t rev_diff, compound_diff; + + svn_pool_clear(iterpool); + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_p2l_entry_from_proto_index(proto_index, &entry, + &eof, iterpool)); + + /* "unused" (and usually non-existent) section to cover the offsets + at the end the of the last page. */ + if (eof) + { + file_size = last_entry_end; + + entry.offset = last_entry_end; + entry.size = APR_ALIGN(entry.offset, page_size) - entry.offset; + entry.type = SVN_FS_FS__ITEM_TYPE_UNUSED; + entry.fnv1_checksum = 0; + entry.item.revision = last_revision; + entry.item.number = 0; + } + else + { + /* fix-up items created when the txn's target rev was unknown */ + if (entry.item.revision == SVN_INVALID_REVNUM) + entry.item.revision = revision; + } + + /* end pages if entry is extending beyond their boundaries */ + entry_end = entry.offset + entry.size; + while (entry_end - last_page_end > page_size) + { + apr_uint64_t buffer_size = svn_spillbuf__get_size(buffer); + APR_ARRAY_PUSH(table_sizes, apr_uint64_t) + = buffer_size - last_buffer_size; + + last_buffer_size = buffer_size; + last_page_end += page_size; + new_page = TRUE; + } + + /* this entry starts a new table -> store its offset + (all following entries in the same table will store sizes only) */ + if (new_page) + { + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.offset), + iterpool)); + last_revision = revision; + last_compound = 0; + } + + /* write simple item entry */ + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.size), + iterpool)); + + rev_diff = entry.item.revision - last_revision; + last_revision = entry.item.revision; + + compound = entry.item.number * 8 + entry.type; + compound_diff = compound - last_compound; + last_compound = compound; + + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_int(encoded, compound_diff), + iterpool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_int(encoded, rev_diff), + iterpool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.fnv1_checksum), + iterpool)); + + last_entry_end = entry_end; + } + + /* close the source file */ + SVN_ERR(svn_io_file_close(proto_index, local_pool)); + + /* store length of last table */ + APR_ARRAY_PUSH(table_sizes, apr_uint64_t) + = svn_spillbuf__get_size(buffer) - last_buffer_size; + + /* Open target stream. */ + stream = svn_stream_checksummed2(svn_stream_from_aprfile2(index_file, TRUE, + local_pool), + NULL, checksum, svn_checksum_md5, FALSE, + result_pool); + + /* write the start revision, file size and page size */ + SVN_ERR(svn_stream_puts(stream, P2L_STREAM_PREFIX)); + SVN_ERR(stream_write_encoded(stream, revision)); + SVN_ERR(stream_write_encoded(stream, file_size)); + SVN_ERR(stream_write_encoded(stream, page_size)); + + /* write the page table (actually, the sizes of each page description) */ + SVN_ERR(stream_write_encoded(stream, table_sizes->nelts)); + for (i = 0; i < table_sizes->nelts; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(table_sizes, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* append page contents and implicitly close STREAM */ + SVN_ERR(svn_stream_copy3(svn_stream__from_spillbuf(buffer, local_pool), + stream, NULL, NULL, local_pool)); + + svn_pool_destroy(iterpool); + svn_pool_destroy(local_pool); + + return SVN_NO_ERROR; +} + +/* If REV_FILE->P2L_STREAM is NULL, create a new stream for the phys-to-log + * index for REVISION in FS using the rev / pack file provided by REV_FILE. + */ +static svn_error_t * +auto_open_p2l_index(svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision) +{ + if (rev_file->p2l_stream == NULL) + { + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + SVN_ERR(packed_stream_open(&rev_file->p2l_stream, + rev_file->file, + rev_file->p2l_offset, + rev_file->footer_offset, + P2L_STREAM_PREFIX, + (apr_size_t)ffd->block_size, + rev_file->pool, + rev_file->pool)); + } + + return SVN_NO_ERROR; +} + + +/* Read the header data structure of the phys-to-log index for REVISION in + * FS and return it in *HEADER, allocated in RESULT_POOL. Use REV_FILE to + * access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_p2l_header(p2l_header_t **header, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_uint64_t value; + apr_size_t i; + apr_off_t offset; + p2l_header_t *result; + svn_boolean_t is_cached = FALSE; + + /* look for the header data in our cache */ + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->p2l_header_cache, + &key, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* not found -> must read it from disk. + * Open index file or position read pointer to the begin of the file */ + if (rev_file->p2l_stream == NULL) + SVN_ERR(auto_open_p2l_index(rev_file, fs, rev_file->start_revision)); + else + packed_stream_seek(rev_file->p2l_stream, 0); + + /* allocate result data structure */ + result = apr_pcalloc(result_pool, sizeof(*result)); + + /* Read table sizes, check them for plausibility and allocate page array. */ + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->first_revision = (svn_revnum_t)value; + if (result->first_revision != rev_file->start_revision) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index rev / pack file revision numbers do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->file_size = value; + if (result->file_size != (apr_uint64_t)rev_file->l2p_offset) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index offset and rev / pack file size do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->page_size = value; + if (!result->page_size || (result->page_size & (result->page_size - 1))) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L index page size is not a power of two")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->page_count = (apr_size_t)value; + if (result->page_count != (result->file_size - 1) / result->page_size + 1) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L page count does not match rev / pack file size")); + + result->offsets + = apr_pcalloc(result_pool, (result->page_count + 1) * sizeof(*result->offsets)); + + /* read page sizes and derive page description offsets from them */ + result->offsets[0] = 0; + for (i = 0; i < result->page_count; ++i) + { + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->offsets[i+1] = result->offsets[i] + (apr_off_t)value; + } + + /* correct the offset values */ + offset = packed_stream_offset(rev_file->p2l_stream); + for (i = 0; i <= result->page_count; ++i) + result->offsets[i] += offset; + + /* cache the header data */ + SVN_ERR(svn_cache__set(ffd->p2l_header_cache, &key, result, scratch_pool)); + + /* return the result */ + *header = result; + + return SVN_NO_ERROR; +} + +/* Data structure that describes which p2l page info shall be extracted + * from the cache and contains the fields that receive the result. + */ +typedef struct p2l_page_info_baton_t +{ + /* input variables */ + /* revision identifying the index file */ + svn_revnum_t revision; + + /* offset within the page in rev / pack file */ + apr_off_t offset; + + /* output variables */ + /* page containing OFFSET */ + apr_size_t page_no; + + /* first revision in this p2l index */ + svn_revnum_t first_revision; + + /* offset within the p2l index file describing this page */ + apr_off_t start_offset; + + /* offset within the p2l index file describing the following page */ + apr_off_t next_offset; + + /* PAGE_NO * PAGE_SIZE (if <= OFFSET) */ + apr_off_t page_start; + + /* total number of pages indexed */ + apr_size_t page_count; + + /* size of each page in pack / rev file */ + apr_uint64_t page_size; +} p2l_page_info_baton_t; + +/* From HEADER and the list of all OFFSETS, fill BATON with the page info + * requested by BATON->OFFSET. + */ +static void +p2l_page_info_copy(p2l_page_info_baton_t *baton, + const p2l_header_t *header, + const apr_off_t *offsets) +{ + /* if the requested offset is out of bounds, return info for + * a zero-sized empty page right behind the last page. + */ + if (baton->offset / header->page_size < header->page_count) + { + /* This cast is safe because the value is < header->page_count. */ + baton->page_no = (apr_size_t)(baton->offset / header->page_size); + baton->start_offset = offsets[baton->page_no]; + baton->next_offset = offsets[baton->page_no + 1]; + baton->page_size = header->page_size; + } + else + { + /* Beyond the last page. */ + baton->page_no = header->page_count; + baton->start_offset = offsets[baton->page_no]; + baton->next_offset = offsets[baton->page_no]; + baton->page_size = 0; + } + + baton->first_revision = header->first_revision; + baton->page_start = (apr_off_t)(header->page_size * baton->page_no); + baton->page_count = header->page_count; +} + +/* Implement svn_cache__partial_getter_func_t: extract the p2l page info + * requested by BATON and return it in BATON. + */ +static svn_error_t * +p2l_page_info_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* all the pointers to cached data we need */ + const p2l_header_t *header = data; + const apr_off_t *offsets + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->offsets); + + /* copy data from cache to BATON */ + p2l_page_info_copy(baton, header, offsets); + return SVN_NO_ERROR; +} + +/* Read the header data structure of the phys-to-log index for revision + * BATON->REVISION in FS. Return in *BATON all info relevant to read the + * index page for the rev / pack file offset BATON->OFFSET. Use REV_FILE + * to access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_p2l_page_info(p2l_page_info_baton_t *baton, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + p2l_header_t *header; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* look for the header data in our cache */ + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(svn_cache__get_partial(&dummy, &is_cached, ffd->p2l_header_cache, + &key, p2l_page_info_func, baton, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + SVN_ERR(get_p2l_header(&header, rev_file, fs, baton->revision, + scratch_pool, scratch_pool)); + + /* copy the requested info into *BATON */ + p2l_page_info_copy(baton, header, header->offsets); + + return SVN_NO_ERROR; +} + +/* Read a mapping entry from the phys-to-log index STREAM and append it to + * RESULT. *ITEM_INDEX contains the phys offset for the entry and will + * be moved forward by the size of entry. + */ +static svn_error_t * +read_entry(svn_fs_fs__packed_number_stream_t *stream, + apr_off_t *item_offset, + svn_revnum_t *last_revision, + apr_uint64_t *last_compound, + apr_array_header_t *result) +{ + apr_uint64_t value; + + svn_fs_fs__p2l_entry_t entry; + + entry.offset = *item_offset; + SVN_ERR(packed_stream_get(&value, stream)); + entry.size = (apr_off_t)value; + + SVN_ERR(packed_stream_get(&value, stream)); + *last_compound += decode_int(value); + + entry.type = *last_compound & 7; + entry.item.number = *last_compound / 8; + + /* Verify item type. */ + if (entry.type > SVN_FS_FS__ITEM_TYPE_CHANGES) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid item type in P2L index")); + if ( entry.type == SVN_FS_FS__ITEM_TYPE_CHANGES + && entry.item.number != SVN_FS_FS__ITEM_INDEX_CHANGES) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Changed path list must have item number 1")); + + SVN_ERR(packed_stream_get(&value, stream)); + *last_revision += (svn_revnum_t)decode_int(value); + entry.item.revision = *last_revision; + + SVN_ERR(packed_stream_get(&value, stream)); + entry.fnv1_checksum = (apr_uint32_t)value; + + /* Truncating the checksum to 32 bits may have hidden random data in the + * unused extra bits of the on-disk representation (7/8 bit representation + * uses 5 bytes on disk for the 32 bit value, leaving 3 bits unused). */ + if (value > APR_UINT32_MAX) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid FNV1 checksum in P2L index")); + + /* Some of the index data for empty rev / pack file sections will not be + * used during normal operation. Thus, we have strict rules for the + * contents of those unused fields. */ + if (entry.type == SVN_FS_FS__ITEM_TYPE_UNUSED) + if ( entry.item.number != SVN_FS_FS__ITEM_INDEX_UNUSED + || entry.fnv1_checksum != 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Empty regions must have item number 0 and checksum 0")); + + APR_ARRAY_PUSH(result, svn_fs_fs__p2l_entry_t) = entry; + *item_offset += entry.size; + + return SVN_NO_ERROR; +} + +/* Read the phys-to-log mappings for the cluster beginning at rev file + * offset PAGE_START from the index for START_REVISION in FS. The data + * can be found in the index page beginning at START_OFFSET with the next + * page beginning at NEXT_OFFSET. PAGE_SIZE is the L2P index page size. + * Return the relevant index entries in *ENTRIES. Use REV_FILE to access + * on-disk data. Allocate *ENTRIES in RESULT_POOL. + */ +static svn_error_t * +get_p2l_page(apr_array_header_t **entries, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t start_revision, + apr_off_t start_offset, + apr_off_t next_offset, + apr_off_t page_start, + apr_uint64_t page_size, + apr_pool_t *result_pool) +{ + apr_uint64_t value; + apr_array_header_t *result + = apr_array_make(result_pool, 16, sizeof(svn_fs_fs__p2l_entry_t)); + apr_off_t item_offset; + apr_off_t offset; + svn_revnum_t last_revision; + apr_uint64_t last_compound; + + /* open index and navigate to page start */ + SVN_ERR(auto_open_p2l_index(rev_file, fs, start_revision)); + packed_stream_seek(rev_file->p2l_stream, start_offset); + + /* read rev file offset of the first page entry (all page entries will + * only store their sizes). */ + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + item_offset = (apr_off_t)value; + + /* read all entries of this page */ + last_revision = start_revision; + last_compound = 0; + + /* Special case: empty pages. */ + if (start_offset == next_offset) + { + /* Empty page. This only happens if the first entry of the next page + * also covers this page (and possibly more) completely. */ + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, + &last_revision, &last_compound, result)); + } + else + { + /* Read non-empty page. */ + do + { + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, + &last_revision, &last_compound, result)); + offset = packed_stream_offset(rev_file->p2l_stream); + } + while (offset < next_offset); + + /* We should now be exactly at the next offset, i.e. the numbers in + * the stream cannot overlap into the next page description. */ + if (offset != next_offset) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L page description overlaps with next page description")); + + /* if we haven't covered the cluster end yet, we must read the first + * entry of the next page */ + if (item_offset < page_start + page_size) + { + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + item_offset = (apr_off_t)value; + last_revision = start_revision; + last_compound = 0; + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, + &last_revision, &last_compound, result)); + } + } + + *entries = result; + + return SVN_NO_ERROR; +} + +/* If it cannot be found in FS's caches, read the p2l index page selected + * by BATON->OFFSET from REV_FILE. Don't read the page if it precedes + * MIN_OFFSET. Set *END to TRUE if the caller should stop refeching. + * + * *BATON will be updated with the selected page's info and SCRATCH_POOL + * will be used for temporary allocations. If the data is alread in the + * cache, descrease *LEAKING_BUCKET and increase it otherwise. With that + * pattern we will still read all pages from the block even if some of + * them survived in the cached. + */ +static svn_error_t * +prefetch_p2l_page(svn_boolean_t *end, + int *leaking_bucket, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + p2l_page_info_baton_t *baton, + apr_off_t min_offset, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t already_cached; + apr_array_header_t *page; + svn_fs_fs__page_cache_key_t key = { 0 }; + + /* fetch the page info */ + *end = FALSE; + baton->revision = baton->first_revision; + SVN_ERR(get_p2l_page_info(baton, rev_file, fs, scratch_pool)); + if (baton->start_offset < min_offset || !rev_file->p2l_stream) + { + /* page outside limits -> stop prefetching */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* do we have that page in our caches already? */ + assert(baton->first_revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)baton->first_revision; + key.is_packed = svn_fs_fs__is_packed_rev(fs, baton->first_revision); + key.page = baton->page_no; + SVN_ERR(svn_cache__has_key(&already_cached, ffd->p2l_page_cache, + &key, scratch_pool)); + + /* yes, already cached */ + if (already_cached) + { + /* stop prefetching if most pages are already cached. */ + if (!--*leaking_bucket) + *end = TRUE; + + return SVN_NO_ERROR; + } + + ++*leaking_bucket; + + /* read from disk */ + SVN_ERR(get_p2l_page(&page, rev_file, fs, + baton->first_revision, + baton->start_offset, + baton->next_offset, + baton->page_start, + baton->page_size, + scratch_pool)); + + /* and put it into our cache */ + SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Lookup & construct the baton and key information that we will need for + * a P2L page cache lookup. We want the page covering OFFSET in the rev / + * pack file containing REVSION in FS. Return the results in *PAGE_INFO_P + * and *KEY_P. Read data through REV_FILE. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +get_p2l_keys(p2l_page_info_baton_t *page_info_p, + svn_fs_fs__page_cache_key_t *key_p, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *scratch_pool) +{ + p2l_page_info_baton_t page_info; + + /* request info for the index pages that describes the pack / rev file + * contents at pack / rev file position OFFSET. */ + page_info.offset = offset; + page_info.revision = revision; + SVN_ERR(get_p2l_page_info(&page_info, rev_file, fs, scratch_pool)); + + /* if the offset refers to a non-existent page, bail out */ + if (page_info.page_count <= page_info.page_no) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Offset %s too large in revision %ld"), + apr_off_t_toa(scratch_pool, offset), revision); + + /* return results */ + if (page_info_p) + *page_info_p = page_info; + + /* construct cache key */ + if (key_p) + { + svn_fs_fs__page_cache_key_t key = { 0 }; + assert(page_info.first_revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)page_info.first_revision; + key.is_packed = rev_file->is_packed; + key.page = page_info.page_no; + + *key_p = key; + } + + return SVN_NO_ERROR; +} + +/* qsort-compatible compare function that compares the OFFSET of the + * svn_fs_fs__p2l_entry_t in *LHS with the apr_off_t in *RHS. */ +static int +compare_start_p2l_entry(const void *lhs, + const void *rhs) +{ + const svn_fs_fs__p2l_entry_t *entry = lhs; + apr_off_t start = *(const apr_off_t*)rhs; + apr_off_t diff = entry->offset - start; + + /* restrict result to int */ + return diff < 0 ? -1 : (diff == 0 ? 0 : 1); +} + +/* From the PAGE_ENTRIES array of svn_fs_fs__p2l_entry_t, ordered + * by their OFFSET member, copy all elements overlapping the range + * [BLOCK_START, BLOCK_END) to ENTRIES. */ +static void +append_p2l_entries(apr_array_header_t *entries, + apr_array_header_t *page_entries, + apr_off_t block_start, + apr_off_t block_end) +{ + const svn_fs_fs__p2l_entry_t *entry; + int idx = svn_sort__bsearch_lower_bound(page_entries, &block_start, + compare_start_p2l_entry); + + /* start at the first entry that overlaps with BLOCK_START */ + if (idx > 0) + { + entry = &APR_ARRAY_IDX(page_entries, idx - 1, svn_fs_fs__p2l_entry_t); + if (entry->offset + entry->size > block_start) + --idx; + } + + /* copy all entries covering the requested range */ + for ( ; idx < page_entries->nelts; ++idx) + { + entry = &APR_ARRAY_IDX(page_entries, idx, svn_fs_fs__p2l_entry_t); + if (entry->offset >= block_end) + break; + + APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t) = *entry; + } +} + +/* Auxilliary struct passed to p2l_entries_func selecting the relevant + * data range. */ +typedef struct p2l_entries_baton_t +{ + apr_off_t start; + apr_off_t end; +} p2l_entries_baton_t; + +/* Implement svn_cache__partial_getter_func_t: extract p2l entries from + * the page in DATA which overlap the p2l_entries_baton_t in BATON. + * The target array is already provided in *OUT. + */ +static svn_error_t * +p2l_entries_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + apr_array_header_t *entries = *(apr_array_header_t **)out; + const apr_array_header_t *raw_page = data; + p2l_entries_baton_t *block = baton; + + /* Make PAGE a readable APR array. */ + apr_array_header_t page = *raw_page; + page.elts = (void *)svn_temp_deserializer__ptr(raw_page, + (const void * const *)&raw_page->elts); + + /* append relevant information to result */ + append_p2l_entries(entries, &page, block->start, block->end); + + return SVN_NO_ERROR; +} + + +/* Body of svn_fs_fs__p2l_index_lookup. However, do a single index page + * lookup and append the result to the ENTRIES array provided by the caller. + * Use successive calls to cover larger ranges. + */ +static svn_error_t * +p2l_index_lookup(apr_array_header_t *entries, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_end, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_fs_fs__page_cache_key_t key; + svn_boolean_t is_cached = FALSE; + p2l_page_info_baton_t page_info; + apr_array_header_t *local_result = entries; + + /* baton selecting the relevant entries from the one page we access */ + p2l_entries_baton_t block; + block.start = block_start; + block.end = block_end; + + /* if we requested an empty range, the result would be empty */ + SVN_ERR_ASSERT(block_start < block_end); + + /* look for the fist page of the range in our cache */ + SVN_ERR(get_p2l_keys(&page_info, &key, rev_file, fs, revision, block_start, + scratch_pool)); + SVN_ERR(svn_cache__get_partial((void**)&local_result, &is_cached, + ffd->p2l_page_cache, &key, p2l_entries_func, + &block, scratch_pool)); + + if (!is_cached) + { + svn_boolean_t end; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_off_t original_page_start = page_info.page_start; + int leaking_bucket = 4; + p2l_page_info_baton_t prefetch_info = page_info; + apr_array_header_t *page_entries; + + apr_off_t max_offset + = APR_ALIGN(page_info.next_offset, ffd->block_size); + apr_off_t min_offset + = APR_ALIGN(page_info.start_offset, ffd->block_size) - ffd->block_size; + + /* Since we read index data in larger chunks, we probably got more + * page data than we requested. Parse & cache that until either we + * encounter pages already cached or reach the end of the buffer. + */ + + /* pre-fetch preceding pages */ + if (ffd->use_block_read) + { + end = FALSE; + prefetch_info.offset = original_page_start; + while (prefetch_info.offset >= prefetch_info.page_size && !end) + { + svn_pool_clear(iterpool); + + prefetch_info.offset -= prefetch_info.page_size; + SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, rev_file, + &prefetch_info, min_offset, + iterpool)); + } + } + + /* fetch page from disk and put it into the cache */ + SVN_ERR(get_p2l_page(&page_entries, rev_file, fs, + page_info.first_revision, + page_info.start_offset, + page_info.next_offset, + page_info.page_start, + page_info.page_size, iterpool)); + + /* The last cache entry must not end beyond the range covered by + * this index. The same applies for any subset of entries. */ + if (page_entries->nelts) + { + const svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(page_entries, page_entries->nelts - 1, + svn_fs_fs__p2l_entry_t); + if ( entry->offset + entry->size + > page_info.page_size * page_info.page_count) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Last P2L index entry extends beyond " + "the last page in revision %ld."), + revision); + } + + SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page_entries, + iterpool)); + + /* append relevant information to result */ + append_p2l_entries(entries, page_entries, block_start, block_end); + + /* pre-fetch following pages */ + if (ffd->use_block_read) + { + end = FALSE; + leaking_bucket = 4; + prefetch_info = page_info; + prefetch_info.offset = original_page_start; + while ( prefetch_info.next_offset < max_offset + && prefetch_info.page_no + 1 < prefetch_info.page_count + && !end) + { + svn_pool_clear(iterpool); + + prefetch_info.offset += prefetch_info.page_size; + SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, rev_file, + &prefetch_info, min_offset, + iterpool)); + } + } + + svn_pool_destroy(iterpool); + } + + /* We access a valid page (otherwise, we had seen an error in the + * get_p2l_keys request). Hence, at least one entry must be found. */ + SVN_ERR_ASSERT(entries->nelts > 0); + + /* Add an "unused" entry if it extends beyond the end of the data file. + * Since the index page size might be smaller than the current data + * read block size, the trailing "unused" entry in this index may not + * fully cover the end of the last block. */ + if (page_info.page_no + 1 >= page_info.page_count) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_fs__p2l_entry_t); + + apr_off_t entry_end = entry->offset + entry->size; + if (entry_end < block_end) + { + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + { + /* extend the terminal filler */ + entry->size = block_end - entry->offset; + } + else + { + /* No terminal filler. Add one. */ + entry = apr_array_push(entries); + entry->offset = entry_end; + entry->size = block_end - entry_end; + entry->type = SVN_FS_FS__ITEM_TYPE_UNUSED; + entry->fnv1_checksum = 0; + entry->item.revision = SVN_INVALID_REVNUM; + entry->item.number = SVN_FS_FS__ITEM_INDEX_UNUSED; + } + } + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__p2l_index_lookup(apr_array_header_t **entries, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_off_t block_end = block_start + block_size; + + /* the receiving container */ + int last_count = 0; + apr_array_header_t *result = apr_array_make(result_pool, 16, + sizeof(svn_fs_fs__p2l_entry_t)); + + /* Fetch entries page-by-page. Since the p2l index is supposed to cover + * every single byte in the rev / pack file - even unused sections - + * every iteration must result in some progress. */ + while (block_start < block_end) + { + svn_fs_fs__p2l_entry_t *entry; + SVN_ERR(p2l_index_lookup(result, rev_file, fs, revision, block_start, + block_end, scratch_pool)); + SVN_ERR_ASSERT(result->nelts > 0); + + /* continue directly behind last item */ + entry = &APR_ARRAY_IDX(result, result->nelts-1, svn_fs_fs__p2l_entry_t); + block_start = entry->offset + entry->size; + + /* Some paranoia check. Successive iterations should never return + * duplicates but if it did, we might get into trouble later on. */ + if (last_count > 0 && last_count < result->nelts) + { + entry = &APR_ARRAY_IDX(result, last_count - 1, + svn_fs_fs__p2l_entry_t); + SVN_ERR_ASSERT(APR_ARRAY_IDX(result, last_count, + svn_fs_fs__p2l_entry_t).offset + >= entry->offset + entry->size); + } + + last_count = result->nelts; + } + + *entries = result; + return SVN_NO_ERROR; +} + +/* compare_fn_t comparing a svn_fs_fs__p2l_entry_t at LHS with an offset + * RHS. + */ +static int +compare_p2l_entry_offsets(const void *lhs, const void *rhs) +{ + const svn_fs_fs__p2l_entry_t *entry = (const svn_fs_fs__p2l_entry_t *)lhs; + apr_off_t offset = *(const apr_off_t *)rhs; + + return entry->offset < offset ? -1 : (entry->offset == offset ? 0 : 1); +} + +/* Cached data extraction utility. DATA is a P2L index page, e.g. an APR + * array of svn_fs_fs__p2l_entry_t elements. Return the entry for the item, + * allocated in RESULT_POOL, starting at OFFSET or NULL if that's not an + * the start offset of any item. Use SCRATCH_POOL for temporary allocations. + */ +static svn_fs_fs__p2l_entry_t * +get_p2l_entry_from_cached_page(const void *data, + apr_uint64_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* resolve all pointer values of in-cache data */ + const apr_array_header_t *page = data; + apr_array_header_t *entries = apr_pmemdup(scratch_pool, page, + sizeof(*page)); + svn_fs_fs__p2l_entry_t *entry; + + entries->elts = (char *)svn_temp_deserializer__ptr(page, + (const void *const *)&page->elts); + + /* search of the offset we want */ + entry = svn_sort__array_lookup(entries, &offset, NULL, + (int (*)(const void *, const void *))compare_p2l_entry_offsets); + + /* return it, if it is a perfect match */ + return entry ? apr_pmemdup(result_pool, entry, sizeof(*entry)) : NULL; +} + +/* Implements svn_cache__partial_getter_func_t for P2L index pages, copying + * the entry for the apr_off_t at BATON into *OUT. *OUT will be NULL if + * there is no matching entry in the index page at DATA. + */ +static svn_error_t * +p2l_entry_lookup_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + svn_fs_fs__p2l_entry_t *entry + = get_p2l_entry_from_cached_page(data, *(apr_off_t *)baton, result_pool, + result_pool); + + *out = entry && entry->offset == *(apr_off_t *)baton + ? apr_pmemdup(result_pool, entry, sizeof(*entry)) + : NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry_p, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_fs_fs__page_cache_key_t key = { 0 }; + svn_boolean_t is_cached = FALSE; + p2l_page_info_baton_t page_info; + + *entry_p = NULL; + + /* look for this info in our cache */ + SVN_ERR(get_p2l_keys(&page_info, &key, rev_file, fs, revision, offset, + scratch_pool)); + SVN_ERR(svn_cache__get_partial((void**)entry_p, &is_cached, + ffd->p2l_page_cache, &key, + p2l_entry_lookup_func, &offset, + result_pool)); + if (!is_cached) + { + /* do a standard index lookup. This is will automatically prefetch + * data to speed up future lookups. */ + apr_array_header_t *entries = apr_array_make(result_pool, 1, + sizeof(**entry_p)); + SVN_ERR(p2l_index_lookup(entries, rev_file, fs, revision, offset, + offset + 1, scratch_pool)); + + /* Find the entry that we want. */ + *entry_p = svn_sort__array_lookup(entries, &offset, NULL, + (int (*)(const void *, const void *))compare_p2l_entry_offsets); + } + + return SVN_NO_ERROR; +} + +/* Implements svn_cache__partial_getter_func_t for P2L headers, setting *OUT + * to the largest the first offset not covered by this P2L index. + */ +static svn_error_t * +p2l_get_max_offset_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + const p2l_header_t *header = data; + apr_off_t max_offset = header->file_size; + *out = apr_pmemdup(result_pool, &max_offset, sizeof(max_offset)); + + return SVN_NO_ERROR; +} + +/* Core functionality of to svn_fs_fs__p2l_get_max_offset with identical + * signature. */ +static svn_error_t * +p2l_get_max_offset(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + p2l_header_t *header; + svn_boolean_t is_cached = FALSE; + apr_off_t *offset_p; + + /* look for the header data in our cache */ + pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(svn_cache__get_partial((void **)&offset_p, &is_cached, + ffd->p2l_header_cache, &key, + p2l_get_max_offset_func, NULL, + scratch_pool)); + if (is_cached) + { + *offset = *offset_p; + return SVN_NO_ERROR; + } + + SVN_ERR(get_p2l_header(&header, rev_file, fs, revision, scratch_pool, + scratch_pool)); + *offset = header->file_size; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__p2l_get_max_offset(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(p2l_get_max_offset(offset, fs, rev_file, revision, + scratch_pool)); +} + +/* Calculate the FNV1 checksum over the offset range in REV_FILE, covered by + * ENTRY. Store the result in ENTRY->FNV1_CHECKSUM. Use SCRATCH_POOL for + * temporary allocations. */ +static svn_error_t * +calc_fnv1(svn_fs_fs__p2l_entry_t *entry, + svn_fs_fs__revision_file_t *rev_file, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[4096]; + svn_checksum_t *checksum; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, scratch_pool); + apr_off_t size = entry->size; + + /* Special rules apply to unused sections / items. The data must be a + * sequence of NUL bytes (not checked here) and the checksum is fixed to 0. + */ + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + { + entry->fnv1_checksum = 0; + return SVN_NO_ERROR; + } + + /* Read the block and feed it to the checksum calculator. */ + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &entry->offset, + scratch_pool)); + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(rev_file->file, buffer, to_read, NULL, + NULL, scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + } + + /* Store final checksum in ENTRY. */ + SVN_ERR(svn_checksum_final(&checksum, context, scratch_pool)); + entry->fnv1_checksum = ntohl(*(const apr_uint32_t *)checksum->digest); + + return SVN_NO_ERROR; +} + +/* + * Index (re-)creation utilities. + */ + +svn_error_t * +svn_fs_fs__p2l_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *proto_index; + + /* Use a subpool for immediate temp file cleanup at the end of this + * function. */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + /* Create a proto-index file. */ + SVN_ERR(svn_io_open_unique_file3(NULL, protoname, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_open(&proto_index, *protoname, + scratch_pool)); + + /* Write ENTRIES to proto-index file and calculate checksums as we go. */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t *); + svn_pool_clear(iterpool); + + SVN_ERR(calc_fnv1(entry, rev_file, iterpool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(proto_index, entry, + iterpool)); + } + + /* Convert proto-index into final index and move it into position. + * Note that REV_FILE contains the start revision of the shard file if it + * has been packed while REVISION may be somewhere in the middle. For + * non-packed shards, they will have identical values. */ + SVN_ERR(svn_io_file_close(proto_index, iterpool)); + + /* Temp file cleanup. */ + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* A svn_sort__array compatible comparator function, sorting the + * svn_fs_fs__p2l_entry_t** given in LHS, RHS by revision. */ +static int +compare_p2l_entry_revision(const void *lhs, + const void *rhs) +{ + const svn_fs_fs__p2l_entry_t *lhs_entry + =*(const svn_fs_fs__p2l_entry_t **)lhs; + const svn_fs_fs__p2l_entry_t *rhs_entry + =*(const svn_fs_fs__p2l_entry_t **)rhs; + + if (lhs_entry->item.revision < rhs_entry->item.revision) + return -1; + + return lhs_entry->item.revision == rhs_entry->item.revision ? 0 : 1; +} + +svn_error_t * +svn_fs_fs__l2p_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *proto_index; + + /* Use a subpool for immediate temp file cleanup at the end of this + * function. */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + svn_revnum_t last_revision = SVN_INVALID_REVNUM; + svn_revnum_t revision = SVN_INVALID_REVNUM; + + /* L2P index must be written in revision order. + * Sort ENTRIES accordingly. */ + svn_sort__array(entries, compare_p2l_entry_revision); + + /* Find the first revision in the index + * (must exist since no truly empty revs are allowed). */ + for (i = 0; i < entries->nelts && !SVN_IS_VALID_REVNUM(revision); ++i) + revision = APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t *) + ->item.revision; + + /* Create the temporary proto-rev file. */ + SVN_ERR(svn_io_open_unique_file3(NULL, protoname, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(svn_fs_fs__l2p_proto_index_open(&proto_index, *protoname, + scratch_pool)); + + /* Write all entries. */ + for (i = 0; i < entries->nelts; ++i) + { + const svn_fs_fs__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t *); + svn_pool_clear(iterpool); + + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + continue; + + if (last_revision != entry->item.revision) + { + SVN_ERR(svn_fs_fs__l2p_proto_index_add_revision(proto_index, + scratch_pool)); + last_revision = entry->item.revision; + } + + SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry(proto_index, + entry->offset, + entry->item.number, + iterpool)); + } + + /* Convert proto-index into final index and move it into position. */ + SVN_ERR(svn_io_file_close(proto_index, iterpool)); + + /* Temp file cleanup. */ + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* + * Standard (de-)serialization functions + */ + +svn_error_t * +svn_fs_fs__serialize_l2p_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + l2p_header_t *header = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t page_count = header->page_table_index[header->revision_count]; + apr_size_t page_table_size = page_count * sizeof(*header->page_table); + apr_size_t index_size + = (header->revision_count + 1) * sizeof(*header->page_table_index); + apr_size_t data_size = sizeof(*header) + index_size + page_table_size; + + /* serialize header and all its elements */ + context = svn_temp_serializer__init(header, + sizeof(*header), + data_size + 32, + pool); + + /* page table index array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->page_table_index, + index_size); + + /* page table array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->page_table, + page_table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_l2p_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + l2p_header_t *header = (l2p_header_t *)data; + + /* resolve the pointers in the struct */ + svn_temp_deserializer__resolve(header, (void**)&header->page_table_index); + svn_temp_deserializer__resolve(header, (void**)&header->page_table); + + /* done */ + *out = header; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__serialize_l2p_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + l2p_page_t *page = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t of_table_size = page->entry_count * sizeof(*page->offsets); + + /* serialize struct and all its elements */ + context = svn_temp_serializer__init(page, + sizeof(*page), + of_table_size + sizeof(*page) + 32, + pool); + + /* offsets and sub_items arrays */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&page->offsets, + of_table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_l2p_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + l2p_page_t *page = data; + + /* resolve the pointers in the struct */ + svn_temp_deserializer__resolve(page, (void**)&page->offsets); + + /* done */ + *out = page; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__serialize_p2l_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + p2l_header_t *header = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t table_size = (header->page_count + 1) * sizeof(*header->offsets); + + /* serialize header and all its elements */ + context = svn_temp_serializer__init(header, + sizeof(*header), + table_size + sizeof(*header) + 32, + pool); + + /* offsets array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->offsets, + table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_p2l_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + p2l_header_t *header = data; + + /* resolve the only pointer in the struct */ + svn_temp_deserializer__resolve(header, (void**)&header->offsets); + + /* done */ + *out = header; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__serialize_p2l_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_array_header_t *page = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t table_size = page->elt_size * page->nelts; + + /* serialize array header and all its elements */ + context = svn_temp_serializer__init(page, + sizeof(*page), + table_size + sizeof(*page) + 32, + pool); + + /* items in the array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&page->elts, + table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_p2l_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + apr_array_header_t *page = (apr_array_header_t *)data; + + /* resolve the only pointer in the struct */ + svn_temp_deserializer__resolve(page, (void**)&page->elts); + + /* patch up members */ + page->pool = pool; + page->nalloc = page->nelts; + + /* done */ + *out = page; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/index.h b/contrib/subversion/subversion/libsvn_fs_fs/index.h new file mode 100644 index 000000000..1b696f222 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/index.h @@ -0,0 +1,356 @@ +/* index.h : interface to FSFS indexing 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__INDEX_H +#define SVN_LIBSVN_FS__INDEX_H + +#include "fs.h" +#include "rev_file.h" + +/* Per-defined item index values. They are used to identify empty or + * mandatory items. + */ +#define SVN_FS_FS__ITEM_INDEX_UNUSED 0 /* invalid / reserved value */ +#define SVN_FS_FS__ITEM_INDEX_CHANGES 1 /* list of changed paths */ +#define SVN_FS_FS__ITEM_INDEX_ROOT_NODE 2 /* the root noderev */ +#define SVN_FS_FS__ITEM_INDEX_FIRST_USER 3 /* first noderev to be freely + assigned */ + +/* Data / item types as stored in the phys-to-log index. + */ +#define SVN_FS_FS__ITEM_TYPE_UNUSED 0 /* file section not used */ +#define SVN_FS_FS__ITEM_TYPE_FILE_REP 1 /* item is a file representation */ +#define SVN_FS_FS__ITEM_TYPE_DIR_REP 2 /* item is a directory rep. */ +#define SVN_FS_FS__ITEM_TYPE_FILE_PROPS 3 /* item is a file property rep. */ +#define SVN_FS_FS__ITEM_TYPE_DIR_PROPS 4 /* item is a directory prop rep */ +#define SVN_FS_FS__ITEM_TYPE_NODEREV 5 /* item is a noderev */ +#define SVN_FS_FS__ITEM_TYPE_CHANGES 6 /* item is a changed paths list */ + +#define SVN_FS_FS__ITEM_TYPE_ANY_REP 7 /* item is any representation. + Only used in pre-format7. */ + +/* Open / create a log-to-phys index file with the full file path name + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. + */ +svn_error_t * +svn_fs_fs__l2p_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool); + +/* Call this function before adding entries for the next revision to the + * log-to-phys index file in PROTO_INDEX. Use SCRATCH_POOL for temporary + * allocations. + */ +svn_error_t * +svn_fs_fs__l2p_proto_index_add_revision(apr_file_t *proto_index, + apr_pool_t *scratch_pool); + +/* Add a new mapping, ITEM_INDEX to the OFFSET, to log-to-phys index file + * in PROTO_INDEX. Please note that mappings may be added in any order + * but duplicate entries for the same ITEM_INDEX are not supported. + * Not all possible index values need to be used. OFFSET may be -1 to + * mark 'invalid' item indexes but that is already implied for all item + * indexes not explicitly given a mapping. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__l2p_proto_index_add_entry(apr_file_t *proto_index, + apr_off_t offset, + apr_uint64_t item_index, + apr_pool_t *scratch_pool); + +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * log-to-phys index and append it to INDEX_FILE. The first revision will + * be REVISION, entries to the next revision will be assigned to REVISION+1 + * and so forth. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Open / create a phys-to-log index file with the full file path name + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. + */ +svn_error_t * +svn_fs_fs__p2l_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool); + +/* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX. + * The entries must be added in ascending offset order and must not leave + * intermittent ranges uncovered. The revision value in ENTRY may be + * SVN_INVALID_REVISION. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__p2l_proto_index_add_entry(apr_file_t *proto_index, + const svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *scratch_pool); + +/* Set *NEXT_OFFSET to the first offset behind the last entry in the + * phys-to-log proto index file PROTO_INDEX. This will be 0 for empty + * index files. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__p2l_proto_index_next_offset(apr_off_t *next_offset, + apr_file_t *proto_index, + apr_pool_t *scratch_pool); + +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * phys-to-log index and append it to INDEX_FILE. Entries without a valid + * revision will be assigned to the REVISION given here. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Use the phys-to-log mapping files in FS to build a list of entries + * that (at least partly) overlap with the range given by BLOCK_START + * offset and BLOCK_SIZE in the rep / pack file containing REVISION. + * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements, + * allocated in RESULT_POOL. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. + * + * Note that (only) the first and the last mapping may cross a cluster + * boundary. + */ +svn_error_t * +svn_fs_fs__p2l_index_lookup(apr_array_header_t **entries, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Use the phys-to-log mapping files in FS to return the entry for the + * item starting at global OFFSET in the rep file containing REVISION in + * *ENTRY, allocated in RESULT_POOL. Sets *ENTRY to NULL if no item starts + * at exactly that offset. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. + */ +svn_error_t * +svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* For ITEM_INDEX within REV in FS, return the position in the respective + * rev or pack file in *ABSOLUTE_POSITION. If TXN_ID is not NULL, return + * the file offset within that transaction and REV should be given as + * SVN_INVALID_REVNUM in that case. + * + * REV_FILE determines whether to access single rev or pack file data. + * If that is not available anymore (neither in cache nor on disk), re-open + * the rev / pack file and retry to open the index file. For anything but + * committed log addressed revisions, REV_FILE may be NULL. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__item_offset(apr_off_t *absolute_position, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + const svn_fs_fs__id_part_t *txn_id, + apr_uint64_t item_index, + apr_pool_t *scratch_pool); + +/* Use the log-to-phys indexes in FS to determine the maximum item indexes + * assigned to revision START_REV to START_REV + COUNT - 1. That is a + * close upper limit to the actual number of items in the respective revs. + * Return the results in *MAX_IDS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__l2p_get_max_ids(apr_array_header_t **max_ids, + svn_fs_t *fs, + svn_revnum_t start_rev, + apr_size_t count, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* In *OFFSET, return the last OFFSET in the pack / rev file containing. + * REV_FILE determines whether to access single rev or pack file data. + * If that is not available anymore (neither in cache nor on disk), re-open + * the rev / pack file and retry to open the index file. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__p2l_get_max_offset(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool); + +/* Index (re-)creation utilities. + */ + +/* For FS, create a new L2P auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * entries are of type svn_fs_fs__p2l_entry_t* (sic!). The ENTRIES array + * will be reordered. Give the proto index file the lifetime of RESULT_POOL + * and use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__l2p_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* For FS, create a new P2L auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * of type svn_fs_fs__p2l_entry_t*. The FVN1 checksums are not taken from + * ENTRIES but are begin calculated from the current contents of REV_FILE + * as we go. Give the proto index file the lifetime of RESULT_POOL and use + * SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__p2l_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + svn_fs_fs__revision_file_t *rev_file, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Serialization and caching interface + */ + +/* We use this key type to address individual pages from both index types. + */ +typedef struct svn_fs_fs__page_cache_key_t +{ + /* in l2p: this is the revision of the items being mapped + in p2l: this is the start revision identifying the pack / rev file */ + apr_uint32_t revision; + + /* if TRUE, this is the index to a pack file + */ + svn_boolean_t is_packed; + + /* in l2p: page number within the revision + * in p2l: page number with the rev / pack file + */ + apr_uint64_t page; +} svn_fs_fs__page_cache_key_t; + +/* + * Implements svn_cache__serialize_func_t for l2p_header_t objects. + */ +svn_error_t * +svn_fs_fs__serialize_l2p_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for l2p_header_t objects. + */ +svn_error_t * +svn_fs_fs__deserialize_l2p_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for l2p_page_t objects. + */ +svn_error_t * +svn_fs_fs__serialize_l2p_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for l2p_page_t objects. + */ +svn_error_t * +svn_fs_fs__deserialize_l2p_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for p2l_header_t objects. + */ +svn_error_t * +svn_fs_fs__serialize_p2l_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for p2l_header_t objects. + */ +svn_error_t * +svn_fs_fs__deserialize_p2l_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for apr_array_header_t objects + * with elements of type svn_fs_fs__p2l_entry_t. + */ +svn_error_t * +svn_fs_fs__serialize_p2l_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for apr_array_header_t objects + * with elements of type svn_fs_fs__p2l_entry_t. + */ +svn_error_t * +svn_fs_fs__deserialize_p2l_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/key-gen.c b/contrib/subversion/subversion/libsvn_fs_fs/key-gen.c deleted file mode 100644 index a65c59d81..000000000 --- a/contrib/subversion/subversion/libsvn_fs_fs/key-gen.c +++ /dev/null @@ -1,159 +0,0 @@ -/* key-gen.c --- manufacturing sequential keys for some db tables - * - * ==================================================================== - * 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 -#include -#include -#include -#include -#include "private/svn_fs_private.h" -#include "key-gen.h" - -/* The Berkeley DB backend uses a key as a transaction name and the - maximum key size must be less than the maximum transaction name - length. */ -#if MAX_KEY_SIZE > SVN_FS__TXN_MAX_LEN -#error The MAX_KEY_SIZE used for BDB txn names is greater than SVN_FS__TXN_MAX_LEN. -#endif - - -/*** Keys for reps and strings. ***/ - -void -svn_fs_fs__add_keys(const char *key1, const char *key2, char *result) -{ - apr_ssize_t i1 = strlen(key1) - 1; - apr_ssize_t i2 = strlen(key2) - 1; - int i3 = 0; - int val; - int carry = 0; - char buf[MAX_KEY_SIZE + 2]; - - while ((i1 >= 0) || (i2 >= 0) || (carry > 0)) - { - val = carry; - if (i1>=0) - val += (key1[i1] <= '9') ? (key1[i1] - '0') : (key1[i1] - 'a' + 10); - - if (i2>=0) - val += (key2[i2] <= '9') ? (key2[i2] - '0') : (key2[i2] - 'a' + 10); - - carry = val / 36; - val = val % 36; - - buf[i3++] = (char)((val <= 9) ? (val + '0') : (val - 10 + 'a')); - - if (i1>=0) - i1--; - if (i2>=0) - i2--; - } - - /* Now reverse the resulting string and NULL terminate it. */ - for (i1 = 0; i1 < i3; i1++) - result[i1] = buf[i3 - i1 - 1]; - - result[i1] = '\0'; -} - - -void -svn_fs_fs__next_key(const char *this, apr_size_t *len, char *next) -{ - apr_ssize_t i; - apr_size_t olen = *len; /* remember the first length */ - char c; /* current char */ - svn_boolean_t carry = TRUE; /* boolean: do we have a carry or not? - We start with a carry, because we're - incrementing the number, after all. */ - - /* Leading zeros are not allowed, except for the string "0". */ - if ((*len > 1) && (this[0] == '0')) - { - *len = 0; - return; - } - - for (i = (olen - 1); i >= 0; i--) - { - c = this[i]; - - /* Validate as we go. */ - if (! (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')))) - { - *len = 0; - return; - } - - if (carry) - { - if (c == 'z') - next[i] = '0'; - else - { - carry = FALSE; - - if (c == '9') - next[i] = 'a'; - else - next[i] = ++c; - } - } - else - next[i] = c; - } - - /* The new length is OLEN, plus 1 if there's a carry out of the - leftmost digit. */ - *len = olen + (carry ? 1 : 0); - - /* Ensure that we haven't overrun the (ludicrous) bound on key length. - Note that MAX_KEY_SIZE is a bound on the size *including* - the trailing null byte. */ - assert(*len < MAX_KEY_SIZE); - - /* Now we know it's safe to add the null terminator. */ - next[*len] = '\0'; - - /* Handle any leftover carry. */ - if (carry) - { - memmove(next+1, next, olen); - next[0] = '1'; - } -} - - -int -svn_fs_fs__key_compare(const char *a, const char *b) -{ - apr_size_t a_len = strlen(a); - apr_size_t b_len = strlen(b); - int cmp; - - if (a_len > b_len) - return 1; - if (b_len > a_len) - return -1; - cmp = strcmp(a, b); - return (cmp ? (cmp / abs(cmp)) : 0); -} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/key-gen.h b/contrib/subversion/subversion/libsvn_fs_fs/key-gen.h deleted file mode 100644 index e1b3858c9..000000000 --- a/contrib/subversion/subversion/libsvn_fs_fs/key-gen.h +++ /dev/null @@ -1,91 +0,0 @@ -/* key-gen.c --- manufacturing sequential keys for some db tables - * - * ==================================================================== - * 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_FS_KEY_GEN_H -#define SVN_LIBSVN_FS_KEY_GEN_H - -#include - -#include "svn_types.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -/* The alphanumeric keys passed in and out of svn_fs_fs__next_key - are guaranteed never to be longer than this many bytes, - *including* the trailing null byte. It is therefore safe - to declare a key as "char key[MAX_KEY_SIZE]". - - Note that this limit will be a problem if the number of - keys in a table ever exceeds - - 18217977168218728251394687124089371267338971528174 - 76066745969754933395997209053270030282678007662838 - 67331479599455916367452421574456059646801054954062 - 15017704234999886990788594743994796171248406730973 - 80736524850563115569208508785942830080999927310762 - 50733948404739350551934565743979678824151197232629 - 947748581376, - - but that's a risk we'll live with for now. */ -#define MAX_KEY_SIZE 200 - - -/* Generate the next key after a given alphanumeric key. - * - * The first *LEN bytes of THIS are an ascii representation of a - * number in base 36: digits 0-9 have their usual values, and a-z have - * values 10-35. - * - * The new key is stored in NEXT, null-terminated. NEXT must be at - * least *LEN + 2 bytes long -- one extra byte to hold a possible - * overflow column, and one for null termination. On return, *LEN - * will be set to the length of the new key, not counting the null - * terminator. In other words, the outgoing *LEN will be either equal - * to the incoming, or to the incoming + 1. - * - * If THIS contains anything other than digits and lower-case - * alphabetic characters, or if it starts with `0' but is not the - * string "0", then *LEN is set to zero and the effect on NEXT - * is undefined. - */ -void svn_fs_fs__next_key(const char *this, apr_size_t *len, char *next); - - -/* Compare two strings A and B as base-36 alphanumeric keys. - * - * Return -1, 0, or 1 if A is less than, equal to, or greater than B, - * respectively. - */ -int svn_fs_fs__key_compare(const char *a, const char *b); - -/* Add two base-36 alphanumeric keys to get a third, the result. */ -void svn_fs_fs__add_keys(const char *key1, const char *key2, char *result); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SVN_LIBSVN_FS_KEY_GEN_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_fs/libsvn_fs_fs.pc.in b/contrib/subversion/subversion/libsvn_fs_fs/libsvn_fs_fs.pc.in new file mode 100644 index 000000000..324a709ee --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/libsvn_fs_fs.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs_fs +Description: Subversion FSFS Repository Filesystem Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr libsvn_fs_util +Libs: -L${libdir} -lsvn_fs_fs +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/load-index.c b/contrib/subversion/subversion/libsvn_fs_fs/load-index.c new file mode 100644 index 000000000..3142e8e35 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/load-index.c @@ -0,0 +1,98 @@ +/* load-index-cmd.c -- implements the dump-index sub-command. + * + * ==================================================================== + * 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_pools.h" + +#include "private/svn_fs_fs_private.h" +#include "private/svn_sorts_private.h" + +#include "index.h" +#include "util.h" +#include "transaction.h" + +/* A svn_sort__array compatible comparator function, sorting the + * svn_fs_fs__p2l_entry_t** given in LHS, RHS by offset. */ +static int +compare_p2l_entry_revision(const void *lhs, + const void *rhs) +{ + const svn_fs_fs__p2l_entry_t *lhs_entry + =*(const svn_fs_fs__p2l_entry_t **)lhs; + const svn_fs_fs__p2l_entry_t *rhs_entry + =*(const svn_fs_fs__p2l_entry_t **)rhs; + + if (lhs_entry->offset < rhs_entry->offset) + return -1; + + return lhs_entry->offset == rhs_entry->offset ? 0 : 1; +} + +svn_error_t * +svn_fs_fs__load_index(svn_fs_t *fs, + svn_revnum_t revision, + apr_array_header_t *entries, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Check the FS format number. */ + if (! svn_fs_fs__use_log_addressing(fs)) + return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, NULL); + + /* P2L index must be written in offset order. + * Sort ENTRIES accordingly. */ + svn_sort__array(entries, compare_p2l_entry_revision); + + /* Treat an empty array as a no-op instead error. */ + if (entries->nelts != 0) + { + const char *l2p_proto_index; + const char *p2l_proto_index; + svn_fs_fs__revision_file_t *rev_file; + + /* Open rev / pack file & trim indexes + footer off it. */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, + revision, iterpool, + iterpool)); + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + SVN_ERR(svn_io_file_trunc(rev_file->file, rev_file->l2p_offset, + iterpool)); + + /* Create proto index files for the new index data + * (will be cleaned up automatically with iterpool). */ + SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs, + rev_file, entries, + iterpool, iterpool)); + SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs, + entries, iterpool, + iterpool)); + + /* Combine rev data with new index data. */ + SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index, + p2l_proto_index, + rev_file->start_revision, iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/lock.c b/contrib/subversion/subversion/libsvn_fs_fs/lock.c index 95bd94309..c852025f2 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/lock.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/lock.c @@ -20,7 +20,6 @@ * ==================================================================== */ - #include "svn_pools.h" #include "svn_error.h" #include "svn_dirent_uri.h" @@ -37,10 +36,12 @@ #include "lock.h" #include "tree.h" #include "fs_fs.h" +#include "util.h" #include "../libsvn_fs/fs-loader.h" #include "private/svn_fs_util.h" #include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" #include "svn_private_config.h" /* Names of hash keys used to store a lock for writing to disk. */ @@ -102,8 +103,7 @@ hash_store(apr_hash_t *hash, of that value (if it exists). */ static const char * hash_fetch(apr_hash_t *hash, - const char *key, - apr_pool_t *pool) + const char *key) { svn_string_t *str = svn_hash_gets(hash, key); return str ? str->data : NULL; @@ -133,7 +133,7 @@ digest_path_from_digest(const char *fs_path, { return svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, apr_pstrmemdup(pool, digest, DIGEST_SUBDIR_LEN), - digest, NULL); + digest, SVN_VA_NULL); } @@ -151,7 +151,7 @@ digest_path_from_path(const char **digest_path, *digest_path = svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, apr_pstrmemdup(pool, digest, DIGEST_SUBDIR_LEN), - digest, NULL); + digest, SVN_VA_NULL); return SVN_NO_ERROR; } @@ -209,8 +209,8 @@ write_digest_file(apr_hash_t *children, for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) { svn_stringbuf_appendbytes(children_list, - svn__apr_hash_index_key(hi), - svn__apr_hash_index_klen(hi)); + apr_hash_this_key(hi), + apr_hash_this_key_len(hi)); svn_stringbuf_appendbyte(children_list, '\n'); } hash_store(hash, CHILDREN_KEY, sizeof(CHILDREN_KEY)-1, @@ -252,24 +252,23 @@ read_digest_file(apr_hash_t **children_p, apr_hash_t *hash; svn_stream_t *stream; const char *val; + svn_node_kind_t kind; if (lock_p) *lock_p = NULL; if (children_p) *children_p = apr_hash_make(pool); - err = svn_stream_open_readonly(&stream, digest_path, pool, pool); - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_clear(err); - return SVN_NO_ERROR; - } - SVN_ERR(err); + SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); + if (kind == svn_node_none) + return SVN_NO_ERROR; /* If our caller doesn't care about anything but the presence of the file... whatever. */ - if (! (lock_p || children_p)) - return svn_stream_close(stream); + if (kind == svn_node_file && !lock_p && !children_p) + return SVN_NO_ERROR; + + SVN_ERR(svn_stream_open_readonly(&stream, digest_path, pool, pool)); hash = apr_hash_make(pool); if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) @@ -284,7 +283,7 @@ read_digest_file(apr_hash_t **children_p, /* If our caller cares, see if we have a lock path in our hash. If so, we'll assume we have a lock here. */ - val = hash_fetch(hash, PATH_KEY, pool); + val = hash_fetch(hash, PATH_KEY); if (val && lock_p) { const char *path = val; @@ -293,30 +292,30 @@ read_digest_file(apr_hash_t **children_p, lock = svn_lock_create(pool); lock->path = path; - if (! ((lock->token = hash_fetch(hash, TOKEN_KEY, pool)))) + if (! ((lock->token = hash_fetch(hash, TOKEN_KEY)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); - if (! ((lock->owner = hash_fetch(hash, OWNER_KEY, pool)))) + if (! ((lock->owner = hash_fetch(hash, OWNER_KEY)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); - if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY, pool)))) + if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); lock->is_dav_comment = (val[0] == '1'); - if (! ((val = hash_fetch(hash, CREATION_DATE_KEY, pool)))) + if (! ((val = hash_fetch(hash, CREATION_DATE_KEY)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); SVN_ERR(svn_time_from_cstring(&(lock->creation_date), val, pool)); - if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY, pool))) + if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY))) SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), val, pool)); - lock->comment = hash_fetch(hash, COMMENT_KEY, pool); + lock->comment = hash_fetch(hash, COMMENT_KEY); *lock_p = lock; } /* If our caller cares, see if we have any children for this path. */ - val = hash_fetch(hash, CHILDREN_KEY, pool); + val = hash_fetch(hash, CHILDREN_KEY); if (val && children_p) { apr_array_header_t *kiddos = svn_cstring_split(val, "\n", FALSE, pool); @@ -340,8 +339,6 @@ read_digest_file(apr_hash_t **children_p, /* Write LOCK in FS to the actual OS filesystem. Use PERMS_REFERENCE for the permissions of any digest files. - - Note: this takes an FS_PATH because it's called from the hotcopy logic. */ static svn_error_t * set_lock(const char *fs_path, @@ -349,130 +346,117 @@ set_lock(const char *fs_path, const char *perms_reference, apr_pool_t *pool) { - svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool); - const char *lock_digest_path = NULL; - apr_pool_t *subpool; + const char *digest_path; + apr_hash_t *children; - SVN_ERR_ASSERT(lock); + SVN_ERR(digest_path_from_path(&digest_path, fs_path, lock->path, pool)); - /* Iterate in reverse, creating the lock for LOCK->path, and then - just adding entries for its parent, until we reach a parent - that's already listed in *its* parent. */ - subpool = svn_pool_create(pool); - while (1729) - { - const char *digest_path, *digest_file; - apr_hash_t *this_children; - svn_lock_t *this_lock; + /* We could get away without reading the file as children should + always come back empty. */ + SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, pool)); - svn_pool_clear(subpool); + SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, + perms_reference, pool)); - /* Calculate the DIGEST_PATH for the currently FS path, and then - get its DIGEST_FILE basename. */ - SVN_ERR(digest_path_from_path(&digest_path, fs_path, this_path->data, - subpool)); - digest_file = svn_dirent_basename(digest_path, subpool); + return SVN_NO_ERROR; +} - SVN_ERR(read_digest_file(&this_children, &this_lock, fs_path, - digest_path, subpool)); +static svn_error_t * +delete_lock(const char *fs_path, + const char *path, + apr_pool_t *pool) +{ + const char *digest_path; - /* We're either writing a new lock (first time through only) or - a new entry (every time but the first). */ - if (lock) - { - this_lock = lock; - lock = NULL; - lock_digest_path = apr_pstrdup(pool, digest_file); - } - else - { - /* If we already have an entry for this path, we're done. */ - if (svn_hash_gets(this_children, lock_digest_path)) - break; - svn_hash_sets(this_children, lock_digest_path, (void *)1); - } - SVN_ERR(write_digest_file(this_children, this_lock, fs_path, - digest_path, perms_reference, subpool)); - - /* Prep for next iteration, or bail if we're done. */ - if (svn_fspath__is_root(this_path->data, this_path->len)) - break; - svn_stringbuf_set(this_path, - svn_fspath__dirname(this_path->data, subpool)); - } + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); + + SVN_ERR(svn_io_remove_file2(digest_path, TRUE, pool)); - svn_pool_destroy(subpool); return SVN_NO_ERROR; } -/* Delete LOCK from FS in the actual OS filesystem. */ static svn_error_t * -delete_lock(svn_fs_t *fs, - svn_lock_t *lock, - apr_pool_t *pool) +add_to_digest(const char *fs_path, + apr_array_header_t *paths, + const char *index_path, + const char *perms_reference, + apr_pool_t *pool) { - svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool); - const char *child_to_kill = NULL; - apr_pool_t *subpool; + const char *index_digest_path; + apr_hash_t *children; + svn_lock_t *lock; + int i; + unsigned int original_count; - SVN_ERR_ASSERT(lock); + SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); - /* Iterate in reverse, deleting the lock for LOCK->path, and then - deleting its entry as it appears in each of its parents. */ - subpool = svn_pool_create(pool); - while (1729) + SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool)); + + original_count = apr_hash_count(children); + + for (i = 0; i < paths->nelts; ++i) { + const char *path = APR_ARRAY_IDX(paths, i, const char *); const char *digest_path, *digest_file; - apr_hash_t *this_children; - svn_lock_t *this_lock; - svn_pool_clear(subpool); + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); + digest_file = svn_dirent_basename(digest_path, NULL); + svn_hash_sets(children, digest_file, (void *)1); + } - /* Calculate the DIGEST_PATH for the currently FS path, and then - get its DIGEST_FILE basename. */ - SVN_ERR(digest_path_from_path(&digest_path, fs->path, this_path->data, - subpool)); - digest_file = svn_dirent_basename(digest_path, subpool); + if (apr_hash_count(children) != original_count) + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + perms_reference, pool)); - SVN_ERR(read_digest_file(&this_children, &this_lock, fs->path, - digest_path, subpool)); + return SVN_NO_ERROR; +} - /* Delete the lock (first time through only). */ - if (lock) - { - this_lock = NULL; - lock = NULL; - child_to_kill = apr_pstrdup(pool, digest_file); - } +static svn_error_t * +delete_from_digest(const char *fs_path, + apr_array_header_t *paths, + const char *index_path, + const char *perms_reference, + apr_pool_t *pool) +{ + const char *index_digest_path; + apr_hash_t *children; + svn_lock_t *lock; + int i; - if (child_to_kill) - svn_hash_sets(this_children, child_to_kill, NULL); + SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); - if (! (this_lock || apr_hash_count(this_children) != 0)) - { - /* Special case: no goodz, no file. And remember to nix - the entry for it in its parent. */ - SVN_ERR(svn_io_remove_file2(digest_path, FALSE, subpool)); - } - else - { - const char *rev_0_path; - SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, fs, 0, pool)); - SVN_ERR(write_digest_file(this_children, this_lock, fs->path, - digest_path, rev_0_path, subpool)); - } + SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool)); - /* Prep for next iteration, or bail if we're done. */ - if (svn_fspath__is_root(this_path->data, this_path->len)) - break; - svn_stringbuf_set(this_path, - svn_fspath__dirname(this_path->data, subpool)); + for (i = 0; i < paths->nelts; ++i) + { + const char *path = APR_ARRAY_IDX(paths, i, const char *); + const char *digest_path, *digest_file; + + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); + digest_file = svn_dirent_basename(digest_path, NULL); + svn_hash_sets(children, digest_file, NULL); } - svn_pool_destroy(subpool); + if (apr_hash_count(children) || lock) + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + perms_reference, pool)); + else + SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, pool)); + return SVN_NO_ERROR; } +static svn_error_t * +unlock_single(svn_fs_t *fs, + svn_lock_t *lock, + apr_pool_t *pool); + +/* Check if LOCK has been already expired. */ +static svn_boolean_t lock_expired(const svn_lock_t *lock) +{ + return lock->expiration_date && (apr_time_now() > lock->expiration_date); +} + /* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be TRUE if the caller (or one of its callers) has taken out the repository-wide write lock, FALSE otherwise. If MUST_EXIST is @@ -502,12 +486,12 @@ get_lock(svn_lock_t **lock_p, return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; /* Don't return an expired lock. */ - if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) + if (lock_expired(lock)) { /* Only remove the lock if we have the write lock. Read operations shouldn't change the filesystem. */ if (have_write_lock) - SVN_ERR(delete_lock(fs, lock, pool)); + SVN_ERR(unlock_single(fs, lock, pool)); return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); } @@ -549,69 +533,17 @@ get_lock_helper(svn_fs_t *fs, } -/* Baton for locks_walker(). */ -struct walk_locks_baton { - svn_fs_get_locks_callback_t get_locks_func; - void *get_locks_baton; - svn_fs_t *fs; -}; - -/* Implements walk_digests_callback_t. */ -static svn_error_t * -locks_walker(void *baton, - const char *fs_path, - const char *digest_path, - apr_hash_t *children, - svn_lock_t *lock, - svn_boolean_t have_write_lock, - apr_pool_t *pool) -{ - struct walk_locks_baton *wlb = baton; - - if (lock) - { - /* Don't report an expired lock. */ - if (lock->expiration_date == 0 - || (apr_time_now() <= lock->expiration_date)) - { - if (wlb->get_locks_func) - SVN_ERR(wlb->get_locks_func(wlb->get_locks_baton, lock, pool)); - } - else - { - /* Only remove the lock if we have the write lock. - Read operations shouldn't change the filesystem. */ - if (have_write_lock) - SVN_ERR(delete_lock(wlb->fs, lock, pool)); - } - } - - return SVN_NO_ERROR; -} - -/* Callback type for walk_digest_files(). - * - * CHILDREN and LOCK come from a read_digest_file(digest_path) call. - */ -typedef svn_error_t *(*walk_digests_callback_t)(void *baton, - const char *fs_path, - const char *digest_path, - apr_hash_t *children, - svn_lock_t *lock, - svn_boolean_t have_write_lock, - apr_pool_t *pool); - -/* A recursive function that calls WALK_DIGESTS_FUNC/WALK_DIGESTS_BATON for - all lock digest files in and under PATH in FS. +/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for + all locks in and under PATH in FS. HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) has the FS write lock. */ static svn_error_t * -walk_digest_files(const char *fs_path, - const char *digest_path, - walk_digests_callback_t walk_digests_func, - void *walk_digests_baton, - svn_boolean_t have_write_lock, - apr_pool_t *pool) +walk_locks(svn_fs_t *fs, + const char *digest_path, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, + svn_boolean_t have_write_lock, + apr_pool_t *pool) { apr_hash_index_t *hi; apr_hash_t *children; @@ -619,47 +551,46 @@ walk_digest_files(const char *fs_path, svn_lock_t *lock; /* First, send up any locks in the current digest file. */ - SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool)); + SVN_ERR(read_digest_file(&children, &lock, fs->path, digest_path, pool)); - SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, - children, lock, - have_write_lock, pool)); + if (lock && lock_expired(lock)) + { + /* Only remove the lock if we have the write lock. + Read operations shouldn't change the filesystem. */ + if (have_write_lock) + SVN_ERR(unlock_single(fs, lock, pool)); + } + else if (lock) + { + SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); + } - /* Now, recurse on this thing's child entries (if any; bail otherwise). */ + /* Now, report all the child entries (if any; bail otherwise). */ if (! apr_hash_count(children)) return SVN_NO_ERROR; subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) { - const char *digest = svn__apr_hash_index_key(hi); + const char *digest = apr_hash_this_key(hi); svn_pool_clear(subpool); - SVN_ERR(walk_digest_files - (fs_path, digest_path_from_digest(fs_path, digest, subpool), - walk_digests_func, walk_digests_baton, have_write_lock, subpool)); - } - svn_pool_destroy(subpool); - return SVN_NO_ERROR; -} -/* A recursive function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for - all locks in and under PATH in FS. - HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) - has the FS write lock. */ -static svn_error_t * -walk_locks(svn_fs_t *fs, - const char *digest_path, - svn_fs_get_locks_callback_t get_locks_func, - void *get_locks_baton, - svn_boolean_t have_write_lock, - apr_pool_t *pool) -{ - struct walk_locks_baton wlb; + SVN_ERR(read_digest_file + (NULL, &lock, fs->path, + digest_path_from_digest(fs->path, digest, subpool), subpool)); - wlb.get_locks_func = get_locks_func; - wlb.get_locks_baton = get_locks_baton; - wlb.fs = fs; - SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb, - have_write_lock, pool)); + if (lock && lock_expired(lock)) + { + /* Only remove the lock if we have the write lock. + Read operations shouldn't change the filesystem. */ + if (have_write_lock) + SVN_ERR(unlock_single(fs, lock, pool)); + } + else if (lock) + { + SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); + } + } + svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -737,70 +668,100 @@ svn_fs_fs__allow_locked_operation(const char *path, return SVN_NO_ERROR; } -/* Baton used for lock_body below. */ +/* Helper function called from the lock and unlock code. + UPDATES is a map from "const char *" parent paths to "apr_array_header_t *" + arrays of child paths. For all of the parent paths of PATH this function + adds PATH to the corresponding array of child paths. */ +static void +schedule_index_update(apr_hash_t *updates, + const char *path, + apr_pool_t *scratch_pool) +{ + apr_pool_t *hashpool = apr_hash_pool_get(updates); + const char *parent_path = path; + + while (! svn_fspath__is_root(parent_path, strlen(parent_path))) + { + apr_array_header_t *children; + + parent_path = svn_fspath__dirname(parent_path, scratch_pool); + children = svn_hash_gets(updates, parent_path); + + if (! children) + { + children = apr_array_make(hashpool, 8, sizeof(const char *)); + svn_hash_sets(updates, apr_pstrdup(hashpool, parent_path), children); + } + + APR_ARRAY_PUSH(children, const char *) = path; + } +} + +/* The effective arguments for lock_body() below. */ struct lock_baton { - svn_lock_t **lock_p; svn_fs_t *fs; - const char *path; - const char *token; + apr_array_header_t *targets; + apr_array_header_t *infos; const char *comment; svn_boolean_t is_dav_comment; apr_time_t expiration_date; - svn_revnum_t current_rev; svn_boolean_t steal_lock; - apr_pool_t *pool; + apr_pool_t *result_pool; }; - -/* This implements the svn_fs_fs__with_write_lock() 'body' callback - type, and assumes that the write lock is held. - BATON is a 'struct lock_baton *'. */ static svn_error_t * -lock_body(void *baton, apr_pool_t *pool) +check_lock(svn_error_t **fs_err, + const char *path, + const svn_fs_lock_target_t *target, + struct lock_baton *lb, + svn_fs_root_t *root, + svn_revnum_t youngest_rev, + apr_pool_t *pool) { - struct lock_baton *lb = baton; svn_node_kind_t kind; svn_lock_t *existing_lock; - svn_lock_t *lock; - svn_fs_root_t *root; - svn_revnum_t youngest; - const char *rev_0_path; - /* Until we implement directory locks someday, we only allow locks - on files or non-existent paths. */ - /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular - library dependencies, which are not portable. */ - SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); - SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); - SVN_ERR(svn_fs_fs__check_path(&kind, root, lb->path, pool)); + *fs_err = SVN_NO_ERROR; + + SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool)); if (kind == svn_node_dir) - return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path); + { + *fs_err = SVN_FS__ERR_NOT_FILE(lb->fs, path); + return SVN_NO_ERROR; + } /* While our locking implementation easily supports the locking of nonexistent paths, we deliberately choose not to allow such madness. */ if (kind == svn_node_none) { - if (SVN_IS_VALID_REVNUM(lb->current_rev)) - return svn_error_createf( + if (SVN_IS_VALID_REVNUM(target->current_rev)) + *fs_err = svn_error_createf( SVN_ERR_FS_OUT_OF_DATE, NULL, _("Path '%s' doesn't exist in HEAD revision"), - lb->path); + path); else - return svn_error_createf( + *fs_err = svn_error_createf( SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' doesn't exist in HEAD revision"), - lb->path); - } + path); - /* We need to have a username attached to the fs. */ - if (!lb->fs->access_ctx || !lb->fs->access_ctx->username) - return SVN_FS__ERR_NO_USER(lb->fs); + return SVN_NO_ERROR; + } /* Is the caller attempting to lock an out-of-date working file? */ - if (SVN_IS_VALID_REVNUM(lb->current_rev)) + if (SVN_IS_VALID_REVNUM(target->current_rev)) { svn_revnum_t created_rev; - SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, lb->path, + + if (target->current_rev > youngest_rev) + { + *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), + target->current_rev); + return SVN_NO_ERROR; + } + + SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path, pool)); /* SVN_INVALID_REVNUM means the path doesn't exist. So @@ -808,14 +769,22 @@ lock_body(void *baton, apr_pool_t *pool) working copy, but somebody else has deleted the thing from HEAD. That counts as being 'out of date'. */ if (! SVN_IS_VALID_REVNUM(created_rev)) - return svn_error_createf - (SVN_ERR_FS_OUT_OF_DATE, NULL, - _("Path '%s' doesn't exist in HEAD revision"), lb->path); - - if (lb->current_rev < created_rev) - return svn_error_createf - (SVN_ERR_FS_OUT_OF_DATE, NULL, - _("Lock failed: newer version of '%s' exists"), lb->path); + { + *fs_err = svn_error_createf + (SVN_ERR_FS_OUT_OF_DATE, NULL, + _("Path '%s' doesn't exist in HEAD revision"), path); + + return SVN_NO_ERROR; + } + + if (target->current_rev < created_rev) + { + *fs_err = svn_error_createf + (SVN_ERR_FS_OUT_OF_DATE, NULL, + _("Lock failed: newer version of '%s' exists"), path); + + return SVN_NO_ERROR; + } } /* If the caller provided a TOKEN, we *really* need to see @@ -834,116 +803,380 @@ lock_body(void *baton, apr_pool_t *pool) acceptable to ignore; it means that the path is now free and clear for locking, because the fsfs funcs just cleared out both of the tables for us. */ - SVN_ERR(get_lock_helper(lb->fs, &existing_lock, lb->path, TRUE, pool)); + SVN_ERR(get_lock_helper(lb->fs, &existing_lock, path, TRUE, pool)); if (existing_lock) { if (! lb->steal_lock) { /* Sorry, the path is already locked. */ - return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock); + *fs_err = SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock); + return SVN_NO_ERROR; } - else + } + + return SVN_NO_ERROR; +} + +struct lock_info_t { + const char *path; + svn_lock_t *lock; + svn_error_t *fs_err; +}; + +/* The body of svn_fs_fs__lock(), which see. + + BATON is a 'struct lock_baton *' holding the effective arguments. + BATON->targets is an array of 'svn_sort__item_t' targets, sorted by + path, mapping canonical path to 'svn_fs_lock_target_t'. Set + BATON->infos to an array of 'lock_info_t' holding the results. For + the other arguments, see svn_fs_lock_many(). + + This implements the svn_fs_fs__with_write_lock() 'body' callback + type, and assumes that the write lock is held. + */ +static svn_error_t * +lock_body(void *baton, apr_pool_t *pool) +{ + struct lock_baton *lb = baton; + svn_fs_root_t *root; + svn_revnum_t youngest; + const char *rev_0_path; + int i; + apr_hash_t *index_updates = apr_hash_make(pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool = svn_pool_create(pool); + + /* Until we implement directory locks someday, we only allow locks + on files or non-existent paths. */ + /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular + library dependencies, which are not portable. */ + SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); + SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); + + for (i = 0; i < lb->targets->nelts; ++i) + { + const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, + svn_sort__item_t); + struct lock_info_t info; + + svn_pool_clear(iterpool); + + info.path = item->key; + info.lock = NULL; + info.fs_err = SVN_NO_ERROR; + + SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root, + youngest, iterpool)); + + /* If no error occurred while pre-checking, schedule the index updates for + this path. */ + if (!info.fs_err) + schedule_index_update(index_updates, info.path, iterpool); + + APR_ARRAY_PUSH(lb->infos, struct lock_info_t) = info; + } + + rev_0_path = svn_fs_fs__path_rev_absolute(lb->fs, 0, pool); + + /* We apply the scheduled index updates before writing the actual locks. + + Writing indices before locks is correct: if interrupted it leaves + indices without locks rather than locks without indices. An + index without a lock is consistent in that it always shows up as + unlocked in svn_fs_fs__allow_locked_operation. A lock without an + index is inconsistent, svn_fs_fs__allow_locked_operation will + show locked on the file but unlocked on the parent. */ + + for (hi = apr_hash_first(pool, index_updates); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + apr_array_header_t *children = apr_hash_this_val(hi); + + svn_pool_clear(iterpool); + SVN_ERR(add_to_digest(lb->fs->path, children, path, rev_0_path, + iterpool)); + } + + for (i = 0; i < lb->infos->nelts; ++i) + { + struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, + struct lock_info_t); + svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, svn_sort__item_t); + svn_fs_lock_target_t *target = item->value; + + svn_pool_clear(iterpool); + + if (! info->fs_err) { - /* STEAL_LOCK was passed, so fs_username is "stealing" the - lock from lock->owner. Destroy the existing lock. */ - SVN_ERR(delete_lock(lb->fs, existing_lock, pool)); + info->lock = svn_lock_create(lb->result_pool); + if (target->token) + info->lock->token = apr_pstrdup(lb->result_pool, target->token); + else + SVN_ERR(svn_fs_fs__generate_lock_token(&(info->lock->token), lb->fs, + lb->result_pool)); + + /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result + of svn_fspath__canonicalize() (see svn_fs_fs__lock()). */ + info->lock->path = info->path; + info->lock->owner = apr_pstrdup(lb->result_pool, + lb->fs->access_ctx->username); + info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment); + info->lock->is_dav_comment = lb->is_dav_comment; + info->lock->creation_date = apr_time_now(); + info->lock->expiration_date = lb->expiration_date; + + info->fs_err = set_lock(lb->fs->path, info->lock, rev_0_path, + iterpool); } } - /* Create our new lock, and add it to the tables. - Ensure that the lock is created in the correct pool. */ - lock = svn_lock_create(lb->pool); - if (lb->token) - lock->token = apr_pstrdup(lb->pool, lb->token); - else - SVN_ERR(svn_fs_fs__generate_lock_token(&(lock->token), lb->fs, - lb->pool)); - lock->path = apr_pstrdup(lb->pool, lb->path); - lock->owner = apr_pstrdup(lb->pool, lb->fs->access_ctx->username); - lock->comment = apr_pstrdup(lb->pool, lb->comment); - lock->is_dav_comment = lb->is_dav_comment; - lock->creation_date = apr_time_now(); - lock->expiration_date = lb->expiration_date; - SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, lb->fs, 0, pool)); - SVN_ERR(set_lock(lb->fs->path, lock, rev_0_path, pool)); - *lb->lock_p = lock; - + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } -/* Baton used for unlock_body below. */ +/* The effective arguments for unlock_body() below. */ struct unlock_baton { svn_fs_t *fs; - const char *path; - const char *token; + apr_array_header_t *targets; + apr_array_header_t *infos; + /* Set skip_check TRUE to prevent the checks that set infos[].fs_err. */ + svn_boolean_t skip_check; svn_boolean_t break_lock; + apr_pool_t *result_pool; }; -/* This implements the svn_fs_fs__with_write_lock() 'body' callback +static svn_error_t * +check_unlock(svn_error_t **fs_err, + const char *path, + const char *token, + struct unlock_baton *ub, + svn_fs_root_t *root, + apr_pool_t *pool) +{ + svn_lock_t *lock; + + *fs_err = get_lock(&lock, ub->fs, path, TRUE, TRUE, pool); + if (!*fs_err && !ub->break_lock) + { + if (strcmp(token, lock->token) != 0) + *fs_err = SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, path); + else if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0) + *fs_err = SVN_FS__ERR_LOCK_OWNER_MISMATCH(ub->fs, + ub->fs->access_ctx->username, + lock->owner); + } + + return SVN_NO_ERROR; +} + +struct unlock_info_t { + const char *path; + svn_error_t *fs_err; + svn_boolean_t done; +}; + +/* The body of svn_fs_fs__unlock(), which see. + + BATON is a 'struct unlock_baton *' holding the effective arguments. + BATON->targets is an array of 'svn_sort__item_t' targets, sorted by + path, mapping canonical path to (const char *) token. Set + BATON->infos to an array of 'unlock_info_t' results. For the other + arguments, see svn_fs_unlock_many(). + + This implements the svn_fs_fs__with_write_lock() 'body' callback type, and assumes that the write lock is held. - BATON is a 'struct unlock_baton *'. */ + */ static svn_error_t * unlock_body(void *baton, apr_pool_t *pool) { struct unlock_baton *ub = baton; - svn_lock_t *lock; + svn_fs_root_t *root; + svn_revnum_t youngest; + const char *rev_0_path; + int i; + apr_hash_t *indices_updates = apr_hash_make(pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool = svn_pool_create(pool); - /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */ - SVN_ERR(get_lock(&lock, ub->fs, ub->path, TRUE, TRUE, pool)); + SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool)); + SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool)); - /* Unless breaking the lock, we do some checks. */ - if (! ub->break_lock) + for (i = 0; i < ub->targets->nelts; ++i) { - /* Sanity check: the incoming token should match lock->token. */ - if (strcmp(ub->token, lock->token) != 0) - return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path); - - /* There better be a username attached to the fs. */ - if (! (ub->fs->access_ctx && ub->fs->access_ctx->username)) - return SVN_FS__ERR_NO_USER(ub->fs); - - /* And that username better be the same as the lock's owner. */ - if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0) - return SVN_FS__ERR_LOCK_OWNER_MISMATCH( - ub->fs, ub->fs->access_ctx->username, lock->owner); + const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i, + svn_sort__item_t); + const char *token = item->value; + struct unlock_info_t info; + + svn_pool_clear(iterpool); + + info.path = item->key; + info.fs_err = SVN_NO_ERROR; + info.done = FALSE; + + if (!ub->skip_check) + SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root, + iterpool)); + + /* If no error occurred while pre-checking, schedule the index updates for + this path. */ + if (!info.fs_err) + schedule_index_update(indices_updates, info.path, iterpool); + + APR_ARRAY_PUSH(ub->infos, struct unlock_info_t) = info; } - /* Remove lock and lock token files. */ - return delete_lock(ub->fs, lock, pool); + rev_0_path = svn_fs_fs__path_rev_absolute(ub->fs, 0, pool); + + /* Unlike the lock_body(), we need to delete locks *before* we start to + update indices. */ + + for (i = 0; i < ub->infos->nelts; ++i) + { + struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, i, + struct unlock_info_t); + + svn_pool_clear(iterpool); + + if (! info->fs_err) + { + SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool)); + info->done = TRUE; + } + } + + for (hi = apr_hash_first(pool, indices_updates); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + apr_array_header_t *children = apr_hash_this_val(hi); + + svn_pool_clear(iterpool); + SVN_ERR(delete_from_digest(ub->fs->path, children, path, rev_0_path, + iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Unlock the lock described by LOCK->path and LOCK->token in FS. + + This assumes that the write lock is held. + */ +static svn_error_t * +unlock_single(svn_fs_t *fs, + svn_lock_t *lock, + apr_pool_t *pool) +{ + struct unlock_baton ub; + svn_sort__item_t item; + apr_array_header_t *targets = apr_array_make(pool, 1, + sizeof(svn_sort__item_t)); + item.key = lock->path; + item.klen = strlen(item.key); + item.value = (char*)lock->token; + APR_ARRAY_PUSH(targets, svn_sort__item_t) = item; + + ub.fs = fs; + ub.targets = targets; + ub.infos = apr_array_make(pool, targets->nelts, + sizeof(struct unlock_info_t)); + ub.skip_check = TRUE; + ub.result_pool = pool; + + /* No ub.infos[].fs_err error because skip_check is TRUE. */ + SVN_ERR(unlock_body(&ub, pool)); + + return SVN_NO_ERROR; } /*** Public API implementations ***/ svn_error_t * -svn_fs_fs__lock(svn_lock_t **lock_p, - svn_fs_t *fs, - const char *path, - const char *token, +svn_fs_fs__lock(svn_fs_t *fs, + apr_hash_t *targets, const char *comment, svn_boolean_t is_dav_comment, apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool) + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { struct lock_baton lb; + apr_array_header_t *sorted_targets; + apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool; + svn_error_t *err, *cb_err = SVN_NO_ERROR; + int i; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - path = svn_fs__canonicalize_abspath(path, pool); - lb.lock_p = lock_p; + /* We need to have a username attached to the fs. */ + if (!fs->access_ctx || !fs->access_ctx->username) + return SVN_FS__ERR_NO_USER(fs); + + /* The FS locking API allows both canonical and non-canonical + paths which means that the same canonical path could be + represented more than once in the TARGETS hash. We just keep + one, choosing one with a token if possible. */ + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + const svn_fs_lock_target_t *target = apr_hash_this_val(hi); + const svn_fs_lock_target_t *other; + + path = svn_fspath__canonicalize(path, result_pool); + other = svn_hash_gets(canonical_targets, path); + + if (!other || (!other->token && target->token)) + svn_hash_sets(canonical_targets, path, target); + } + + sorted_targets = svn_sort__hash(canonical_targets, + svn_sort_compare_items_as_paths, + scratch_pool); + lb.fs = fs; - lb.path = path; - lb.token = token; + lb.targets = sorted_targets; + lb.infos = apr_array_make(result_pool, sorted_targets->nelts, + sizeof(struct lock_info_t)); lb.comment = comment; lb.is_dav_comment = is_dav_comment; lb.expiration_date = expiration_date; - lb.current_rev = current_rev; lb.steal_lock = steal_lock; - lb.pool = pool; + lb.result_pool = result_pool; + + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, iterpool); + for (i = 0; i < lb.infos->nelts; ++i) + { + struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i, + struct lock_info_t); + svn_pool_clear(iterpool); + if (!cb_err && lock_callback) + { + if (!info->lock && !info->fs_err) + info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, + 0, _("Failed to lock '%s'"), + info->path); + + cb_err = lock_callback(lock_baton, info->path, info->lock, + info->fs_err, iterpool); + } + svn_error_clear(info->fs_err); + } + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; - return svn_fs_fs__with_write_lock(fs, lock_body, &lb, pool); + return svn_error_trace(err); } @@ -959,29 +1192,84 @@ svn_fs_fs__generate_lock_token(const char **token, generate a URI that matches the DAV RFC. We could change this to some other URI scheme someday, if we wish. */ *token = apr_pstrcat(pool, "opaquelocktoken:", - svn_uuid_generate(pool), (char *)NULL); + svn_uuid_generate(pool), SVN_VA_NULL); return SVN_NO_ERROR; } - svn_error_t * svn_fs_fs__unlock(svn_fs_t *fs, - const char *path, - const char *token, + apr_hash_t *targets, svn_boolean_t break_lock, - apr_pool_t *pool) + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { struct unlock_baton ub; + apr_array_header_t *sorted_targets; + apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool; + svn_error_t *err, *cb_err = SVN_NO_ERROR; + int i; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - path = svn_fs__canonicalize_abspath(path, pool); + + /* We need to have a username attached to the fs. */ + if (!fs->access_ctx || !fs->access_ctx->username) + return SVN_FS__ERR_NO_USER(fs); + + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); + const char *other; + + path = svn_fspath__canonicalize(path, result_pool); + other = svn_hash_gets(canonical_targets, path); + + if (!other) + svn_hash_sets(canonical_targets, path, token); + } + + sorted_targets = svn_sort__hash(canonical_targets, + svn_sort_compare_items_as_paths, + scratch_pool); ub.fs = fs; - ub.path = path; - ub.token = token; + ub.targets = sorted_targets; + ub.infos = apr_array_make(result_pool, sorted_targets->nelts, + sizeof(struct unlock_info_t)); + ub.skip_check = FALSE; ub.break_lock = break_lock; + ub.result_pool = result_pool; + + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, iterpool); + for (i = 0; i < ub.infos->nelts; ++i) + { + struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i, + struct unlock_info_t); + svn_pool_clear(iterpool); + if (!cb_err && lock_callback) + { + if (!info->done && !info->fs_err) + info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, + 0, _("Failed to unlock '%s'"), + info->path); + cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err, + iterpool); + } + svn_error_clear(info->fs_err); + } + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; - return svn_fs_fs__with_write_lock(fs, unlock_body, &ub, pool); + return svn_error_trace(err); } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/lock.h b/contrib/subversion/subversion/libsvn_fs_fs/lock.h index 1acc79e0b..beaaa0da9 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/lock.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/lock.h @@ -32,32 +32,39 @@ extern "C" { /* These functions implement some of the calls in the FS loader library's fs vtables. */ -svn_error_t *svn_fs_fs__lock(svn_lock_t **lock, - svn_fs_t *fs, - const char *path, - const char *token, +/* See svn_fs_lock(), svn_fs_lock_many(). */ +svn_error_t *svn_fs_fs__lock(svn_fs_t *fs, + apr_hash_t *targets, const char *comment, svn_boolean_t is_dav_comment, apr_time_t expiration_date, - svn_revnum_t current_rev, svn_boolean_t steal_lock, - apr_pool_t *pool); + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* See svn_fs_generate_lock_token(). */ svn_error_t *svn_fs_fs__generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool); +/* See svn_fs_unlock(), svn_fs_unlock_many(). */ svn_error_t *svn_fs_fs__unlock(svn_fs_t *fs, - const char *path, - const char *token, + apr_hash_t *targets, svn_boolean_t break_lock, - apr_pool_t *pool); + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* See svn_fs_get_lock(). */ svn_error_t *svn_fs_fs__get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, apr_pool_t *pool); +/* See svn_fs_get_locks2(). */ svn_error_t *svn_fs_fs__get_locks(svn_fs_t *fs, const char *path, svn_depth_t depth, diff --git a/contrib/subversion/subversion/libsvn_fs_fs/low_level.c b/contrib/subversion/subversion/libsvn_fs_fs/low_level.c new file mode 100644 index 000000000..a27bbcc35 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/low_level.c @@ -0,0 +1,1196 @@ +/* low_level.c --- low level r/w access to fs_fs file structures + * + * ==================================================================== + * 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_sorts.h" +#include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_fspath.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "low_level.h" + +/* 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 of representation. */ +#define REP_PLAIN "PLAIN" +#define REP_DELTA "DELTA" + +/* 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 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 + +/* Convert the C string in *TEXT to a revision number and return it in *REV. + * Overflows, negative values other than -1 and terminating characters other + * than 0x20 or 0x0 will cause an error. Set *TEXT to the first char after + * the initial separator or to EOS. + */ +static svn_error_t * +parse_revnum(svn_revnum_t *rev, + const char **text) +{ + const char *string = *text; + if ((string[0] == '-') && (string[1] == '1')) + { + *rev = SVN_INVALID_REVNUM; + string += 2; + } + else + { + SVN_ERR(svn_revnum_parse(rev, string, &string)); + } + + if (*string == ' ') + ++string; + else if (*string != '\0') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid character in revision number")); + + *text = string; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__parse_revision_trailer(apr_off_t *root_offset, + apr_off_t *changes_offset, + svn_stringbuf_t *trailer, + svn_revnum_t rev) +{ + int i, num_bytes; + const char *str; + + /* This cast should be safe since the maximum amount read, 64, will + never be bigger than the size of an int. */ + num_bytes = (int) trailer->len; + + /* The last byte should be a newline. */ + if (trailer->len == 0 || trailer->data[trailer->len - 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 (trailer->data[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 = &trailer->data[i]; + + /* find the next space */ + for ( ; i < (num_bytes - 2) ; i++) + if (trailer->data[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; + + trailer->data[i] = '\0'; + SVN_ERR(svn_cstring_atoi64(&val, str)); + *root_offset = (apr_off_t)val; + } + + i++; + str = &trailer->data[i]; + + /* find the next newline */ + for ( ; i < num_bytes; i++) + if (trailer->data[i] == '\n') + break; + + if (changes_offset) + { + apr_int64_t val; + + trailer->data[i] = '\0'; + SVN_ERR(svn_cstring_atoi64(&val, str)); + *changes_offset = (apr_off_t)val; + } + + return SVN_NO_ERROR; +} + +svn_stringbuf_t * +svn_fs_fs__unparse_revision_trailer(apr_off_t root_offset, + apr_off_t changes_offset, + apr_pool_t *result_pool) +{ + return svn_stringbuf_createf(result_pool, + "%" APR_OFF_T_FMT " %" APR_OFF_T_FMT "\n", + root_offset, + changes_offset); +} + +svn_error_t * +svn_fs_fs__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + apr_int64_t val; + char *last_str = footer->data; + + /* Get the L2P offset. */ + const char *str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_cstring_atoi64(&val, str)); + *l2p_offset = (apr_off_t)val; + + /* Get the L2P checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, + result_pool)); + + /* Get the P2L offset. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_cstring_atoi64(&val, str)); + *p2l_offset = (apr_off_t)val; + + /* Get the P2L checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, + result_pool)); + + return SVN_NO_ERROR; +} + +svn_stringbuf_t * +svn_fs_fs__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_stringbuf_createf(result_pool, + "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s", + l2p_offset, + svn_checksum_to_cstring(l2p_checksum, + scratch_pool), + p2l_offset, + svn_checksum_to_cstring(p2l_checksum, + scratch_pool)); +} + +/* 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, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *line; + svn_boolean_t eof = TRUE; + change_t *change; + char *str, *last_str, *kind_str; + svn_fs_path_change2_t *info; + + /* Default return value. */ + *change_p = NULL; + + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); + + /* Check for a blank line. */ + if (eof || (line->len == 0)) + return SVN_NO_ERROR; + + change = apr_pcalloc(result_pool, sizeof(*change)); + info = &change->info; + last_str = line->data; + + /* 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")); + + SVN_ERR(svn_fs_fs__id_parse(&info->node_rev_id, str, result_pool)); + if (info->node_rev_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. */ + info->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, SVN_FS_FS__KIND_FILE) == 0) + info->node_kind = svn_node_file; + else if (strcmp(kind_str, SVN_FS_FS__KIND_DIR) == 0) + info->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) + { + info->change_kind = svn_fs_path_change_modify; + } + else if (strcmp(str, ACTION_ADD) == 0) + { + info->change_kind = svn_fs_path_change_add; + } + else if (strcmp(str, ACTION_DELETE) == 0) + { + info->change_kind = svn_fs_path_change_delete; + } + else if (strcmp(str, ACTION_REPLACE) == 0) + { + info->change_kind = svn_fs_path_change_replace; + } + else if (strcmp(str, ACTION_RESET) == 0) + { + info->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) + { + info->text_mod = TRUE; + } + else if (strcmp(str, FLAG_FALSE) == 0) + { + info->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) + { + info->prop_mod = TRUE; + } + else if (strcmp(str, FLAG_FALSE) == 0) + { + info->prop_mod = FALSE; + } + else + { + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid prop-mod flag in rev-file")); + } + + /* Get the mergeinfo-mod flag if given. Otherwise, the next thing + is the path starting with a slash. Also, we must initialize the + flag explicitly because 0 is not valid for a svn_tristate_t. */ + info->mergeinfo_mod = svn_tristate_unknown; + if (*last_str != '/') + { + 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) + { + info->mergeinfo_mod = svn_tristate_true; + } + else if (strcmp(str, FLAG_FALSE) == 0) + { + info->mergeinfo_mod = svn_tristate_false; + } + else + { + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid mergeinfo-mod flag in rev-file")); + } + } + + /* Get the changed path. */ + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid path in changes line")); + + change->path.len = strlen(last_str); + change->path.data = apr_pstrdup(result_pool, last_str); + + /* Read the next line, the copyfrom line. */ + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); + info->copyfrom_known = TRUE; + if (eof || line->len == 0) + { + info->copyfrom_rev = SVN_INVALID_REVNUM; + info->copyfrom_path = NULL; + } + else + { + last_str = line->data; + SVN_ERR(parse_revnum(&info->copyfrom_rev, (const char **)&last_str)); + + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid copy-from path in changes line")); + + info->copyfrom_path = apr_pstrdup(result_pool, last_str); + } + + *change_p = change; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_changes(apr_array_header_t **changes, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + change_t *change; + apr_pool_t *iterpool; + + /* Pre-allocate enough room for most change lists. + (will be auto-expanded as necessary). + + Chose the default to just below 2^N such that the doubling reallocs + will request roughly 2^M bytes from the OS without exceeding the + respective two-power by just a few bytes (leaves room array and APR + node overhead for large enough M). + */ + *changes = apr_array_make(result_pool, 63, sizeof(change_t *)); + + SVN_ERR(read_change(&change, stream, result_pool, scratch_pool)); + iterpool = svn_pool_create(scratch_pool); + while (change) + { + APR_ARRAY_PUSH(*changes, change_t*) = change; + SVN_ERR(read_change(&change, stream, result_pool, iterpool)); + svn_pool_clear(iterpool); + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_changes_incrementally(svn_stream_t *stream, + svn_fs_fs__change_receiver_t + change_receiver, + void *change_receiver_baton, + apr_pool_t *scratch_pool) +{ + change_t *change; + apr_pool_t *iterpool; + + iterpool = svn_pool_create(scratch_pool); + do + { + svn_pool_clear(iterpool); + + SVN_ERR(read_change(&change, stream, iterpool, iterpool)); + if (change) + SVN_ERR(change_receiver(change_receiver_baton, change, iterpool)); + } + while (change); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Write a single change entry, path PATH, change CHANGE, to STREAM. + + Only include the node kind field if INCLUDE_NODE_KIND is true. Only + include the mergeinfo-mod field if INCLUDE_MERGEINFO_MODS is true. + All temporary allocations are in SCRATCH_POOL. */ +static svn_error_t * +write_change_entry(svn_stream_t *stream, + const char *path, + svn_fs_path_change2_t *change, + svn_boolean_t include_node_kind, + svn_boolean_t include_mergeinfo_mods, + apr_pool_t *scratch_pool) +{ + const char *idstr; + const char *change_string = NULL; + const char *kind_string = ""; + const char *mergeinfo_string = ""; + svn_stringbuf_t *buf; + apr_size_t len; + + 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, scratch_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(scratch_pool, "-%s", + change->node_kind == svn_node_dir + ? SVN_FS_FS__KIND_DIR + : SVN_FS_FS__KIND_FILE); + } + + if (include_mergeinfo_mods && change->mergeinfo_mod != svn_tristate_unknown) + mergeinfo_string = apr_psprintf(scratch_pool, " %s", + change->mergeinfo_mod == svn_tristate_true + ? FLAG_TRUE + : FLAG_FALSE); + + buf = svn_stringbuf_createf(scratch_pool, "%s %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, + mergeinfo_string, + path); + + if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) + { + svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s", + change->copyfrom_rev, + change->copyfrom_path)); + } + + svn_stringbuf_appendbyte(buf, '\n'); + + /* Write all change info in one write call. */ + len = buf->len; + return svn_error_trace(svn_stream_write(stream, buf->data, &len)); +} + +svn_error_t * +svn_fs_fs__write_changes(svn_stream_t *stream, + svn_fs_t *fs, + apr_hash_t *changes, + svn_boolean_t terminate_list, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_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; + svn_boolean_t include_mergeinfo_mods = + ffd->format >= SVN_FS_FS__MIN_MERGEINFO_IN_CHANGED_FORMAT; + apr_array_header_t *sorted_changed_paths; + int i; + + /* 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. + + Also, this sorting is only effective in writing all entries with + a single call as write_final_changed_path_info() does. For the + list being written incrementally during transaction, we actually + *must not* change the order of entries from different calls. + */ + sorted_changed_paths = svn_sort__hash(changes, + svn_sort_compare_items_lexically, + scratch_pool); + + /* Write all items to disk in the new order. */ + for (i = 0; i < sorted_changed_paths->nelts; ++i) + { + 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; + + /* Write out the new entry into the final rev-file. */ + SVN_ERR(write_change_entry(stream, path, change, include_node_kinds, + include_mergeinfo_mods, iterpool)); + } + + if (terminate_list) + svn_stream_puts(stream, "\n"); + + svn_pool_destroy(iterpool); + + 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 + RESULT_POOL. */ +static svn_error_t * +read_header_block(apr_hash_t **headers, + svn_stream_t *stream, + apr_pool_t *result_pool) +{ + *headers = svn_hash__make(result_pool); + + while (1) + { + svn_stringbuf_t *header_str; + const char *name, *value; + apr_size_t i = 0; + apr_size_t name_len; + svn_boolean_t eof; + + SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, + result_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; + name_len = i; + + /* Check if we have enough data to parse. */ + if (i + 2 > header_str->len) + { + /* Restore the original line for the error. */ + header_str->data[i] = ':'; + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Found malformed header '%s' in " + "revision file"), + header_str->data); + } + + /* Skip over the NULL byte and the space following it. */ + i += 2; + + value = header_str->data + i; + + /* header_str is safely in our pool, so we can use bits of it as + key and value. */ + apr_hash_set(*headers, name, name_len, value); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__parse_representation(representation_t **rep_p, + svn_stringbuf_t *text, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + representation_t *rep; + char *str; + apr_int64_t val; + char *string = text->data; + svn_checksum_t *checksum; + const char *end; + + rep = apr_pcalloc(result_pool, sizeof(*rep)); + *rep_p = rep; + + SVN_ERR(parse_revnum(&rep->revision, (const char **)&string)); + + /* initialize transaction info (never stored) */ + svn_fs_fs__id_txn_reset(&rep->txn_id); + + /* while in transactions, it is legal to simply write "-1" */ + str = svn_cstring_tokenize(" ", &string); + if (str == NULL) + { + if (rep->revision == SVN_INVALID_REVNUM) + return SVN_NO_ERROR; + + 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->item_index = (apr_uint64_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(&checksum, svn_checksum_md5, str, + scratch_pool)); + memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest)); + + /* 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(&checksum, svn_checksum_sha1, str, + scratch_pool)); + rep->has_sha1 = checksum != NULL; + memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); + + /* 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")); + + SVN_ERR(svn_fs_fs__id_txn_parse(&rep->uniquifier.noderev_txn_id, str)); + + str = svn_cstring_tokenize(" ", &string); + if (str == NULL || *str != '_') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed text representation offset line in node-rev")); + + ++str; + rep->uniquifier.number = svn__base36toui64(&end, str); + + if (*end) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed text representation offset line in node-rev")); + + return SVN_NO_ERROR; +} + +/* Wrap svn_fs_fs__parse_representation(), 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, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err + = svn_fs_fs__parse_representation(rep_p, + svn_stringbuf_create_wrap(string, + scratch_pool), + result_pool, + scratch_pool); + if (err) + { + const svn_string_t *id_unparsed; + const char *where; + + id_unparsed = svn_fs_fs__id_unparse(noderev_id, scratch_pool); + where = apr_psprintf(scratch_pool, + _("While reading representation offsets " + "for node-revision '%s':"), + noderev_id ? id_unparsed->data : "(null)"); + + return svn_error_quick_wrap(err, where); + } + + if ((*rep_p)->revision == SVN_INVALID_REVNUM) + if (noderev_id) + (*rep_p)->txn_id = *svn_fs_fs__id_txn_id(noderev_id); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_noderev(node_revision_t **noderev_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *headers; + node_revision_t *noderev; + char *value; + const char *noderev_id; + + SVN_ERR(read_header_block(&headers, stream, scratch_pool)); + + noderev = apr_pcalloc(result_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)); + + SVN_ERR(svn_fs_fs__id_parse(&noderev->id, value, result_pool)); + noderev_id = value; /* for error messages later */ + + /* Read the type. */ + value = svn_hash_gets(headers, HEADER_TYPE); + + if ((value == NULL) || + ( strcmp(value, SVN_FS_FS__KIND_FILE) + && strcmp(value, SVN_FS_FS__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, SVN_FS_FS__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, result_pool, scratch_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, result_pool, scratch_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 + { + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Non-canonical cpath field in node-rev '%s'"), + noderev_id); + + noderev->created_path = apr_pstrdup(result_pool, value); + } + + /* Get the predecessor ID. */ + value = svn_hash_gets(headers, HEADER_PRED); + if (value) + SVN_ERR(svn_fs_fs__id_parse(&noderev->predecessor_id, value, + result_pool)); + + /* Get the copyroot. */ + value = svn_hash_gets(headers, HEADER_COPYROOT); + if (value == NULL) + { + noderev->copyroot_path = apr_pstrdup(result_pool, noderev->created_path); + noderev->copyroot_rev = svn_fs_fs__id_rev(noderev->id); + } + else + { + SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value)); + + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed copyroot line in node-rev '%s'"), + noderev_id); + noderev->copyroot_path = apr_pstrdup(result_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 + { + SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value)); + + 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(result_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; +} + +/* Return a textual representation of the DIGEST of given KIND. + * If IS_NULL is TRUE, no digest is available. + * Allocate the result in RESULT_POOL. + */ +static const char * +format_digest(const unsigned char *digest, + svn_checksum_kind_t kind, + svn_boolean_t is_null, + apr_pool_t *result_pool) +{ + svn_checksum_t checksum; + checksum.digest = digest; + checksum.kind = kind; + + if (is_null) + return "(null)"; + + return svn_checksum_to_cstring_display(&checksum, result_pool); +} + +svn_stringbuf_t * +svn_fs_fs__unparse_representation(representation_t *rep, + int format, + svn_boolean_t mutable_rep_truncated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + if (svn_fs_fs__id_txn_used(&rep->txn_id) && mutable_rep_truncated) + return svn_stringbuf_ncreate("-1", 2, result_pool); + + if (format < SVN_FS_FS__MIN_REP_SHARING_FORMAT || !rep->has_sha1) + return svn_stringbuf_createf + (result_pool, "%ld %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT + " %" SVN_FILESIZE_T_FMT " %s", + rep->revision, rep->item_index, rep->size, + rep->expanded_size, + format_digest(rep->md5_digest, svn_checksum_md5, FALSE, + scratch_pool)); + + svn__ui64tobase36(buffer, rep->uniquifier.number); + return svn_stringbuf_createf + (result_pool, "%ld %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT + " %" SVN_FILESIZE_T_FMT " %s %s %s/_%s", + rep->revision, rep->item_index, rep->size, + rep->expanded_size, + format_digest(rep->md5_digest, svn_checksum_md5, + FALSE, scratch_pool), + format_digest(rep->sha1_digest, svn_checksum_sha1, + !rep->has_sha1, scratch_pool), + svn_fs_fs__id_txn_unparse(&rep->uniquifier.noderev_txn_id, + scratch_pool), + buffer); +} + + +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 *scratch_pool) +{ + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", + svn_fs_fs__id_unparse(noderev->id, + scratch_pool)->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", + (noderev->kind == svn_node_file) ? + SVN_FS_FS__KIND_FILE : SVN_FS_FS__KIND_DIR)); + + if (noderev->predecessor_id) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", + svn_fs_fs__id_unparse(noderev->predecessor_id, + scratch_pool)->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", + noderev->predecessor_count)); + + if (noderev->data_rep) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", + svn_fs_fs__unparse_representation + (noderev->data_rep, + format, + noderev->kind == svn_node_dir, + scratch_pool, scratch_pool)->data)); + + if (noderev->prop_rep) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", + svn_fs_fs__unparse_representation + (noderev->prop_rep, format, + TRUE, scratch_pool, scratch_pool)->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", + noderev->created_path)); + + if (noderev->copyfrom_path) + SVN_ERR(svn_stream_printf(outfile, scratch_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, scratch_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, scratch_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__read_rep_header(svn_fs_fs__rep_header_t **header, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *buffer; + char *str, *last_str; + apr_int64_t val; + svn_boolean_t eol = FALSE; + + SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool)); + + *header = apr_pcalloc(result_pool, sizeof(**header)); + (*header)->header_size = buffer->len + 1; + if (strcmp(buffer->data, REP_PLAIN) == 0) + { + (*header)->type = svn_fs_fs__rep_plain; + return SVN_NO_ERROR; + } + + if (strcmp(buffer->data, REP_DELTA) == 0) + { + /* This is a delta against the empty stream. */ + (*header)->type = svn_fs_fs__rep_self_delta; + return SVN_NO_ERROR; + } + + (*header)->type = svn_fs_fs__rep_delta; + + /* We have hopefully a DELTA vs. a non-empty base revision. */ + last_str = buffer->data; + str = svn_cstring_tokenize(" ", &last_str); + if (! str || (strcmp(str, REP_DELTA) != 0)) + goto error; + + SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str)); + + str = svn_cstring_tokenize(" ", &last_str); + if (! str) + goto error; + SVN_ERR(svn_cstring_atoi64(&val, str)); + (*header)->base_item_index = (apr_off_t)val; + + str = svn_cstring_tokenize(" ", &last_str); + if (! str) + goto error; + SVN_ERR(svn_cstring_atoi64(&val, str)); + (*header)->base_length = (svn_filesize_t)val; + + return SVN_NO_ERROR; + + error: + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed representation header")); +} + +svn_error_t * +svn_fs_fs__write_rep_header(svn_fs_fs__rep_header_t *header, + svn_stream_t *stream, + apr_pool_t *scratch_pool) +{ + const char *text; + + switch (header->type) + { + case svn_fs_fs__rep_plain: + text = REP_PLAIN "\n"; + break; + + case svn_fs_fs__rep_self_delta: + text = REP_DELTA "\n"; + break; + + default: + text = apr_psprintf(scratch_pool, REP_DELTA " %ld %" APR_OFF_T_FMT + " %" SVN_FILESIZE_T_FMT "\n", + header->base_revision, header->base_item_index, + header->base_length); + } + + return svn_error_trace(svn_stream_puts(stream, text)); +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/low_level.h b/contrib/subversion/subversion/libsvn_fs_fs/low_level.h new file mode 100644 index 000000000..35b9d0da5 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/low_level.h @@ -0,0 +1,230 @@ +/* low_level.c --- low level r/w access to fs_fs file structures + * + * ==================================================================== + * 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_fs.h" + +#include "fs_fs.h" +#include "id.h" + +/* Kinds that a node-rev can be. */ +#define SVN_FS_FS__KIND_FILE "file" +#define SVN_FS_FS__KIND_DIR "dir" + +/* The functions are grouped as follows: + * + * - revision trailer (up to format 6) + * - revision footer (since format 7) + * - changed path list + * - node revision + * - representation (as in "text:" and "props:" lines) + * - representation header ("PLAIN" and "DELTA" lines) + */ + +/* Given the last "few" bytes (should be at least 40) of revision REV in + * TRAILER, parse the last line and return the offset of the root noderev + * in *ROOT_OFFSET and the offset of the changed paths list in + * *CHANGES_OFFSET. Offsets are relative to the revision's start offset. + * ROOT_OFFSET and / or CHANGES_OFFSET may be NULL. + * + * Note that REV is only used to construct nicer error objects. + */ +svn_error_t * +svn_fs_fs__parse_revision_trailer(apr_off_t *root_offset, + apr_off_t *changes_offset, + svn_stringbuf_t *trailer, + svn_revnum_t rev); + +/* Given the offset of the root noderev in ROOT_OFFSET and the offset of + * the changed paths list in CHANGES_OFFSET, return the corresponding + * revision's trailer. Allocate it in RESULT_POOL. + */ +svn_stringbuf_t * +svn_fs_fs__unparse_revision_trailer(apr_off_t root_offset, + apr_off_t changes_offset, + apr_pool_t *result_pool); + +/* Given the format 7+ revision / pack FOOTER, parse it destructively + * and return the start offsets of the index data in *L2P_OFFSET and + * *P2L_OFFSET, respectively. Also, return the expected checksums in + * in *L2P_CHECKSUM and *P2L_CHECKSUM. + * + * Note that REV is only used to construct nicer error objects that + * mention this revision. Allocate the checksums in RESULT_POOL. + */ +svn_error_t * +svn_fs_fs__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Given the offset of the L2P index data in L2P_OFFSET, the content + * checksum in L2P_CHECKSUM and the offset plus checksum of the P2L + * index data in P2L_OFFSET and P2L_CHECKSUM. + * + * Return the corresponding format 7+ revision / pack file footer. + * Allocate it in RESULT_POOL and use SCRATCH_POOL for temporary. + */ +svn_stringbuf_t * +svn_fs_fs__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Read all the changes from STREAM and store them in *CHANGES, + allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_fs__read_changes(apr_array_header_t **changes, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Callback function used by svn_fs_fs__read_changes_incrementally(), + * asking the receiver to process to process CHANGE using BATON. CHANGE + * and SCRATCH_POOL will not be valid beyond the current callback invocation. + */ +typedef svn_error_t *(*svn_fs_fs__change_receiver_t)( + void *baton, + change_t *change, + apr_pool_t *scratch_pool); + +/* Read all the changes from STREAM and invoke CHANGE_RECEIVER on each change. + Do all allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_fs__read_changes_incrementally(svn_stream_t *stream, + svn_fs_fs__change_receiver_t + change_receiver, + void *change_receiver_baton, + apr_pool_t *scratch_pool); + +/* Write the changed path info from CHANGES in filesystem FS to the + output stream STREAM. You may call this function multiple time on + the same stream. If you are writing to a (proto-)revision file, + the last call must set TERMINATE_LIST to write an extra empty line + that marks the end of the changed paths list. + Perform temporary allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_fs__write_changes(svn_stream_t *stream, + svn_fs_t *fs, + apr_hash_t *changes, + svn_boolean_t terminate_list, + apr_pool_t *scratch_pool); + +/* Read a node-revision from STREAM. Set *NODEREV to the new structure, + allocated in RESULT_POOL. */ +svn_error_t * +svn_fs_fs__read_noderev(node_revision_t **noderev, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Write the node-revision NODEREV into the stream OUTFILE, compatible with + filesystem format FORMAT. Only write mergeinfo-related metadata if + INCLUDE_MERGEINFO is true. Temporary allocations are from SCRATCH_POOL. */ +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 *scratch_pool); + +/* Parse the description of a representation from TEXT and store it + into *REP_P. TEXT will be invalidated by this call. Allocate *REP_P in + RESULT_POOL and use SCRATCH_POOL for temporaries. */ +svn_error_t * +svn_fs_fs__parse_representation(representation_t **rep_p, + svn_stringbuf_t *text, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* 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. + Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ +svn_stringbuf_t * +svn_fs_fs__unparse_representation(representation_t *rep, + int format, + svn_boolean_t mutable_rep_truncated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* This type enumerates all forms of representations that we support. */ +typedef enum svn_fs_fs__rep_type_t +{ + /* this is a PLAIN representation */ + svn_fs_fs__rep_plain, + + /* this is a DELTA representation with no base representation */ + svn_fs_fs__rep_self_delta, + + /* this is a DELTA representation against some base representation */ + svn_fs_fs__rep_delta +} svn_fs_fs__rep_type_t; + +/* This structure is used to hold the information stored in a representation + * header. */ +typedef struct svn_fs_fs__rep_header_t +{ + /* type of the representation, i.e. whether it is PLAIN, self-DELTA etc. */ + svn_fs_fs__rep_type_t type; + + /* if this rep is a delta against some other rep, that base rep can + * be found in this revision. Should be 0 if there is no base rep. */ + svn_revnum_t base_revision; + + /* if this rep is a delta against some other rep, that base rep can + * be found at this item index within the base rep's revision. Should + * be 0 if there is no base rep. */ + apr_off_t base_item_index; + + /* if this rep is a delta against some other rep, this is the (deltified) + * size of that base rep. Should be 0 if there is no base rep. */ + svn_filesize_t base_length; + + /* length of the textual representation of the header in the rep or pack + * file, including EOL. Only valid after reading it from disk. + * Should be 0 otherwise. */ + apr_size_t header_size; +} svn_fs_fs__rep_header_t; + +/* Read the next line from STREAM and parse it as a text + representation header. Return the parsed entry in *HEADER, allocated + in RESULT_POOL. Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_fs__read_rep_header(svn_fs_fs__rep_header_t **header, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Write the representation HEADER to STREAM. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__write_rep_header(svn_fs_fs__rep_header_t *header, + svn_stream_t *stream, + apr_pool_t *scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_fs_fs/pack.c b/contrib/subversion/subversion/libsvn_fs_fs/pack.c new file mode 100644 index 000000000..50b8898c4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/pack.c @@ -0,0 +1,2039 @@ +/* pack.c --- FSFS shard packing 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. + * ==================================================================== + */ +#include +#include + +#include "svn_pools.h" +#include "svn_dirent_uri.h" +#include "svn_sorts.h" +#include "private/svn_temp_serializer.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "private/svn_io_private.h" + +#include "fs_fs.h" +#include "pack.h" +#include "util.h" +#include "id.h" +#include "index.h" +#include "low_level.h" +#include "revprops.h" +#include "transaction.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" +#include "temp_serializer.h" + +/* Logical addressing packing logic: + * + * We pack files on a pack file basis (e.g. 1000 revs) without changing + * existing pack files nor the revision files outside the range to pack. + * + * First, we will scan the revision file indexes to determine the number + * of items to "place" (i.e. determine their optimal position within the + * future pack file). For each item, we will need a constant amount of + * memory to track it. A MAX_MEM parameter sets a limit to the number of + * items we may place in one go. That means, we may not be able to add + * all revisions at once. Instead, we will run the placement for a subset + * of revisions at a time. The very unlikely worst case will simply append + * all revision data with just a little reshuffling inside each revision. + * + * In a second step, we read all revisions in the selected range, build + * the item tracking information and copy the items themselves from the + * revision files to temporary files. The latter serve as buckets for a + * very coarse bucket presort: Separate change lists, file properties, + * directory properties and noderevs + representations from one another. + * + * The third step will determine an optimized placement for the items in + * each of the 4 buckets separately. The first three will simply order + * their items by revision, starting with the newest once. Placing rep + * and noderev items is a more elaborate process documented in the code. + * + * In short, we store items in the following order: + * - changed paths lists + * - node property + * - directory properties + * - directory representations corresponding noderevs, lexical path order + * with special treatment of "trunk" and "branches" + * - same for file representations + * + * Step 4 copies the items from the temporary buckets into the final + * pack file and writes the temporary index files. + * + * Finally, after the last range of revisions, create the final indexes. + */ + +/* Maximum amount of memory we allocate for placement information during + * the pack process. + */ +#define DEFAULT_MAX_MEM (64 * 1024 * 1024) + +/* Data structure describing a node change at PATH, REVISION. + * We will sort these instances by PATH and NODE_ID such that we can combine + * similar nodes in the same reps container and store containers in path + * major order. + */ +typedef struct path_order_t +{ + /* changed path */ + svn_prefix_string__t *path; + + /* node ID for this PATH in REVISION */ + svn_fs_fs__id_part_t node_id; + + /* when this change happened */ + svn_revnum_t revision; + + /* noderev predecessor count */ + int predecessor_count; + + /* this is a directory node */ + svn_boolean_t is_dir; + + /* length of the expanded representation content */ + apr_int64_t expanded_size; + + /* item ID of the noderev linked to the change. May be (0, 0). */ + svn_fs_fs__id_part_t noderev_id; + + /* item ID of the representation containing the new data. May be (0, 0). */ + svn_fs_fs__id_part_t rep_id; +} path_order_t; + +/* Represents a reference from item FROM to item TO. FROM may be a noderev + * or rep_id while TO is (currently) always a representation. We will sort + * them by TO which allows us to collect all dependent items. + */ +typedef struct reference_t +{ + svn_fs_fs__id_part_t to; + svn_fs_fs__id_part_t from; +} reference_t; + +/* This structure keeps track of all the temporary data and status that + * needs to be kept around during the creation of one pack file. After + * each revision range (in case we can't process all revs at once due to + * memory restrictions), parts of the data will get re-initialized. + */ +typedef struct pack_context_t +{ + /* file system that we operate on */ + svn_fs_t *fs; + + /* cancel function to invoke at regular intervals. May be NULL */ + svn_cancel_func_t cancel_func; + + /* baton to pass to CANCEL_FUNC */ + void *cancel_baton; + + /* first revision in the shard (and future pack file) */ + svn_revnum_t shard_rev; + + /* first revision in the range to process (>= SHARD_REV) */ + svn_revnum_t start_rev; + + /* first revision after the range to process (<= SHARD_END_REV) */ + svn_revnum_t end_rev; + + /* first revision after the current shard */ + svn_revnum_t shard_end_rev; + + /* log-to-phys proto index for the whole pack file */ + apr_file_t *proto_l2p_index; + + /* phys-to-log proto index for the whole pack file */ + apr_file_t *proto_p2l_index; + + /* full shard directory path (containing the unpacked revisions) */ + const char *shard_dir; + + /* full packed shard directory path (containing the pack file + indexes) */ + const char *pack_file_dir; + + /* full pack file path (including PACK_FILE_DIR) */ + const char *pack_file_path; + + /* current write position (i.e. file length) in the pack file */ + apr_off_t pack_offset; + + /* the pack file to ultimately write all data to */ + apr_file_t *pack_file; + + /* array of svn_fs_fs__p2l_entry_t *, all referring to change lists. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *changes; + + /* temp file receiving all change list items (referenced by CHANGES). + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_file_t *changes_file; + + /* array of svn_fs_fs__p2l_entry_t *, all referring to file properties. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *file_props; + + /* temp file receiving all file prop items (referenced by FILE_PROPS). + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *file_props_file; + + /* array of svn_fs_fs__p2l_entry_t *, all referring to directory properties. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *dir_props; + + /* temp file receiving all directory prop items (referenced by DIR_PROPS). + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *dir_props_file; + + /* container for all PATH members in PATH_ORDER. */ + svn_prefix_tree__t *paths; + + /* array of path_order_t *. Will be filled in phase 2 and be cleared + * after each revision range. Sorted by PATH, NODE_ID. */ + apr_array_header_t *path_order; + + /* array of reference_t* linking representations to their delta bases. + * Will be filled in phase 2 and be cleared after each revision range. + * It will be sorted by the FROM members (for rep->base rep lookup). */ + apr_array_header_t *references; + + /* array of svn_fs_fs__p2l_entry_t*. Will be filled in phase 2 and be + * cleared after each revision range. During phase 3, we will set items + * to NULL that we already processed. */ + apr_array_header_t *reps; + + /* array of int, marking for each revision, the which offset their items + * begin in REPS. Will be filled in phase 2 and be cleared after + * each revision range. */ + apr_array_header_t *rev_offsets; + + /* temp file receiving all items referenced by REPS. + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *reps_file; + + /* pool used for temporary data structures that will be cleaned up when + * the next range of revisions is being processed */ + apr_pool_t *info_pool; +} pack_context_t; + +/* Create and initialize a new pack context for packing shard SHARD_REV in + * SHARD_DIR into PACK_FILE_DIR within filesystem FS. Allocate it in POOL + * and return the structure in *CONTEXT. + * + * Limit the number of items being copied per iteration to MAX_ITEMS. + * Set CANCEL_FUNC and CANCEL_BATON as well. + */ +static svn_error_t * +initialize_pack_context(pack_context_t *context, + svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_dir, + svn_revnum_t shard_rev, + int max_items, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + const char *temp_dir; + int max_revs = MIN(ffd->max_files_per_dir, max_items); + + SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT); + SVN_ERR_ASSERT(shard_rev % ffd->max_files_per_dir == 0); + + /* where we will place our various temp files */ + SVN_ERR(svn_io_temp_dir(&temp_dir, pool)); + + /* store parameters */ + context->fs = fs; + context->cancel_func = cancel_func; + context->cancel_baton = cancel_baton; + + context->shard_rev = shard_rev; + context->start_rev = shard_rev; + context->end_rev = shard_rev; + context->shard_end_rev = shard_rev + ffd->max_files_per_dir; + + /* Create the new directory and pack file. */ + context->shard_dir = shard_dir; + context->pack_file_dir = pack_file_dir; + context->pack_file_path + = svn_dirent_join(pack_file_dir, PATH_PACKED, pool); + SVN_ERR(svn_io_file_open(&context->pack_file, context->pack_file_path, + APR_WRITE | APR_BUFFERED | APR_BINARY | APR_EXCL + | APR_CREATE, APR_OS_DEFAULT, pool)); + + /* Proto index files */ + SVN_ERR(svn_fs_fs__l2p_proto_index_open( + &context->proto_l2p_index, + svn_dirent_join(pack_file_dir, + PATH_INDEX PATH_EXT_L2P_INDEX, + pool), + pool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_open( + &context->proto_p2l_index, + svn_dirent_join(pack_file_dir, + PATH_INDEX PATH_EXT_P2L_INDEX, + pool), + pool)); + + /* item buckets: one item info array and one temp file per bucket */ + context->changes = apr_array_make(pool, max_items, + sizeof(svn_fs_fs__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->changes_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + context->file_props = apr_array_make(pool, max_items, + sizeof(svn_fs_fs__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->file_props_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + context->dir_props = apr_array_make(pool, max_items, + sizeof(svn_fs_fs__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->dir_props_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + + /* noderev and representation item bucket */ + context->rev_offsets = apr_array_make(pool, max_revs, sizeof(int)); + context->path_order = apr_array_make(pool, max_items, + sizeof(path_order_t *)); + context->references = apr_array_make(pool, max_items, + sizeof(reference_t *)); + context->reps = apr_array_make(pool, max_items, + sizeof(svn_fs_fs__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->reps_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + + /* the pool used for temp structures */ + context->info_pool = svn_pool_create(pool); + context->paths = svn_prefix_tree__create(context->info_pool); + + return SVN_NO_ERROR; +} + +/* Clean up / free all revision range specific data and files in CONTEXT. + * Use POOL for temporary allocations. + */ +static svn_error_t * +reset_pack_context(pack_context_t *context, + apr_pool_t *pool) +{ + apr_array_clear(context->changes); + SVN_ERR(svn_io_file_trunc(context->changes_file, 0, pool)); + apr_array_clear(context->file_props); + SVN_ERR(svn_io_file_trunc(context->file_props_file, 0, pool)); + apr_array_clear(context->dir_props); + SVN_ERR(svn_io_file_trunc(context->dir_props_file, 0, pool)); + + apr_array_clear(context->rev_offsets); + apr_array_clear(context->path_order); + apr_array_clear(context->references); + apr_array_clear(context->reps); + SVN_ERR(svn_io_file_trunc(context->reps_file, 0, pool)); + + svn_pool_clear(context->info_pool); + + return SVN_NO_ERROR; +} + +/* Call this after the last revision range. It will finalize all index files + * for CONTEXT and close any open files. Use POOL for temporary allocations. + */ +static svn_error_t * +close_pack_context(pack_context_t *context, + apr_pool_t *pool) +{ + const char *proto_l2p_index_path; + const char *proto_p2l_index_path; + + /* need the file names for the actual index creation call further down */ + SVN_ERR(svn_io_file_name_get(&proto_l2p_index_path, + context->proto_l2p_index, pool)); + SVN_ERR(svn_io_file_name_get(&proto_p2l_index_path, + context->proto_p2l_index, pool)); + + /* finalize proto index files */ + SVN_ERR(svn_io_file_close(context->proto_l2p_index, pool)); + SVN_ERR(svn_io_file_close(context->proto_p2l_index, pool)); + + /* Append the actual index data to the pack file. */ + SVN_ERR(svn_fs_fs__add_index_data(context->fs, context->pack_file, + proto_l2p_index_path, + proto_p2l_index_path, + context->shard_rev, + pool)); + + /* remove proto index files */ + SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool)); + SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, pool)); + + /* Ensure that packed file is written to disk.*/ + SVN_ERR(svn_io_file_flush_to_disk(context->pack_file, pool)); + SVN_ERR(svn_io_file_close(context->pack_file, pool)); + + return SVN_NO_ERROR; +} + +/* Efficiently copy SIZE bytes from SOURCE to DEST. Invoke the CANCEL_FUNC + * from CONTEXT at regular intervals. Use POOL for allocations. + */ +static svn_error_t * +copy_file_data(pack_context_t *context, + apr_file_t *dest, + apr_file_t *source, + apr_off_t size, + apr_pool_t *pool) +{ + /* most non-representation items will be small. Minimize the buffer + * and infrastructure overhead in that case. */ + enum { STACK_BUFFER_SIZE = 1024 }; + + if (size < STACK_BUFFER_SIZE) + { + /* copy small data using a fixed-size buffer on stack */ + char buffer[STACK_BUFFER_SIZE]; + SVN_ERR(svn_io_file_read_full2(source, buffer, (apr_size_t)size, + NULL, NULL, pool)); + SVN_ERR(svn_io_file_write_full(dest, buffer, (apr_size_t)size, + NULL, pool)); + } + else + { + /* use streaming copies for larger data blocks. That may require + * the allocation of larger buffers and we should make sure that + * this extra memory is released asap. */ + fs_fs_data_t *ffd = context->fs->fsap_data; + apr_pool_t *copypool = svn_pool_create(pool); + char *buffer = apr_palloc(copypool, ffd->block_size); + + while (size) + { + apr_size_t to_copy = (apr_size_t)(MIN(size, ffd->block_size)); + if (context->cancel_func) + SVN_ERR(context->cancel_func(context->cancel_baton)); + + SVN_ERR(svn_io_file_read_full2(source, buffer, to_copy, + NULL, NULL, pool)); + SVN_ERR(svn_io_file_write_full(dest, buffer, to_copy, + NULL, pool)); + + size -= to_copy; + } + + svn_pool_destroy(copypool); + } + + return SVN_NO_ERROR; +} + +/* Writes SIZE bytes, all 0, to DEST. Uses POOL for allocations. + */ +static svn_error_t * +write_null_bytes(apr_file_t *dest, + apr_off_t size, + apr_pool_t *pool) +{ + /* Have a collection of high-quality, easy to access NUL bytes handy. */ + enum { BUFFER_SIZE = 1024 }; + static const char buffer[BUFFER_SIZE] = { 0 }; + + /* copy SIZE of them into the file's buffer */ + while (size) + { + apr_size_t to_write = MIN(size, BUFFER_SIZE); + SVN_ERR(svn_io_file_write_full(dest, buffer, to_write, NULL, pool)); + size -= to_write; + } + + return SVN_NO_ERROR; +} + +/* Copy the "simple" item (changed paths list or property representation) + * from the current position in REV_FILE to TEMP_FILE using CONTEXT. Add + * a copy of ENTRY to ENTRIES but with an updated offset value that points + * to the copy destination in TEMP_FILE. Use POOL for allocations. + */ +static svn_error_t * +copy_item_to_temp(pack_context_t *context, + apr_array_header_t *entries, + apr_file_t *temp_file, + apr_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + svn_fs_fs__p2l_entry_t *new_entry + = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); + + SVN_ERR(svn_fs_fs__get_file_offset(&new_entry->offset, temp_file, pool)); + APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = new_entry; + + SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool)); + + return SVN_NO_ERROR; +} + +/* Return the offset within CONTEXT->REPS that corresponds to item + * ITEM_INDEX in REVISION. + */ +static int +get_item_array_index(pack_context_t *context, + svn_revnum_t revision, + apr_int64_t item_index) +{ + assert(revision >= context->start_rev); + return (int)item_index + APR_ARRAY_IDX(context->rev_offsets, + revision - context->start_rev, + int); +} + +/* Write INFO to the correct position in CONTEXT->REP_INFOS. The latter + * may need auto-expanding. Overwriting an array element is not allowed. + */ +static void +add_item_rep_mapping(pack_context_t *context, + svn_fs_fs__p2l_entry_t *entry) +{ + int idx; + + /* index of INFO */ + idx = get_item_array_index(context, + entry->item.revision, + entry->item.number); + + /* make sure the index exists in the array */ + while (context->reps->nelts <= idx) + APR_ARRAY_PUSH(context->reps, void *) = NULL; + + /* set the element. If there is already an entry, there are probably + * two items claiming to be the same -> bail out */ + assert(!APR_ARRAY_IDX(context->reps, idx, void *)); + APR_ARRAY_IDX(context->reps, idx, void *) = entry; +} + +/* Return the P2L entry from CONTEXT->REPS for the given ID. If there is + * none (or not anymore), return NULL. If RESET has been specified, set + * the array entry to NULL after returning the entry. + */ +static svn_fs_fs__p2l_entry_t * +get_item(pack_context_t *context, + const svn_fs_fs__id_part_t *id, + svn_boolean_t reset) +{ + svn_fs_fs__p2l_entry_t *result = NULL; + if (id->number && id->revision >= context->start_rev) + { + int idx = get_item_array_index(context, id->revision, id->number); + if (context->reps->nelts > idx) + { + result = APR_ARRAY_IDX(context->reps, idx, void *); + if (result && reset) + APR_ARRAY_IDX(context->reps, idx, void *) = NULL; + } + } + + return result; +} + +/* Copy representation item identified by ENTRY from the current position + * in REV_FILE into CONTEXT->REPS_FILE. Add all tracking into needed by + * our placement algorithm to CONTEXT. Use POOL for temporary allocations. + */ +static svn_error_t * +copy_rep_to_temp(pack_context_t *context, + apr_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + svn_fs_fs__rep_header_t *rep_header; + svn_stream_t *stream; + apr_off_t source_offset = entry->offset; + + /* create a copy of ENTRY, make it point to the copy destination and + * store it in CONTEXT */ + entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); + SVN_ERR(svn_fs_fs__get_file_offset(&entry->offset, context->reps_file, pool)); + add_item_rep_mapping(context, entry); + + /* read & parse the representation header */ + stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); + SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, stream, pool, pool)); + svn_stream_close(stream); + + /* if the representation is a delta against some other rep, link the two */ + if ( rep_header->type == svn_fs_fs__rep_delta + && rep_header->base_revision >= context->start_rev) + { + reference_t *reference = apr_pcalloc(context->info_pool, + sizeof(*reference)); + reference->from = entry->item; + reference->to.revision = rep_header->base_revision; + reference->to.number = rep_header->base_item_index; + APR_ARRAY_PUSH(context->references, reference_t *) = reference; + } + + /* copy the whole rep (including header!) to our temp file */ + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, + pool)); + + return SVN_NO_ERROR; +} + +/* Directories first, dirs / files sorted by name in reverse lexical order. + * This maximizes the chance of two items being located close to one another + * in *all* pack files independent of their change order. It also groups + * multi-project repos nicely according to their sub-projects. The reverse + * order aspect gives "trunk" preference over "tags" and "branches", so + * trunk-related items are more likely to be contiguous. + */ +static int +compare_dir_entries_format7(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value; + const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value; + + if (lhs->kind != rhs->kind) + return lhs->kind == svn_node_dir ? -1 : 1; + + return strcmp(lhs->name, rhs->name); +} + +/* Directories entries sorted by revision (decreasing - to max cache hits) + * and offset (increasing - to max benefit from APR file buffering). + */ +static int +compare_dir_entries_format6(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value; + const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value; + + const svn_fs_fs__id_part_t *lhs_rev_item + = svn_fs_fs__id_rev_item(lhs->id); + const svn_fs_fs__id_part_t *rhs_rev_item + = svn_fs_fs__id_rev_item(rhs->id); + + /* decreasing ("reverse") order on revs */ + if (lhs_rev_item->revision != rhs_rev_item->revision) + return lhs_rev_item->revision > rhs_rev_item->revision ? -1 : 1; + + /* increasing order on offsets */ + if (lhs_rev_item->number != rhs_rev_item->number) + return lhs_rev_item->number > rhs_rev_item->number ? 1 : -1; + + return 0; +} + +apr_array_header_t * +svn_fs_fs__order_dir_entries(svn_fs_t *fs, + apr_hash_t *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *ordered + = svn_sort__hash(directory, + svn_fs_fs__use_log_addressing(fs) + ? compare_dir_entries_format7 + : compare_dir_entries_format6, + scratch_pool); + + apr_array_header_t *result + = apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *)); + + int i; + for (i = 0; i < ordered->nelts; ++i) + APR_ARRAY_PUSH(result, svn_fs_dirent_t *) + = APR_ARRAY_IDX(ordered, i, svn_sort__item_t).value; + + return result; +} + +/* Return a duplicate of the the ORIGINAL path and with special sub-strins + * (e.g. "trunk") modified in such a way that have a lower lexicographic + * value than any other "normal" file name. + */ +static const char * +tweak_path_for_ordering(const char *original, + apr_pool_t *pool) +{ + /* We may add further special cases as needed. */ + enum {SPECIAL_COUNT = 2}; + static const char *special[SPECIAL_COUNT] = {"trunk", "branch"}; + char *pos; + char *path = apr_pstrdup(pool, original); + int i; + + /* Replace the first char of any "special" sub-string we find by + * a control char, i.e. '\1' .. '\31'. In the rare event that this + * would clash with existing paths, no data will be lost but merely + * the node ordering will be sub-optimal. + */ + for (i = 0; i < SPECIAL_COUNT; ++i) + for (pos = strstr(path, special[i]); + pos; + pos = strstr(pos + 1, special[i])) + { + *pos = (char)(i + '\1'); + } + + return path; +} + +/* Copy node revision item identified by ENTRY from the current position + * in REV_FILE into CONTEXT->REPS_FILE. Add all tracking into needed by + * our placement algorithm to CONTEXT. Use POOL for temporary allocations. + */ +static svn_error_t * +copy_node_to_temp(pack_context_t *context, + apr_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + path_order_t *path_order = apr_pcalloc(context->info_pool, + sizeof(*path_order)); + node_revision_t *noderev; + const char *sort_path; + svn_stream_t *stream; + apr_off_t source_offset = entry->offset; + + /* read & parse noderev */ + stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); + SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool, pool)); + svn_stream_close(stream); + + /* create a copy of ENTRY, make it point to the copy destination and + * store it in CONTEXT */ + entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); + SVN_ERR(svn_fs_fs__get_file_offset(&entry->offset, context->reps_file, + pool)); + add_item_rep_mapping(context, entry); + + /* copy the noderev to our temp file */ + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, + pool)); + + /* if the node has a data representation, make that the node's "base". + * This will (often) cause the noderev to be placed right in front of + * its data representation. */ + + if (noderev->data_rep && noderev->data_rep->revision >= context->start_rev) + { + path_order->rep_id.revision = noderev->data_rep->revision; + path_order->rep_id.number = noderev->data_rep->item_index; + path_order->expanded_size = noderev->data_rep->expanded_size + ? noderev->data_rep->expanded_size + : noderev->data_rep->size; + } + + /* Sort path is the key used for ordering noderevs and associated reps. + * It will not be stored in the final pack file. */ + sort_path = tweak_path_for_ordering(noderev->created_path, pool); + path_order->path = svn_prefix_string__create(context->paths, sort_path); + path_order->node_id = *svn_fs_fs__id_node_id(noderev->id); + path_order->revision = svn_fs_fs__id_rev(noderev->id); + path_order->predecessor_count = noderev->predecessor_count; + path_order->is_dir = noderev->kind == svn_node_dir; + path_order->noderev_id = *svn_fs_fs__id_rev_item(noderev->id); + APR_ARRAY_PUSH(context->path_order, path_order_t *) = path_order; + + return SVN_NO_ERROR; +} + +/* implements compare_fn_t. Bring all directories in front of the files + and sort descendingly by PATH, NODE_ID and REVISION. + */ +static int +compare_path_order(const path_order_t * const * lhs_p, + const path_order_t * const * rhs_p) +{ + const path_order_t * lhs = *lhs_p; + const path_order_t * rhs = *rhs_p; + + /* cluster all directories */ + int diff = rhs->is_dir - lhs->is_dir; + if (diff) + return diff; + + /* lexicographic order on path and node (i.e. latest first) */ + diff = svn_prefix_string__compare(lhs->path, rhs->path); + if (diff) + return diff; + + /* reverse order on node (i.e. latest first) */ + diff = svn_fs_fs__id_part_compare(&rhs->node_id, &lhs->node_id); + if (diff) + return diff; + + /* reverse order on revision (i.e. latest first) */ + if (lhs->revision != rhs->revision) + return lhs->revision < rhs->revision ? 1 : -1; + + return 0; +} + +/* implements compare_fn_t. Sort ascendingly by FROM, TO. + */ +static int +compare_references(const reference_t * const * lhs_p, + const reference_t * const * rhs_p) +{ + const reference_t * lhs = *lhs_p; + const reference_t * rhs = *rhs_p; + + int diff = svn_fs_fs__id_part_compare(&lhs->from, &rhs->from); + return diff ? diff : svn_fs_fs__id_part_compare(&lhs->to, &rhs->to); +} + +/* implements compare_fn_t. Assume ascending order by FROM. + */ +static int +compare_ref_to_item(const reference_t * const * lhs_p, + const svn_fs_fs__id_part_t * rhs_p) +{ + return svn_fs_fs__id_part_compare(&(*lhs_p)->from, rhs_p); +} + +/* implements compare_fn_t. Finds the DIR / FILE boundary. + */ +static int +compare_is_dir(const path_order_t * const * lhs_p, + const void *unused) +{ + return (*lhs_p)->is_dir ? -1 : 0; +} + +/* Look for the least significant bit set in VALUE and return the smallest + * number with the same property, i.e. the largest power of 2 that is a + * factor in VALUE. */ +static int +roundness(int value) +{ + return value ? value - (value & (value - 1)) : INT_MAX; +} + +/* Order a range of data collected in CONTEXT such that we can place them + * in the desired order. The input is taken from *PATH_ORDER, offsets FIRST + * to LAST and then written in the final order to the same range in *TEMP. + */ +static void +sort_reps_range(pack_context_t *context, + const path_order_t **path_order, + const path_order_t **temp, + int first, + int last) +{ + const svn_prefix_string__t *path; + int i, dest, best; + svn_fs_fs__id_part_t rep_id; + fs_fs_data_t *ffd = context->fs->fsap_data; + + /* The logic below would fail for empty ranges. */ + if (first == last) + return; + + /* Re-order noderevs like this: + * + * (1) Most likely to be referenced by future pack files, in path order. + * (2) highest revision rep per path + dependency chain + * (3) Remaining reps in path, rev order + * + * We simply pick & chose from the existing path, rev order. + */ + dest = first; + path = path_order[first]->path; + best = first; + + /* (1) For each path, pick the "roundest" representation and put it in + * front of all other nodes in the pack file. The "roundest" rep is + * the one most likely to be referenced from future pack files, i.e. we + * concentrate those potential "foreign link targets" in one section of + * the pack file. + * + * And we only apply this to reps outside the linear deltification + * sections because references *into* linear deltification ranges are + * much less likely. + */ + for (i = first; i < last; ++i) + { + /* Investigated all nodes for the current path? */ + if (svn_prefix_string__compare(path, path_order[i]->path)) + { + /* next path */ + path = path_order[i]->path; + + /* Pick roundest non-linear deltified node. */ + if (roundness(path_order[best]->predecessor_count) + >= ffd->max_linear_deltification) + { + temp[dest++] = path_order[best]; + path_order[best] = NULL; + best = i; + } + } + + /* next entry */ + if ( roundness(path_order[best]->predecessor_count) + < roundness(path_order[i]->predecessor_count)) + best = i; + } + + /* Treat the last path the same as all others. */ + if (roundness(path_order[best]->predecessor_count) + >= ffd->max_linear_deltification) + { + temp[dest++] = path_order[best]; + path_order[best] = NULL; + } + + /* (2) For each (remaining) path, pick the nodes along the delta chain + * for the highest revision. Due to our ordering, this is the first + * node we encounter for any path. + * + * Most references that don't hit a delta base picked in (1), will + * access HEAD of the respective path. Keeping all its dependency chain + * in one place turns reconstruction into a linear scan of minimal length. + */ + for (i = first; i < last; ++i) + if (path_order[i]) + { + /* This is the first path we still have to handle. */ + path = path_order[i]->path; + rep_id = path_order[i]->rep_id; + break; + } + + for (i = first; i < last; ++i) + if (path_order[i]) + { + /* New path? */ + if (svn_prefix_string__compare(path, path_order[i]->path)) + { + path = path_order[i]->path; + rep_id = path_order[i]->rep_id; + } + + /* Pick nodes along the deltification chain. Skip side-branches. */ + if (svn_fs_fs__id_part_eq(&path_order[i]->rep_id, &rep_id)) + { + reference_t **reference; + + temp[dest++] = path_order[i]; + path_order[i] = NULL; + + reference = svn_sort__array_lookup(context->references, + &rep_id, NULL, + (int (*)(const void *, const void *))compare_ref_to_item); + if (reference) + rep_id = (*reference)->to; + } + } + + /* (3) All remaining nodes in path, rev order. Linear deltification + * makes HEAD delta chains from (2) cover all or most of their deltas + * in a given pack file. So, this is just a few remnants that we put + * at the end of the pack file. + */ + for (i = first; i < last; ++i) + if (path_order[i]) + temp[dest++] = path_order[i]; + + /* We now know the final ordering. */ + assert(dest == last); +} + +/* Order the data collected in CONTEXT such that we can place them in the + * desired order. + */ +static void +sort_reps(pack_context_t *context) +{ + apr_pool_t *temp_pool; + const path_order_t **temp, **path_order; + int i, count, dir_count; + + /* We will later assume that there is at least one node / path. + */ + if (context->path_order->nelts == 0) + { + assert(context->references->nelts == 0); + return; + } + + /* Sort containers by path and IDs, respectively. + */ + svn_sort__array(context->path_order, + (int (*)(const void *, const void *))compare_path_order); + svn_sort__array(context->references, + (int (*)(const void *, const void *))compare_references); + + /* Directories are already in front; sort directories section and files + * section separately but use the same heuristics (see sub-function). + */ + temp_pool = svn_pool_create(context->info_pool); + count = context->path_order->nelts; + temp = apr_pcalloc(temp_pool, count * sizeof(*temp)); + path_order = (void *)context->path_order->elts; + + /* Find the boundary between DIR and FILE section. */ + dir_count = svn_sort__bsearch_lower_bound(context->path_order, NULL, + (int (*)(const void *, const void *))compare_is_dir); + + /* Sort those sub-sections separately. */ + sort_reps_range(context, path_order, temp, 0, dir_count); + sort_reps_range(context, path_order, temp, dir_count, count); + + /* We now know the final ordering. */ + for (i = 0; i < count; ++i) + path_order[i] = temp[i]; + + svn_pool_destroy(temp_pool); +} + +/* implements compare_fn_t. Place LHS before RHS, if the latter is older. + */ +static int +compare_p2l_info(const svn_fs_fs__p2l_entry_t * const * lhs, + const svn_fs_fs__p2l_entry_t * const * rhs) +{ + assert(*lhs != *rhs); + + if ((*lhs)->item.revision == (*rhs)->item.revision) + return (*lhs)->item.number > (*rhs)->item.number ? -1 : 1; + + return (*lhs)->item.revision > (*rhs)->item.revision ? -1 : 1; +} + +/* Sort svn_fs_fs__p2l_entry_t * array ENTRIES by age. Place the latest + * items first. + */ +static void +sort_items(apr_array_header_t *entries) +{ + svn_sort__array(entries, + (int (*)(const void *, const void *))compare_p2l_info); +} + +/* Return the remaining unused bytes in the current block in CONTEXT's + * pack file. + */ +static apr_ssize_t +get_block_left(pack_context_t *context) +{ + fs_fs_data_t *ffd = context->fs->fsap_data; + return ffd->block_size - (context->pack_offset % ffd->block_size); +} + +/* To prevent items from overlapping a block boundary, we will usually + * put them into the next block and top up the old one with NUL bytes. + * Pad CONTEXT's pack file to the end of the current block, if TO_ADD does + * not fit into the current block and the padding is short enough. + * Use POOL for allocations. + */ +static svn_error_t * +auto_pad_block(pack_context_t *context, + apr_off_t to_add, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = context->fs->fsap_data; + + /* This is the maximum number of bytes "wasted" that way per block. + * Larger items will cross the block boundaries. */ + const apr_off_t max_padding = MAX(ffd->block_size / 50, 512); + + /* Is wasted space small enough to align the current item to the next + * block? */ + apr_off_t padding = get_block_left(context); + + if (padding < to_add && padding < max_padding) + { + /* Yes. To up with NUL bytes and don't forget to create + * an P2L index entry marking this section as unused. */ + svn_fs_fs__p2l_entry_t null_entry; + + null_entry.offset = context->pack_offset; + null_entry.size = padding; + null_entry.type = SVN_FS_FS__ITEM_TYPE_UNUSED; + null_entry.item.revision = SVN_INVALID_REVNUM; + null_entry.item.number = SVN_FS_FS__ITEM_INDEX_UNUSED; + null_entry.fnv1_checksum = 0; + + SVN_ERR(write_null_bytes(context->pack_file, padding, pool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry( + context->proto_p2l_index, &null_entry, pool)); + context->pack_offset += padding; + } + + return SVN_NO_ERROR; +} + +/* Read the contents of ITEM, if not empty, from TEMP_FILE and write it + * to CONTEXT->PACK_FILE. Use POOL for allocations. + */ +static svn_error_t * +store_item(pack_context_t *context, + apr_file_t *temp_file, + svn_fs_fs__p2l_entry_t *item, + apr_pool_t *pool) +{ + apr_off_t safety_margin; + + /* skip empty entries */ + if (item->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + return SVN_NO_ERROR; + + /* If the next item does not fit into the current block, auto-pad it. + Take special care of textual noderevs since their parsers may + prefetch up to 80 bytes and we don't want them to cross block + boundaries. */ + safety_margin = item->type == SVN_FS_FS__ITEM_TYPE_NODEREV + ? SVN__LINE_CHUNK_SIZE + : 0; + SVN_ERR(auto_pad_block(context, item->size + safety_margin, pool)); + + /* select the item in the source file and copy it into the target + * pack file */ + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &item->offset, pool)); + SVN_ERR(copy_file_data(context, context->pack_file, temp_file, + item->size, pool)); + + /* write index entry and update current position */ + item->offset = context->pack_offset; + context->pack_offset += item->size; + + SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(context->proto_p2l_index, + item, pool)); + + APR_ARRAY_PUSH(context->reps, svn_fs_fs__p2l_entry_t *) = item; + + return SVN_NO_ERROR; +} + +/* Read the contents of the non-empty items in ITEMS from TEMP_FILE and + * write them to CONTEXT->PACK_FILE. Use POOL for allocations. + */ +static svn_error_t * +store_items(pack_context_t *context, + apr_file_t *temp_file, + apr_array_header_t *items, + apr_pool_t *pool) +{ + int i; + apr_pool_t *iterpool = svn_pool_create(pool); + + /* copy all items in strict order */ + for (i = 0; i < items->nelts; ++i) + { + svn_pool_clear(iterpool); + SVN_ERR(store_item(context, temp_file, + APR_ARRAY_IDX(items, i, svn_fs_fs__p2l_entry_t *), + iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Copy (append) the items identified by svn_fs_fs__p2l_entry_t * elements + * in ENTRIES strictly in order from TEMP_FILE into CONTEXT->PACK_FILE. + * Use POOL for temporary allocations. + */ +static svn_error_t * +copy_reps_from_temp(pack_context_t *context, + apr_file_t *temp_file, + apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); + apr_array_header_t *path_order = context->path_order; + int i; + + /* copy items in path order. */ + for (i = 0; i < path_order->nelts; ++i) + { + path_order_t *current_path; + svn_fs_fs__p2l_entry_t *node_part; + svn_fs_fs__p2l_entry_t *rep_part; + + svn_pool_clear(iterpool); + + current_path = APR_ARRAY_IDX(path_order, i, path_order_t *); + node_part = get_item(context, ¤t_path->noderev_id, TRUE); + rep_part = get_item(context, ¤t_path->rep_id, TRUE); + + if (node_part) + SVN_ERR(store_item(context, temp_file, node_part, iterpool)); + if (rep_part) + SVN_ERR(store_item(context, temp_file, rep_part, iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* implements compare_fn_t. Place LHS before RHS, if the latter belongs to + * a newer revision. + */ +static int +compare_p2l_info_rev(const svn_fs_fs__p2l_entry_t * const * lhs_p, + const svn_fs_fs__p2l_entry_t * const * rhs_p) +{ + const svn_fs_fs__p2l_entry_t * lhs = *lhs_p; + const svn_fs_fs__p2l_entry_t * rhs = *rhs_p; + + if (lhs->item.revision == rhs->item.revision) + return 0; + + return lhs->item.revision < rhs->item.revision ? -1 : 1; +} + +/* Write the log-to-phys proto index file for CONTEXT and use POOL for + * temporary allocations. All items in all buckets must have been placed + * by now. + */ +static svn_error_t * +write_l2p_index(pack_context_t *context, + apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); + svn_revnum_t prev_rev = SVN_INVALID_REVNUM; + int i, dest; + + /* eliminate empty entries from CONTEXT->REPS */ + for (i = 0, dest = 0; i < context->reps->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = APR_ARRAY_IDX(context->reps, i, svn_fs_fs__p2l_entry_t *); + if (entry) + APR_ARRAY_IDX(context->reps, dest++, svn_fs_fs__p2l_entry_t *) + = entry; + } + context->reps->nelts = dest; + + /* we need to write the l2p index revision by revision */ + svn_sort__array(context->reps, + (int (*)(const void *, const void *))compare_p2l_info_rev); + + /* write index entries */ + for (i = 0; i < context->reps->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *p2l_entry + = APR_ARRAY_IDX(context->reps, i, svn_fs_fs__p2l_entry_t *); + if (p2l_entry == NULL) + continue; + + /* next revision? */ + if (prev_rev != p2l_entry->item.revision) + { + prev_rev = p2l_entry->item.revision; + SVN_ERR(svn_fs_fs__l2p_proto_index_add_revision( + context->proto_l2p_index, iterpool)); + } + + /* add entry */ + SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry(context->proto_l2p_index, + p2l_entry->offset, + p2l_entry->item.number, + iterpool)); + + /* keep memory usage in check */ + if (i % 256 == 0) + svn_pool_clear(iterpool); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Pack the current revision range of CONTEXT, i.e. this covers phases 2 + * to 4. Use POOL for allocations. + */ +static svn_error_t * +pack_range(pack_context_t *context, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = context->fs->fsap_data; + apr_pool_t *revpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool2 = svn_pool_create(pool); + + /* Phase 2: Copy items into various buckets and build tracking info */ + svn_revnum_t revision; + for (revision = context->start_rev; revision < context->end_rev; ++revision) + { + apr_off_t offset = 0; + svn_fs_fs__revision_file_t *rev_file; + + svn_pool_clear(revpool); + + /* Get the rev file dimensions (mainly index locations). */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, context->fs, + revision, revpool, iterpool)); + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + + /* store the indirect array index */ + APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts; + + /* read the phys-to-log index file until we covered the whole rev file. + * That index contains enough info to build both target indexes from it. */ + while (offset < rev_file->l2p_offset) + { + /* read one cluster */ + int i; + apr_array_header_t *entries; + + svn_pool_clear(iterpool); + + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs, + rev_file, revision, offset, + ffd->p2l_page_size, iterpool, + iterpool)); + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + + /* skip first entry if that was duplicated due crossing a + cluster boundary */ + if (offset > entry->offset) + continue; + + svn_pool_clear(iterpool2); + + /* process entry while inside the rev file */ + offset = entry->offset; + if (offset < rev_file->l2p_offset) + { + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, + iterpool2)); + + if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES) + SVN_ERR(copy_item_to_temp(context, + context->changes, + context->changes_file, + rev_file->file, entry, + iterpool2)); + else if (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS) + SVN_ERR(copy_item_to_temp(context, + context->file_props, + context->file_props_file, + rev_file->file, entry, + iterpool2)); + else if (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_PROPS) + SVN_ERR(copy_item_to_temp(context, + context->dir_props, + context->dir_props_file, + rev_file->file, entry, + iterpool2)); + else if ( entry->type == SVN_FS_FS__ITEM_TYPE_FILE_REP + || entry->type == SVN_FS_FS__ITEM_TYPE_DIR_REP) + SVN_ERR(copy_rep_to_temp(context, rev_file->file, entry, + iterpool2)); + else if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV) + SVN_ERR(copy_node_to_temp(context, rev_file->file, entry, + iterpool2)); + else + SVN_ERR_ASSERT(entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED); + + offset += entry->size; + } + } + + if (context->cancel_func) + SVN_ERR(context->cancel_func(context->cancel_baton)); + } + } + + svn_pool_destroy(iterpool2); + svn_pool_destroy(iterpool); + + /* phase 3: placement. + * Use "newest first" placement for simple items. */ + sort_items(context->changes); + sort_items(context->file_props); + sort_items(context->dir_props); + + /* follow dependencies recursively for noderevs and data representations */ + sort_reps(context); + + /* phase 4: copy bucket data to pack file. Write P2L index. */ + SVN_ERR(store_items(context, context->changes_file, context->changes, + revpool)); + svn_pool_clear(revpool); + SVN_ERR(store_items(context, context->file_props_file, context->file_props, + revpool)); + svn_pool_clear(revpool); + SVN_ERR(store_items(context, context->dir_props_file, context->dir_props, + revpool)); + svn_pool_clear(revpool); + SVN_ERR(copy_reps_from_temp(context, context->reps_file, revpool)); + svn_pool_clear(revpool); + + /* write L2P index as well (now that we know all target offsets) */ + SVN_ERR(write_l2p_index(context, revpool)); + + svn_pool_destroy(revpool); + + return SVN_NO_ERROR; +} + +/* Append CONTEXT->START_REV to the context's pack file with no re-ordering. + * This function will only be used for very large revisions (>>100k changes). + * Use POOL for temporary allocations. + */ +static svn_error_t * +append_revision(pack_context_t *context, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = context->fs->fsap_data; + apr_off_t offset = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + svn_fs_fs__revision_file_t *rev_file; + apr_finfo_t finfo; + + /* Get the size of the file. */ + const char *path = svn_dirent_join(context->shard_dir, + apr_psprintf(iterpool, "%ld", + context->start_rev), + pool); + SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, pool)); + + /* Copy all the bits from the rev file to the end of the pack file. */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, context->fs, + context->start_rev, pool, + iterpool)); + SVN_ERR(copy_file_data(context, context->pack_file, rev_file->file, + finfo.size, iterpool)); + + /* mark the start of a new revision */ + SVN_ERR(svn_fs_fs__l2p_proto_index_add_revision(context->proto_l2p_index, + pool)); + + /* read the phys-to-log index file until we covered the whole rev file. + * That index contains enough info to build both target indexes from it. */ + while (offset < finfo.size) + { + /* read one cluster */ + int i; + apr_array_header_t *entries; + + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs, rev_file, + context->start_rev, offset, + ffd->p2l_page_size, iterpool, + iterpool)); + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + + /* skip first entry if that was duplicated due crossing a + cluster boundary */ + if (offset > entry->offset) + continue; + + /* process entry while inside the rev file */ + offset = entry->offset; + if (offset < finfo.size) + { + entry->offset += context->pack_offset; + offset += entry->size; + SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry( + context->proto_l2p_index, entry->offset, + entry->item.number, iterpool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry( + context->proto_p2l_index, entry, iterpool)); + } + } + } + + svn_pool_destroy(iterpool); + context->pack_offset += finfo.size; + + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Logical addressing mode packing logic. + * + * Pack the revision shard starting at SHARD_REV in filesystem FS from + * SHARD_DIR into the PACK_FILE_DIR, using POOL for allocations. Limit + * the extra memory consumption to MAX_MEM bytes. CANCEL_FUNC and + * CANCEL_BATON are what you think they are. + */ +static svn_error_t * +pack_log_addressed(svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_dir, + svn_revnum_t shard_rev, + apr_size_t max_mem, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + enum + { + /* estimated amount of memory used to represent one item in memory + * during rev file packing */ + PER_ITEM_MEM = APR_ALIGN_DEFAULT(sizeof(path_order_t)) + + APR_ALIGN_DEFAULT(2 *sizeof(void*)) + + APR_ALIGN_DEFAULT(sizeof(reference_t)) + + APR_ALIGN_DEFAULT(sizeof(svn_fs_fs__p2l_entry_t)) + + 6 * sizeof(void*) + }; + + int max_items; + apr_array_header_t *max_ids; + pack_context_t context = { 0 }; + int i; + apr_size_t item_count = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + + /* Prevent integer overflow. We use apr arrays to process the items so + * the maximum number of items is INT_MAX. */ + { + apr_size_t temp = max_mem / PER_ITEM_MEM; + SVN_ERR_ASSERT(temp <= INT_MAX); + max_items = (int)temp; + } + + /* set up a pack context */ + SVN_ERR(initialize_pack_context(&context, fs, pack_file_dir, shard_dir, + shard_rev, max_items, cancel_func, + cancel_baton, pool)); + + /* phase 1: determine the size of the revisions to pack */ + SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, shard_rev, + context.shard_end_rev - shard_rev, + pool, pool)); + + /* pack revisions in ranges that don't exceed MAX_MEM */ + for (i = 0; i < max_ids->nelts; ++i) + if (APR_ARRAY_IDX(max_ids, i, apr_uint64_t) + item_count <= max_items) + { + context.end_rev++; + } + else + { + svn_pool_clear(iterpool); + + /* some unpacked revisions before this one? */ + if (context.start_rev < context.end_rev) + { + /* pack them intelligently (might be just 1 rev but still ...) */ + SVN_ERR(pack_range(&context, iterpool)); + SVN_ERR(reset_pack_context(&context, iterpool)); + item_count = 0; + } + + /* next revision range is to start with the current revision */ + context.start_rev = i + context.shard_rev; + context.end_rev = context.start_rev + 1; + + /* if this is a very large revision, we must place it as is */ + if (APR_ARRAY_IDX(max_ids, i, apr_uint64_t) > max_items) + { + SVN_ERR(append_revision(&context, iterpool)); + context.start_rev++; + } + else + item_count += (apr_size_t)APR_ARRAY_IDX(max_ids, i, apr_uint64_t); + } + + /* non-empty revision range at the end? */ + if (context.start_rev < context.end_rev) + SVN_ERR(pack_range(&context, iterpool)); + + /* last phase: finalize indexes and clean up */ + SVN_ERR(reset_pack_context(&context, iterpool)); + SVN_ERR(close_pack_context(&context, iterpool)); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file. + Use POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__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, + svn_fs_fs__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(svn_fs_fs__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); +} + +/* Packing logic for physical addresssing mode: + * Simply concatenate all revision contents. + * + * Pack the revision shard starting at SHARD_REV 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. + */ +static svn_error_t * +pack_phys_addressed(const char *pack_file_dir, + const char *shard_path, + svn_revnum_t start_rev, + 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; + apr_file_t *pack_file; + apr_file_t *manifest_file; + svn_stream_t *manifest_stream; + svn_revnum_t 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); + + /* Create the new directory and pack file. + * Use unbuffered apr_file_t since we're going to write using 16kb + * chunks. */ + SVN_ERR(svn_io_file_open(&pack_file, pack_file_path, + APR_WRITE | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, pool)); + + /* Create the manifest file. */ + SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path, + APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, pool)); + manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, pool); + + end_rev = start_rev + 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)); + + /* build 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_from_aprfile2(pack_file, TRUE, pool), + cancel_func, cancel_baton, iterpool)); + } + + /* Close stream over APR file. */ + SVN_ERR(svn_stream_close(manifest_stream)); + + /* Ensure that pack file is written to disk. */ + SVN_ERR(svn_io_file_flush_to_disk(manifest_file, pool)); + SVN_ERR(svn_io_file_close(manifest_file, pool)); + + /* disallow write access to the manifest file */ + SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool)); + + /* Ensure that pack file is written to disk. */ + SVN_ERR(svn_io_file_flush_to_disk(pack_file, pool)); + SVN_ERR(svn_io_file_close(pack_file, pool)); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* In filesystem FS, pack the revision SHARD containing exactly + * MAX_FILES_PER_DIR revisions from SHARD_PATH into the PACK_FILE_DIR, + * using POOL for allocations. Try to limit the amount of temporary + * memory needed to MAX_MEM bytes. 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. + * + * The actual packing will be done in a format-specific sub-function. + */ +static svn_error_t * +pack_rev_shard(svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_path, + apr_int64_t shard, + int max_files_per_dir, + apr_size_t max_mem, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + const char *pack_file_path; + svn_revnum_t shard_rev = (svn_revnum_t) (shard * max_files_per_dir); + + /* Some useful paths. */ + pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, 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 file. */ + SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, pool)); + + /* Index information files */ + if (svn_fs_fs__use_log_addressing(fs)) + SVN_ERR(pack_log_addressed(fs, pack_file_dir, shard_path, shard_rev, + max_mem, cancel_func, cancel_baton, pool)); + else + SVN_ERR(pack_phys_addressed(pack_file_dir, shard_path, shard_rev, + max_files_per_dir, cancel_func, + cancel_baton, pool)); + + SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool)); + SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool)); + + return SVN_NO_ERROR; +} + +/* Baton struct used by pack_body(), pack_shard() and synced_pack_shard(). + These calls are nested and for every level additional fields will be + available. */ +struct pack_baton +{ + /* Valid when entering pack_body(). */ + svn_fs_t *fs; + svn_fs_pack_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_baton; + + /* Additional entries valid when entering pack_shard(). */ + const char *revs_dir; + const char *revsprops_dir; + apr_int64_t shard; + + /* Additional entries valid when entering synced_pack_shard(). */ + const char *rev_shard_path; +}; + + +/* Part of the pack process that requires global (write) synchronization. + * We pack the revision properties of the shard described by BATON and + * In the file system at FS_PATH, pack the SHARD in REVS_DIR and replace + * the non-packed revprop & rev shard folder(s) with the packed ones. + * The packed rev folder has been created prior to calling this function. + */ +static svn_error_t * +synced_pack_shard(void *baton, + apr_pool_t *pool) +{ + struct pack_baton *pb = baton; + fs_fs_data_t *ffd = pb->fs->fsap_data; + const char *revprops_shard_path, *revprops_pack_file_dir; + + /* if enabled, pack the revprops in an equivalent way */ + if (pb->revsprops_dir) + { + revprops_pack_file_dir = svn_dirent_join(pb->revsprops_dir, + apr_psprintf(pool, + "%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD, + pb->shard), + pool); + revprops_shard_path = svn_dirent_join(pb->revsprops_dir, + apr_psprintf(pool, "%" APR_INT64_T_FMT, pb->shard), + pool); + + SVN_ERR(svn_fs_fs__pack_revprops_shard(revprops_pack_file_dir, + revprops_shard_path, + pb->shard, + ffd->max_files_per_dir, + (int)(0.9*ffd->revprop_pack_size), + ffd->compress_packed_revprops + ? SVN__COMPRESSION_ZLIB_DEFAULT + : SVN__COMPRESSION_NONE, + pb->cancel_func, + pb->cancel_baton, + pool)); + } + + /* Update the min-unpacked-rev file to reflect our newly packed shard. */ + SVN_ERR(svn_fs_fs__write_min_unpacked_rev(pb->fs, + (svn_revnum_t)((pb->shard + 1) * ffd->max_files_per_dir), + pool)); + ffd->min_unpacked_rev + = (svn_revnum_t)((pb->shard + 1) * ffd->max_files_per_dir); + + /* Finally, remove the existing shard directories. + * For revprops, clean up older obsolete shards as well as they might + * have been left over from an interrupted FS upgrade. */ + SVN_ERR(svn_io_remove_dir2(pb->rev_shard_path, TRUE, + pb->cancel_func, pb->cancel_baton, pool)); + if (pb->revsprops_dir) + { + svn_node_kind_t kind = svn_node_dir; + apr_int64_t to_cleanup = pb->shard; + do + { + SVN_ERR(svn_fs_fs__delete_revprops_shard(revprops_shard_path, + to_cleanup, + ffd->max_files_per_dir, + pb->cancel_func, + pb->cancel_baton, + pool)); + + /* If the previous shard exists, clean it up as well. + Don't try to clean up shard 0 as it we can't tell quickly + whether it actually needs cleaning up. */ + revprops_shard_path = svn_dirent_join(pb->revsprops_dir, + apr_psprintf(pool, "%" APR_INT64_T_FMT, --to_cleanup), + pool); + SVN_ERR(svn_io_check_path(revprops_shard_path, &kind, pool)); + } + while (kind == svn_node_dir && to_cleanup > 0); + } + + return SVN_NO_ERROR; +} + +/* Pack the shard described by 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(struct pack_baton *baton, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = baton->fs->fsap_data; + const char *rev_pack_file_dir; + + /* Notify caller we're starting to pack this shard. */ + if (baton->notify_func) + SVN_ERR(baton->notify_func(baton->notify_baton, baton->shard, + svn_fs_pack_notify_start, pool)); + + /* Some useful paths. */ + rev_pack_file_dir = svn_dirent_join(baton->revs_dir, + apr_psprintf(pool, + "%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD, + baton->shard), + pool); + baton->rev_shard_path = svn_dirent_join(baton->revs_dir, + apr_psprintf(pool, + "%" APR_INT64_T_FMT, + baton->shard), + pool); + + /* pack the revision content */ + SVN_ERR(pack_rev_shard(baton->fs, rev_pack_file_dir, baton->rev_shard_path, + baton->shard, ffd->max_files_per_dir, + DEFAULT_MAX_MEM, baton->cancel_func, + baton->cancel_baton, pool)); + + /* For newer repo formats, we only acquired the pack lock so far. + Before modifying the repo state by switching over to the packed + data, we need to acquire the global (write) lock. */ + if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) + SVN_ERR(svn_fs_fs__with_write_lock(baton->fs, synced_pack_shard, baton, + pool)); + else + SVN_ERR(synced_pack_shard(baton, pool)); + + /* Notify caller we're starting to pack this shard. */ + if (baton->notify_func) + SVN_ERR(baton->notify_func(baton->notify_baton, baton->shard, + svn_fs_pack_notify_end, pool)); + + return SVN_NO_ERROR; +} + +/* 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 + svn_fs_fs__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 = pb->fs->fsap_data; + apr_int64_t completed_shards; + svn_revnum_t youngest; + apr_pool_t *iterpool; + + /* 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(svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, pb->fs, + pool)); + + SVN_ERR(svn_fs_fs__youngest_rev(&youngest, pb->fs, 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; + + pb->revs_dir = svn_dirent_join(pb->fs->path, PATH_REVS_DIR, pool); + if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT) + pb->revsprops_dir = svn_dirent_join(pb->fs->path, PATH_REVPROPS_DIR, + pool); + + iterpool = svn_pool_create(pool); + for (pb->shard = ffd->min_unpacked_rev / ffd->max_files_per_dir; + pb->shard < completed_shards; + pb->shard++) + { + svn_pool_clear(iterpool); + + if (pb->cancel_func) + SVN_ERR(pb->cancel_func(pb->cancel_baton)); + + SVN_ERR(pack_shard(pb, 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 }; + fs_fs_data_t *ffd = fs->fsap_data; + svn_error_t *err; + + pb.fs = fs; + pb.notify_func = notify_func; + pb.notify_baton = notify_baton; + pb.cancel_func = cancel_func; + pb.cancel_baton = cancel_baton; + + if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) + { + /* Newer repositories provide a pack operation specific lock. + Acquire it to prevent concurrent packs. + + Since the file lock's lifetime is bound to a pool, we create a + separate subpool here to release the lock immediately after the + operation finished. + */ + err = svn_fs_fs__with_pack_lock(fs, pack_body, &pb, pool); + } + else + { + /* Use the global write lock for older repos. */ + err = svn_fs_fs__with_write_lock(fs, pack_body, &pb, pool); + } + + return svn_error_trace(err); +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/pack.h b/contrib/subversion/subversion/libsvn_fs_fs/pack.h new file mode 100644 index 000000000..ce1ceb01e --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/pack.h @@ -0,0 +1,64 @@ +/* pack.h : interface FSFS pack 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__PACK_H +#define SVN_LIBSVN_FS__PACK_H + +#include "fs.h" + +/* Possibly pack the repository at PATH. This just take full shards, and + combines all the revision files into a single one, with a manifest header. + If given, NOTIFY_FUNC will be called with NOTIFY_BATON to report progress. + Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. + + Existing filesystem references need not change. */ +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); + +/** + * For the packed revision @a rev in @a fs, determine the offset within + * the revision pack file and return it in @a rev_offset. Use @a pool for + * allocations. + */ +svn_error_t * +svn_fs_fs__get_packed_offset(apr_off_t *rev_offset, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the svn_dir_entry_t* objects of DIRECTORY in an APR array + * allocated in RESULT_POOL with entries added in storage (on-disk) order. + * FS' format will be used to pick the optimal ordering strategy. Use + * SCRATCH_POOL for temporary allocations. + */ +apr_array_header_t * +svn_fs_fs__order_dir_entries(svn_fs_t *fs, + apr_hash_t *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/recovery.c b/contrib/subversion/subversion/libsvn_fs_fs/recovery.c new file mode 100644 index 000000000..125d47a1f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/recovery.c @@ -0,0 +1,509 @@ +/* recovery.c --- FSFS recovery 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. + * ==================================================================== + */ + +#include "recovery.h" + +#include "svn_hash.h" +#include "svn_pools.h" +#include "private/svn_string_private.h" + +#include "index.h" +#include "low_level.h" +#include "rep-cache.h" +#include "revprops.h" +#include "util.h" +#include "cached_data.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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; + svn_fs_fs__revision_file_t *file; + svn_pool_clear(iterpool); + + err = svn_fs_fs__open_pack_or_rev_file(&file, fs, right, iterpool, + 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; + svn_fs_fs__revision_file_t *file; + svn_pool_clear(iterpool); + + err = svn_fs_fs__open_pack_or_rev_file(&file, fs, probe, iterpool, + 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 +{ + svn_stream_t *stream; + 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; + apr_size_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 ((apr_int64_t)bytes_to_read > (apr_int64_t)b->remaining) + bytes_to_read = (apr_size_t)b->remaining; + b->remaining -= bytes_to_read; + + return svn_stream_read_full(b->stream, buffer, &bytes_to_read); +} + +/* 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, + svn_fs_fs__revision_file_t *rev_file, + apr_off_t offset, + apr_uint64_t *max_node_id, + apr_uint64_t *max_copy_id, + apr_pool_t *pool) +{ + svn_fs_fs__rep_header_t *header; + struct recover_read_from_file_baton baton; + svn_stream_t *stream; + apr_hash_t *entries; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + node_revision_t *noderev; + svn_error_t *err; + + baton.stream = rev_file->stream; + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, pool)); + SVN_ERR(svn_fs_fs__read_noderev(&noderev, baton.stream, pool, pool)); + + /* Check that this is a directory. It should be. */ + if (noderev->kind != svn_node_dir) + 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. */ + if (!noderev->data_rep) + return SVN_NO_ERROR; + + /* 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 (noderev->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. */ + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, rev, NULL, + noderev->data_rep->item_index, pool)); + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, pool)); + SVN_ERR(svn_fs_fs__read_rep_header(&header, baton.stream, pool, pool)); + if (header->type != svn_fs_fs__rep_plain) + 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. Note that this is a directory, i.e. + represented using the hash format on disk and can never have 0 length. */ + baton.pool = pool; + baton.remaining = noderev->data_rep->expanded_size + ? noderev->data_rep->expanded_size + : noderev->data_rep->size; + stream = svn_stream_create(&baton, pool); + svn_stream_set_read2(stream, NULL /* only full read support */, + read_handler_recover); + + /* Now read the entries from that stream. */ + entries = apr_hash_make(pool); + err = svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + svn_error_clear(svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed representation for node-revision '%s'"), + id_str->data); + } + 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; + const svn_fs_id_t *id; + const svn_fs_fs__id_part_t *rev_item; + apr_uint64_t node_id, copy_id; + apr_off_t child_dir_offset; + const svn_string_t *path = apr_hash_this_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, SVN_FS_FS__KIND_FILE) == 0) + kind = svn_node_file; + else if (strcmp(str, SVN_FS_FS__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")); + + SVN_ERR(svn_fs_fs__id_parse(&id, str, iterpool)); + + rev_item = svn_fs_fs__id_rev_item(id); + if (rev_item->revision != 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)->number; + copy_id = svn_fs_fs__id_copy_id(id)->number; + + if (node_id > *max_node_id) + *max_node_id = node_id; + if (copy_id > *max_copy_id) + *max_copy_id = copy_id; + + if (kind == svn_node_file) + continue; + + SVN_ERR(svn_fs_fs__item_offset(&child_dir_offset, fs, + rev_file, rev, NULL, rev_item->number, + iterpool)); + 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; +} + +/* Part of the recovery procedure. Given an open non-packed revision file + REV_FILE for REV, locate the trailer that specifies the offset to the root + node-id and store this offset in *ROOT_OFFSET. Do temporary allocations in + POOL. */ +static svn_error_t * +recover_get_root_offset(apr_off_t *root_offset, + svn_revnum_t rev, + svn_fs_fs__revision_file_t *rev_file, + apr_pool_t *pool) +{ + char buffer[64]; + svn_stringbuf_t *trailer; + apr_off_t start; + apr_off_t end; + apr_size_t len; + + SVN_ERR_ASSERT(!rev_file->is_packed); + + /* We will assume that the last line containing the two offsets (to the root + node-id and to the changed path information) will never be longer than 64 + characters. */ + end = 0; + SVN_ERR(svn_io_file_seek(rev_file->file, APR_END, &end, pool)); + + if (end < sizeof(buffer)) + { + len = (apr_size_t)end; + start = 0; + } + else + { + len = sizeof(buffer); + start = end - sizeof(buffer); + } + + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &start, pool)); + SVN_ERR(svn_io_file_read_full2(rev_file->file, buffer, len, + NULL, NULL, pool)); + + trailer = svn_stringbuf_ncreate(buffer, len, pool); + SVN_ERR(svn_fs_fs__parse_revision_trailer(root_offset, NULL, trailer, rev)); + + return SVN_NO_ERROR; +} + +/* 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; + apr_uint64_t next_node_id = 0; + apr_uint64_t next_copy_id = 0; + svn_revnum_t youngest_rev; + svn_node_kind_t youngest_revprops_kind; + + /* The admin may have created a plain copy of this repo before attempting + to recover it (hotcopy may or may not work with corrupted repos). + Bump the instance ID. */ + SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool)); + + /* 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(svn_fs_fs__youngest_rev(&youngest_rev, fs, 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); + + for (rev = 0; rev <= max_rev; rev++) + { + svn_fs_fs__revision_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(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rev, pool, + iterpool)); + SVN_ERR(recover_get_root_offset(&root_offset, rev, rev_file, pool)); + SVN_ERR(recover_find_max_ids(fs, rev, rev_file, root_offset, + &next_node_id, &next_copy_id, pool)); + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + } + 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. */ + next_node_id++; + next_copy_id++; + } + + /* Before setting current, verify that there is a revprops file + for the youngest revision. (Issue #2992) */ + SVN_ERR(svn_io_check_path(svn_fs_fs__path_revprops(fs, max_rev, pool), + &youngest_revprops_kind, pool)); + if (youngest_revprops_kind == svn_node_none) + { + svn_boolean_t missing = TRUE; + if (!svn_fs_fs__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 svn_fs_fs__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_all_locks(fs, recover_body, &b, pool); +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/recovery.h b/contrib/subversion/subversion/libsvn_fs_fs/recovery.h new file mode 100644 index 000000000..5a6b68a06 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/recovery.h @@ -0,0 +1,36 @@ +/* recovery.h : interface to the FSFS recovery 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__RECOVERY_H +#define SVN_LIBSVN_FS__RECOVERY_H + +#include "fs.h" + +/* Recover the fsfs associated with filesystem FS. + Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. + Use POOL for temporary allocations. */ +svn_error_t *svn_fs_fs__recover(svn_fs_t *fs, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.h b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.h index c3a23072b..56692c92e 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from rep-cache-db.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_fs_fs/token-map.h. +/* This file is automatically generated from rep-cache-db.sql and .dist_sandbox/subversion-1.9.4/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 @@ -58,6 +58,12 @@ "INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0) " \ "" +#define STMT_UNLOCK_REP 7 +#define STMT_7_INFO {"STMT_UNLOCK_REP", NULL} +#define STMT_7 \ + "ROLLBACK TRANSACTION; " \ + "" + #define REP_CACHE_DB_SQL_DECLARE_STATEMENTS(varname) \ static const char * const varname[] = { \ STMT_0, \ @@ -67,6 +73,7 @@ STMT_4, \ STMT_5, \ STMT_6, \ + STMT_7, \ NULL \ } @@ -79,5 +86,6 @@ STMT_4_INFO, \ STMT_5_INFO, \ STMT_6_INFO, \ + STMT_7_INFO, \ {NULL, NULL} \ } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.sql b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.sql index b88c3e0c8..caaac334c 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.sql +++ b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache-db.sql @@ -63,3 +63,6 @@ WHERE revision > ?1 -- STMT_LOCK_REP BEGIN TRANSACTION; INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0) + +-- STMT_UNLOCK_REP +ROLLBACK TRANSACTION; diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c index 008226688..c2b42808b 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c @@ -50,20 +50,6 @@ path_rep_cache_db(const char *fs_path, return svn_dirent_join(fs_path, REP_CACHE_DB_NAME, result_pool); } -/* Check that REP refers to a revision that exists in FS. */ -static svn_error_t * -rep_has_been_born(representation_t *rep, - svn_fs_t *fs, - apr_pool_t *pool) -{ - SVN_ERR_ASSERT(rep); - - SVN_ERR(svn_fs_fs__revision_exists(rep->revision, fs, pool)); - - return SVN_NO_ERROR; -} - - /** Library-private API's. **/ @@ -81,7 +67,7 @@ open_rep_cache(void *baton, int version; /* Open (or create) the sqlite database. It will be automatically - closed when fs->pool is destoyed. */ + closed when fs->pool is destroyed. */ db_path = path_rep_cache_db(fs->path, pool); #ifndef WIN32 { @@ -94,7 +80,7 @@ open_rep_cache(void *baton, if (!exists) { const char *current = svn_fs_fs__path_current(fs, pool); - svn_error_t *err = svn_io_file_create(db_path, "", pool); + svn_error_t *err = svn_io_file_create_empty(db_path, pool); if (err && !APR_STATUS_IS_EEXIST(err->apr_err)) /* A real error. */ @@ -110,7 +96,7 @@ open_rep_cache(void *baton, #endif SVN_ERR(svn_sqlite__open(&sdb, db_path, svn_sqlite__mode_rwcreate, statements, - 0, NULL, + 0, NULL, 0, fs->pool, pool)); SVN_ERR(svn_sqlite__read_schema_version(&version, sdb, pool)); @@ -188,7 +174,7 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *fs, max = svn_sqlite__column_revnum(stmt, 0); SVN_ERR(svn_sqlite__reset(stmt)); if (SVN_IS_VALID_REVNUM(max)) /* The rep-cache could be empty. */ - SVN_ERR(svn_fs_fs__revision_exists(max, fs, iterpool)); + SVN_ERR(svn_fs_fs__ensure_revision_exists(max, fs, iterpool)); } SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, @@ -203,6 +189,7 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *fs, representation_t *rep; const char *sha1_digest; svn_error_t *err; + svn_checksum_t *checksum; /* Clear ITERPOOL occasionally. */ if (iterations++ % 16 == 0) @@ -218,14 +205,17 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *fs, /* Construct a representation_t. */ rep = apr_pcalloc(iterpool, sizeof(*rep)); + svn_fs_fs__id_txn_reset(&rep->txn_id); sha1_digest = svn_sqlite__column_text(stmt, 0, iterpool); - err = svn_checksum_parse_hex(&rep->sha1_checksum, - svn_checksum_sha1, sha1_digest, - iterpool); + err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1, + sha1_digest, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + rep->has_sha1 = TRUE; + memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); rep->revision = svn_sqlite__column_revnum(stmt, 1); - rep->offset = svn_sqlite__column_int64(stmt, 2); + rep->item_index = svn_sqlite__column_int64(stmt, 2); rep->size = svn_sqlite__column_int64(stmt, 3); rep->expanded_size = svn_sqlite__column_int64(stmt, 4); @@ -275,9 +265,12 @@ svn_fs_fs__get_rep_reference(representation_t **rep, if (have_row) { *rep = apr_pcalloc(pool, sizeof(**rep)); - (*rep)->sha1_checksum = svn_checksum_dup(checksum, pool); + svn_fs_fs__id_txn_reset(&(*rep)->txn_id); + memcpy((*rep)->sha1_digest, checksum->digest, + sizeof((*rep)->sha1_digest)); + (*rep)->has_sha1 = TRUE; (*rep)->revision = svn_sqlite__column_revnum(stmt, 0); - (*rep)->offset = svn_sqlite__column_int64(stmt, 1); + (*rep)->item_index = svn_sqlite__column_int64(stmt, 1); (*rep)->size = svn_sqlite__column_int64(stmt, 2); (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3); } @@ -287,7 +280,16 @@ svn_fs_fs__get_rep_reference(representation_t **rep, SVN_ERR(svn_sqlite__reset(stmt)); if (*rep) - SVN_ERR(rep_has_been_born(*rep, fs, pool)); + { + /* Check that REP refers to a revision that exists in FS. */ + svn_error_t *err = svn_fs_fs__ensure_revision_exists((*rep)->revision, + fs, pool); + if (err) + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + "Checksum '%s' in rep-cache is beyond HEAD", + svn_checksum_to_cstring_display(checksum, + pool)); + } return SVN_NO_ERROR; } @@ -295,28 +297,30 @@ svn_fs_fs__get_rep_reference(representation_t **rep, svn_error_t * svn_fs_fs__set_rep_reference(svn_fs_t *fs, representation_t *rep, - svn_boolean_t reject_dup, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; svn_sqlite__stmt_t *stmt; svn_error_t *err; + svn_checksum_t checksum; + checksum.kind = svn_checksum_sha1; + checksum.digest = rep->sha1_digest; SVN_ERR_ASSERT(ffd->rep_sharing_allowed); if (! ffd->rep_cache_db) SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool)); /* We only allow SHA1 checksums in this table. */ - if (rep->sha1_checksum == NULL) + if (! rep->has_sha1) return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, _("Only SHA1 checksums can be used as keys in the " "rep_cache table.\n")); SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP)); SVN_ERR(svn_sqlite__bindf(stmt, "siiii", - svn_checksum_to_cstring(rep->sha1_checksum, pool), + svn_checksum_to_cstring(&checksum, pool), (apr_int64_t) rep->revision, - (apr_int64_t) rep->offset, + (apr_int64_t) rep->item_index, (apr_int64_t) rep->size, (apr_int64_t) rep->expanded_size)); @@ -331,35 +335,11 @@ svn_fs_fs__set_rep_reference(svn_fs_t *fs, svn_error_clear(err); /* Constraint failed so the mapping for SHA1_CHECKSUM->REP - should exist. If so, and the value is the same one we were - about to write, that's cool -- just do nothing. If, however, - the value is *different*, that's a red flag! */ - SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, rep->sha1_checksum, - pool)); + should exist. If so that's cool -- just do nothing. If not, + that's a red flag! */ + SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, &checksum, pool)); - if (old_rep) - { - if (reject_dup && ((old_rep->revision != rep->revision) - || (old_rep->offset != rep->offset) - || (old_rep->size != rep->size) - || (old_rep->expanded_size != rep->expanded_size))) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - apr_psprintf(pool, - _("Representation key for checksum '%%s' exists " - "in filesystem '%%s' with a different value " - "(%%ld,%%%s,%%%s,%%%s) than what we were about " - "to store (%%ld,%%%s,%%%s,%%%s)"), - APR_OFF_T_FMT, SVN_FILESIZE_T_FMT, - SVN_FILESIZE_T_FMT, APR_OFF_T_FMT, - SVN_FILESIZE_T_FMT, SVN_FILESIZE_T_FMT), - svn_checksum_to_cstring_display(rep->sha1_checksum, pool), - fs->path, old_rep->revision, old_rep->offset, old_rep->size, - old_rep->expanded_size, rep->revision, rep->offset, rep->size, - rep->expanded_size); - else - return SVN_NO_ERROR; - } - else + if (!old_rep) { /* Something really odd at this point, we failed to insert the checksum AND failed to read an existing checksum. Do we need @@ -391,9 +371,13 @@ svn_fs_fs__del_rep_reference(svn_fs_t *fs, return SVN_NO_ERROR; } -svn_error_t * -svn_fs_fs__lock_rep_cache(svn_fs_t *fs, - apr_pool_t *pool) +/* Start a transaction to take an SQLite reserved lock that prevents + other writes. + + See unlock_rep_cache(). */ +static svn_error_t * +lock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; @@ -404,3 +388,31 @@ svn_fs_fs__lock_rep_cache(svn_fs_t *fs, return SVN_NO_ERROR; } + +/* End the transaction started by lock_rep_cache(). */ +static svn_error_t * +unlock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR_ASSERT(ffd->rep_cache_db); /* was opened by lock_rep_cache() */ + + SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_UNLOCK_REP)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *, + apr_pool_t *), + void *baton, + apr_pool_t *pool) +{ + svn_error_t *err; + + SVN_ERR(lock_rep_cache(fs, pool)); + err = body(baton, pool); + return svn_error_compose_create(err, unlock_rep_cache(fs, pool)); +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.h b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.h index 3ccb05684..ce424e6ea 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/rep-cache.h @@ -61,7 +61,8 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *fs, /* Return the representation REP in FS which has fulltext CHECKSUM. REP is allocated in POOL. If the rep cache database has not been - opened, just set *REP to NULL. */ + opened, just set *REP to NULL. Returns SVN_ERR_FS_CORRUPT if + a reference beyond HEAD is detected. */ svn_error_t * svn_fs_fs__get_rep_reference(representation_t **rep, svn_fs_t *fs, @@ -69,16 +70,13 @@ svn_fs_fs__get_rep_reference(representation_t **rep, apr_pool_t *pool); /* Set the representation REP in FS, using REP->CHECKSUM. - Use POOL for temporary allocations. + Use POOL for temporary allocations. Returns SVN_ERR_FS_CORRUPT if + an existing reference beyond HEAD is detected. - If the rep cache database has not been opened, this may be a no op. - - If REJECT_DUP is TRUE, return an error if there is an existing - match for REP->CHECKSUM. */ + If the rep cache database has not been opened, this may be a no op. */ svn_error_t * svn_fs_fs__set_rep_reference(svn_fs_t *fs, representation_t *rep, - svn_boolean_t reject_dup, apr_pool_t *pool); /* Delete from the cache all reps corresponding to revisions younger @@ -89,10 +87,14 @@ svn_fs_fs__del_rep_reference(svn_fs_t *fs, apr_pool_t *pool); /* Start a transaction to take an SQLite reserved lock that prevents - other writes. */ + other writes, call BODY, end the transaction, and return what BODY returned. + */ svn_error_t * -svn_fs_fs__lock_rep_cache(svn_fs_t *fs, - apr_pool_t *pool); +svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rev_file.c b/contrib/subversion/subversion/libsvn_fs_fs/rev_file.c new file mode 100644 index 000000000..7c18ac851 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/rev_file.c @@ -0,0 +1,306 @@ +/* rev_file.c --- revision file and index access functions + * + * ==================================================================== + * 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 "rev_file.h" +#include "fs_fs.h" +#include "index.h" +#include "low_level.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "private/svn_io_private.h" +#include "svn_private_config.h" + +/* Initialize the *FILE structure for REVISION in filesystem FS. Set its + * pool member to the provided POOL. */ +static void +init_revision_file(svn_fs_fs__revision_file_t *file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + file->is_packed = svn_fs_fs__is_packed_rev(fs, revision); + file->start_revision = svn_fs_fs__packed_base_rev(fs, revision); + + file->file = NULL; + file->stream = NULL; + file->p2l_stream = NULL; + file->l2p_stream = NULL; + file->block_size = ffd->block_size; + file->l2p_offset = -1; + file->l2p_checksum = NULL; + file->p2l_offset = -1; + file->p2l_checksum = NULL; + file->footer_offset = -1; + file->pool = pool; +} + +/* Baton type for set_read_only() */ +typedef struct set_read_only_baton_t +{ + /* File to set to read-only. */ + const char *file_path; + + /* Scratch pool sufficient life time. + * Ideally the pool that we registered the cleanup on. */ + apr_pool_t *pool; +} set_read_only_baton_t; + +/* APR pool cleanup callback taking a set_read_only_baton_t baton and then + * (trying to) set the specified file to r/o mode. */ +static apr_status_t +set_read_only(void *baton) +{ + set_read_only_baton_t *ro_baton = baton; + apr_status_t status = APR_SUCCESS; + svn_error_t *err; + + err = svn_io_set_file_read_only(ro_baton->file_path, TRUE, ro_baton->pool); + if (err) + { + status = err->apr_err; + svn_error_clear(err); + } + + return status; +} + +/* If the file at PATH is read-only, attempt to make it writable. The + * original state will be restored with RESULT_POOL gets cleaned up. + * SCRATCH_POOL is for temporary allocations. */ +static svn_error_t * +auto_make_writable(const char *path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t is_read_only; + apr_finfo_t finfo; + + SVN_ERR(svn_io_stat(&finfo, path, SVN__APR_FINFO_READONLY, scratch_pool)); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, scratch_pool)); + + if (is_read_only) + { + /* Tell the pool to restore the r/o state upon cleanup + (assuming the file will still exist, failing silently otherwise). */ + set_read_only_baton_t *baton = apr_pcalloc(result_pool, + sizeof(*baton)); + baton->pool = result_pool; + baton->file_path = apr_pstrdup(result_pool, path); + apr_pool_cleanup_register(result_pool, baton, + set_read_only, apr_pool_cleanup_null); + + /* Finally, allow write access (undoing it has already been scheduled + and is idempotent). */ + SVN_ERR(svn_io_set_file_read_write(path, FALSE, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Core implementation of svn_fs_fs__open_pack_or_rev_file working on an + * existing, initialized FILE structure. If WRITABLE is TRUE, give write + * access to the file - temporarily resetting the r/o state if necessary. + */ +static svn_error_t * +open_pack_or_rev_file(svn_fs_fs__revision_file_t *file, + svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t writable, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_error_t *err; + svn_boolean_t retry = FALSE; + + do + { + const char *path = svn_fs_fs__path_rev_absolute(fs, rev, scratch_pool); + apr_file_t *apr_file; + apr_int32_t flags = writable + ? APR_READ | APR_WRITE | APR_BUFFERED + : APR_READ | APR_BUFFERED; + + /* We may have to *temporarily* enable write access. */ + err = writable ? auto_make_writable(path, result_pool, scratch_pool) + : SVN_NO_ERROR; + + /* open the revision file in buffered r/o or r/w mode */ + if (!err) + err = svn_io_file_open(&apr_file, path, flags, APR_OS_DEFAULT, + result_pool); + + if (!err) + { + file->file = apr_file; + file->stream = svn_stream_from_aprfile2(apr_file, TRUE, + result_pool); + file->is_packed = svn_fs_fs__is_packed_rev(fs, rev); + + return SVN_NO_ERROR; + } + + 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(svn_fs_fs__update_min_unpacked_rev(fs, scratch_pool)); + file->start_revision = svn_fs_fs__packed_base_rev(fs, rev); + + 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); +} + +svn_error_t * +svn_fs_fs__open_pack_or_rev_file(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *file = apr_palloc(result_pool, sizeof(**file)); + init_revision_file(*file, fs, rev, result_pool); + + return svn_error_trace(open_pack_or_rev_file(*file, fs, rev, FALSE, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_fs_fs__open_pack_or_rev_file_writable(svn_fs_fs__revision_file_t** file, + svn_fs_t* fs, + svn_revnum_t rev, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool) +{ + *file = apr_palloc(result_pool, sizeof(**file)); + init_revision_file(*file, fs, rev, result_pool); + + return svn_error_trace(open_pack_or_rev_file(*file, fs, rev, TRUE, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_fs_fs__auto_read_footer(svn_fs_fs__revision_file_t *file) +{ + if (file->l2p_offset == -1) + { + apr_off_t filesize = 0; + unsigned char footer_length; + svn_stringbuf_t *footer; + + /* Determine file size. */ + SVN_ERR(svn_io_file_seek(file->file, APR_END, &filesize, file->pool)); + + /* Read last byte (containing the length of the footer). */ + SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, + filesize - 1, file->pool)); + SVN_ERR(svn_io_file_read_full2(file->file, &footer_length, + sizeof(footer_length), NULL, NULL, + file->pool)); + + /* Read footer. */ + footer = svn_stringbuf_create_ensure(footer_length, file->pool); + SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, + filesize - 1 - footer_length, + file->pool)); + SVN_ERR(svn_io_file_read_full2(file->file, footer->data, footer_length, + &footer->len, NULL, file->pool)); + footer->data[footer->len] = '\0'; + + /* Extract index locations. */ + SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->l2p_checksum, + &file->p2l_offset, &file->p2l_checksum, + footer, file->start_revision, + file->pool)); + file->footer_offset = filesize - footer_length - 1; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__open_proto_rev_file(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *apr_file; + SVN_ERR(svn_io_file_open(&apr_file, + svn_fs_fs__path_txn_proto_rev(fs, txn_id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + result_pool)); + + *file = apr_pcalloc(result_pool, sizeof(**file)); + (*file)->file = apr_file; + (*file)->is_packed = FALSE; + (*file)->start_revision = SVN_INVALID_REVNUM; + (*file)->stream = svn_stream_from_aprfile2(apr_file, TRUE, result_pool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__close_revision_file(svn_fs_fs__revision_file_t *file) +{ + if (file->stream) + SVN_ERR(svn_stream_close(file->stream)); + if (file->file) + SVN_ERR(svn_io_file_close(file->file, file->pool)); + + file->file = NULL; + file->stream = NULL; + file->l2p_stream = NULL; + file->p2l_stream = NULL; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/rev_file.h b/contrib/subversion/subversion/libsvn_fs_fs/rev_file.h new file mode 100644 index 000000000..951199446 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/rev_file.h @@ -0,0 +1,145 @@ +/* rev_file.h --- revision file and index access data structure + * + * ==================================================================== + * 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_FS__REV_FILE_H +#define SVN_LIBSVN_FS__REV_FILE_H + +#include "svn_fs.h" +#include "id.h" + +/* In format 7, index files must be read in sync with the respective + * revision / pack file. I.e. we must use packed index files for packed + * rev files and unpacked ones for non-packed rev files. So, the whole + * point is to open them with matching "is packed" setting in case some + * background pack process was run. + */ + +/* Opaque index stream type. + */ +typedef struct svn_fs_fs__packed_number_stream_t + svn_fs_fs__packed_number_stream_t; + +/* Data file, including indexes data, and associated properties for + * START_REVISION. As the FILE is kept open, background pack operations + * will not cause access to this file to fail. + */ +typedef struct svn_fs_fs__revision_file_t +{ + /* first (potentially only) revision in the rev / pack file. + * SVN_INVALID_REVNUM for txn proto-rev files. */ + svn_revnum_t start_revision; + + /* the revision was packed when the first file / stream got opened */ + svn_boolean_t is_packed; + + /* rev / pack file */ + apr_file_t *file; + + /* stream based on FILE and not NULL exactly when FILE is not NULL */ + svn_stream_t *stream; + + /* the opened P2L index stream or NULL. Always NULL for txns. */ + svn_fs_fs__packed_number_stream_t *p2l_stream; + + /* the opened L2P index stream or NULL. Always NULL for txns. */ + svn_fs_fs__packed_number_stream_t *l2p_stream; + + /* Copied from FS->FFD->BLOCK_SIZE upon creation. It allows us to + * use aligned seek() without having the FS handy. */ + apr_off_t block_size; + + /* Offset within FILE at which the rev data ends and the L2P index + * data starts. Less than P2L_OFFSET. -1 if svn_fs_fs__auto_read_footer + * has not been called, yet. */ + apr_off_t l2p_offset; + + /* MD5 checksum on the whole on-disk representation of the L2P index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *l2p_checksum; + + /* Offset within FILE at which the L2P index ends and the P2L index + * data starts. Greater than L2P_OFFSET. -1 if svn_fs_fs__auto_read_footer + * has not been called, yet. */ + apr_off_t p2l_offset; + + /* MD5 checksum on the whole on-disk representation of the P2L index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *p2l_checksum; + + /* Offset within FILE at which the P2L index ends and the footer starts. + * Greater than P2L_OFFSET. -1 if svn_fs_fs__auto_read_footer has not + * been called, yet. */ + apr_off_t footer_offset; + + /* pool containing this object */ + apr_pool_t *pool; +} svn_fs_fs__revision_file_t; + +/* 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. Allocate *FILE in RESULT_POOL and use SCRATCH_POOL + * for temporaries. */ +svn_error_t * +svn_fs_fs__open_pack_or_rev_file(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Open the correct revision file for REV with read and write access. + * If necessary, temporarily reset the file's read-only state. 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. + * Allocate *FILE in RESULT_POOL and use SCRATCH_POOLfor temporaries. */ +svn_error_t * +svn_fs_fs__open_pack_or_rev_file_writable(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* If the footer data in FILE has not been read, yet, do so now. + * Index locations will only be read upon request as we assume they get + * cached and the FILE is usually used for REP data access only. + * Hence, the separate step. + */ +svn_error_t * +svn_fs_fs__auto_read_footer(svn_fs_fs__revision_file_t *file); + +/* Open the proto-rev file of transaction TXN_ID in FS and return it in *FILE. + * Allocate *FILE in RESULT_POOL use and SCRATCH_POOL for temporaries.. */ +svn_error_t * +svn_fs_fs__open_proto_rev_file(svn_fs_fs__revision_file_t **file, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool); + +/* Close all files and streams in FILE. + */ +svn_error_t * +svn_fs_fs__close_revision_file(svn_fs_fs__revision_file_t *file); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/revprops.c b/contrib/subversion/subversion/libsvn_fs_fs/revprops.c new file mode 100644 index 000000000..dbb185bec --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/revprops.c @@ -0,0 +1,1381 @@ +/* revprops.c --- everything needed to handle revprops in FSFS + * + * ==================================================================== + * 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 + +#include "svn_pools.h" +#include "svn_hash.h" +#include "svn_dirent_uri.h" + +#include "fs_fs.h" +#include "revprops.h" +#include "util.h" + +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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) + +svn_error_t * +svn_fs_fs__upgrade_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + 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) + { + svn_pool_clear(iterpool); + + 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(svn_fs_fs__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, + cancel_func, cancel_baton, + iterpool)); + if (notify_func) + SVN_ERR(notify_func(notify_baton, shard, + svn_fs_upgrade_pack_revprops, iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__upgrade_cleanup_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + 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) + { + svn_pool_clear(iterpool); + + revprops_shard_path = svn_dirent_join(revsprops_dir, + apr_psprintf(iterpool, "%" APR_INT64_T_FMT, shard), + iterpool); + SVN_ERR(svn_fs_fs__delete_revprops_shard(revprops_shard_path, + shard, + ffd->max_files_per_dir, + cancel_func, cancel_baton, + iterpool)); + if (notify_func) + SVN_ERR(notify_func(notify_baton, shard, + svn_fs_upgrade_cleanup_revprops, iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* 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_W(svn_hash_read2(*properties, stream, SVN_HASH_TERMINATOR, pool), + apr_psprintf(scratch_pool, "Failed to parse revprops for r%ld.", + revision)); + + 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 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT && !missing && !content; + ++i) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__try_stringbuf_from_file(&content, + &missing, + svn_fs_fs__path_revprops(fs, rev, iterpool), + i + 1 < SVN_FS_FS__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; +} + +/* Return the minimum length of any packed revprop file name in REVPROPS. */ +static apr_size_t +get_min_filename_len(packed_revprops_t *revprops) +{ + char number_buffer[SVN_INT64_BUFFER_SIZE]; + + /* The revprop filenames have the format . - with being + * at least the first rev in the shard and having at least one + * digit. Thus, the minimum is 2 + #decimal places in the start rev. + */ + return svn__i64toa(number_buffer, revprops->manifest_start) + 2; +} + +/* 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, rev_count; + char *buffer, *buffer_end; + const char **filenames, **filenames_end; + apr_size_t min_filename_len; + + /* Determine the dimensions. Rev 0 is excluded from the first shard. */ + rev_count = ffd->max_files_per_dir; + revprops->manifest_start + = revprops->revision - (revprops->revision % rev_count); + if (revprops->manifest_start == 0) + { + ++revprops->manifest_start; + --rev_count; + } + + revprops->manifest = apr_array_make(pool, rev_count, sizeof(const char*)); + + /* No line in the file can be less than this number of chars long. */ + min_filename_len = get_min_filename_len(revprops); + + /* Read the content of the manifest file */ + revprops->folder + = svn_fs_fs__path_revprops_pack_shard(fs, revprops->revision, pool); + manifest_file_path + = svn_dirent_join(revprops->folder, PATH_MANIFEST, pool); + + SVN_ERR(svn_fs_fs__read_content(&content, manifest_file_path, pool)); + + /* There CONTENT must have a certain minimal size and there no + * unterminated lines at the end of the file. Both guarantees also + * simplify the parser loop below. + */ + if ( content->len < rev_count * (min_filename_len + 1) + || content->data[content->len - 1] != '\n') + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld not " + "properly terminated"), revprops->revision); + + /* Chop (parse) the manifest CONTENT into filenames, one per line. + * We only have to replace all newlines with NUL and add all line + * starts to REVPROPS->MANIFEST. + * + * There must be exactly REV_COUNT lines and that is the number of + * lines we parse from BUFFER to FILENAMES. Set the end pointer for + * the source BUFFER such that BUFFER+MIN_FILENAME_LEN is still valid + * BUFFER_END is always valid due to CONTENT->LEN > MIN_FILENAME_LEN. + * + * Please note that this loop is performance critical for e.g. 'svn log'. + * It is run 1000x per revprop access, i.e. per revision and about + * 50 million times per sec (and CPU core). + */ + for (filenames = (const char **)revprops->manifest->elts, + filenames_end = filenames + rev_count, + buffer = content->data, + buffer_end = buffer + content->len - min_filename_len; + (filenames < filenames_end) && (buffer < buffer_end); + ++filenames) + { + /* BUFFER always points to the start of the next line / filename. */ + *filenames = buffer; + + /* Find the next EOL. This is guaranteed to stay within the CONTENT + * buffer because we left enough room after BUFFER_END and we know + * we will always see a newline as the last non-NUL char. */ + buffer += min_filename_len; + while (*buffer != '\n') + ++buffer; + + /* Found EOL. Turn it into the filename terminator and move BUFFER + * to the start of the next line or CONTENT buffer end. */ + *buffer = '\0'; + ++buffer; + } + + /* We must have reached the end of both buffers. */ + if (buffer < content->data + content->len) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld " + "has too many entries"), revprops->revision); + + if (filenames < filenames_end) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld " + "has too few entries"), revprops->revision); + + /* The target array has now exactly one entry per revision. */ + revprops->manifest->nelts = rev_count; + + /* Now get the file name */ + idx = (int)(revprops->revision - revprops->manifest_start); + 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 member, and make PACKED_REVPROPS point to the + * first serialized revprop. If READ_ALL is set, initialize the SIZES + * and OFFSETS members as well. + * + * 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, + svn_boolean_t read_all, + 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_stringbuf_t *compressed = 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(svn_fs_fs__read_number_from_stream(&first_rev, NULL, stream, + iterpool)); + SVN_ERR(svn_fs_fs__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 (!svn_fs_fs__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. */ + revprops->start_revision = (svn_revnum_t)first_rev; + if (read_all) + { + /* Init / construct REVPROPS members. */ + 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; + svn_revnum_t revision = (svn_revnum_t)(first_rev + i); + svn_pool_clear(iterpool); + + /* read & check the serialized size */ + SVN_ERR(svn_fs_fs__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; + + /* If we only wanted the revprops for REVISION then we are done. */ + if (!read_all) + break; + } + + if (read_all) + { + /* 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; + } + + 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. + * If you want to modify revprop contents / update REVPROPS, READ_ALL + * must be set. Otherwise, only the properties of REV are being provided. + * 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, + svn_boolean_t read_all, + 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 (!svn_fs_fs__is_packed_revprop(fs, rev)) + SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, iterpool)); + + if (!svn_fs_fs__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 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT && !result->packed_revprops; + ++i) + { + const char *file_path; + svn_pool_clear(iterpool); + + /* 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(svn_fs_fs__try_stringbuf_from_file(&result->packed_revprops, + &missing, + file_path, + i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT, + pool)); + } + + /* 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, read_all, 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. + */ +svn_error_t * +svn_fs_fs__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(svn_fs_fs__ensure_revision_exists(rev, fs, pool)); + + /* 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 (!svn_fs_fs__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 *revprops; + SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, FALSE, pool)); + *proplist_p = 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 = svn_fs_fs__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_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. + * + * 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, + apr_pool_t *pool) +{ + SVN_ERR(svn_fs_fs__move_into_place(tmp_path, final_path, perms_reference, + 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_pool_clear(iterpool); + SVN_ERR(svn_io_remove_file2(path, TRUE, 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) + { + /* Non-standard pool usage. + * + * We only allocate a few bytes each iteration -- even with a + * million iterations we would still be in good shape memory-wise. + */ + 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_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* 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, + 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(uncompressed, + compressed, + ffd->compress_packed_revprops + ? SVN_DELTA_COMPRESSION_LEVEL_DEFAULT + : SVN_DELTA_COMPRESSION_LEVEL_NONE)); + + /* 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 *FILE that is + * allocated in POOL. + */ +static svn_error_t * +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; + 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; + + /* 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)); + + 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 contents of the current pack file */ + SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, TRUE, 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_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, + 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_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, + file, pool)); + } + + if (left_count + right_count < revprops->sizes->nelts) + { + 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, + file, pool)); + } + + if (right_count) + { + 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, file, + pool)); + } + + /* write the new manifest */ + *final_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, 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_io_file_write_full(file, filename, strlen(filename), + NULL, pool)); + SVN_ERR(svn_io_file_putc('\n', file, pool)); + } + + 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. */ +svn_error_t * +svn_fs_fs__set_revision_proplist(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *proplist, + apr_pool_t *pool) +{ + svn_boolean_t is_packed; + const char *final_path; + const char *tmp_path; + const char *perms_reference; + apr_array_header_t *files_to_delete = NULL; + + SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool)); + + /* this info will not change while we hold the global FS write lock */ + is_packed = svn_fs_fs__is_packed_revprop(fs, rev); + + /* 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.) + */ + perms_reference = svn_fs_fs__path_rev_absolute(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, pool)); + + 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. + */ +svn_boolean_t +svn_fs_fs__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 + = svn_fs_fs__path_revprops_pack_shard(fs, revision, pool); + const char *manifest_path = svn_dirent_join(folder, PATH_MANIFEST, pool); + + svn_error_t *err = svn_fs_fs__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; +} + + +/****** Packing FSFS shards *********/ + +svn_error_t * +svn_fs_fs__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); + + /* 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_stream_t *stream; + + 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(uncompressed, compressed, compression_level)); + + /* write the pack file content to disk */ + SVN_ERR(svn_io_file_write_full(pack_file, compressed->data, compressed->len, + NULL, scratch_pool)); + SVN_ERR(svn_io_file_flush_to_disk(pack_file, scratch_pool)); + SVN_ERR(svn_io_file_close(pack_file, scratch_pool)); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__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; + apr_file_t *manifest_file; + 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_io_file_open(&manifest_file, manifest_file_path, + APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, scratch_pool)); + manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, + 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; + /* Special special case: if max_files_per_dir is 1, then at this point + start_rev == 1 and end_rev == 0 (!). Fortunately, everything just + works. */ + + /* 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(svn_fs_fs__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(svn_fs_fs__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 to disk and update permissions */ + SVN_ERR(svn_stream_close(manifest_stream)); + SVN_ERR(svn_io_file_flush_to_disk(manifest_file, iterpool)); + SVN_ERR(svn_io_file_close(manifest_file, iterpool)); + SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, iterpool)); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__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_pool_clear(iterpool); + + 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_destroy(iterpool); + } + else + SVN_ERR(svn_io_remove_dir2(shard_path, TRUE, + cancel_func, cancel_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + diff --git a/contrib/subversion/subversion/libsvn_fs_fs/revprops.h b/contrib/subversion/subversion/libsvn_fs_fs/revprops.h new file mode 100644 index 000000000..66c137c33 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/revprops.h @@ -0,0 +1,159 @@ +/* revprops.h --- everything needed to handle revprops in FSFS + * + * ==================================================================== + * 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_fs.h" + +/* 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. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Temporary allocations are done in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_fs__upgrade_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* In the filesystem FS, remove all non-packed revprop shards up to + * min_unpacked_rev. Temporary allocations are done in SCRATCH_POOL. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Cancellation is supported in the sense that we + * will cleanly abort the operation. However, there will be remnant shards + * that must be removed manually. + * + * See upgrade_pack_revprops for more info. + */ +svn_error_t * +svn_fs_fs__upgrade_cleanup_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* Read the revprops for revision REV in FS and return them in *PROPERTIES_P. + * + * Allocations will be done in POOL. + */ +svn_error_t * +svn_fs_fs__get_revision_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Set the revision property list of revision REV in filesystem FS to + PROPLIST. Use POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__set_revision_proplist(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *proplist, + apr_pool_t *pool); + + +/* 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. + */ +svn_boolean_t +svn_fs_fs__packed_revprop_available(svn_boolean_t *missing, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *pool); + + +/****** Packing FSFS shards *********/ + +/* 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. + */ +svn_error_t * +svn_fs_fs__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); + +/* 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. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Temporary allocations are done in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_fs__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); + +/* In the filesystem FS, remove all non-packed revprop shards up to + * min_unpacked_rev. Temporary allocations are done in SCRATCH_POOL. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Cancellation is supported in the sense that we + * will cleanly abort the operation. However, there will be remnant shards + * that must be removed manually. + * + * See upgrade_pack_revprops for more info. + */ +svn_error_t * +svn_fs_fs__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); diff --git a/contrib/subversion/subversion/libsvn_fs_fs/stats.c b/contrib/subversion/subversion/libsvn_fs_fs/stats.c new file mode 100644 index 000000000..97a2ed773 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/stats.c @@ -0,0 +1,1255 @@ +/* stats.c -- implements the svn_fs_fs__get_stats private API. + * + * ==================================================================== + * 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_dirent_uri.h" +#include "svn_fs.h" +#include "svn_pools.h" +#include "svn_sorts.h" + +#include "private/svn_cache.h" +#include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +#include "private/svn_fs_fs_private.h" + +#include "index.h" +#include "pack.h" +#include "rev_file.h" +#include "util.h" +#include "fs_fs.h" +#include "cached_data.h" +#include "low_level.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* We group representations into 2x2 different kinds plus one default: + * [dir / file] x [text / prop]. The assignment is done by the first node + * that references the respective representation. + */ +typedef enum rep_kind_t +{ + /* The representation is not used _directly_, i.e. not referenced by any + * noderev. However, some other representation may use it as delta base. + * Null value. Should not occur in real-word repositories. */ + unused_rep, + + /* a properties on directory rep */ + dir_property_rep, + + /* a properties on file rep */ + file_property_rep, + + /* a directory rep */ + dir_rep, + + /* a file rep */ + file_rep +} rep_kind_t; + +/* A representation fragment. + */ +typedef struct rep_stats_t +{ + /* absolute offset in the file */ + apr_off_t offset; + + /* item length in bytes */ + apr_uint64_t size; + + /* item length after de-deltification */ + apr_uint64_t expanded_size; + + /* revision that contains this representation + * (may be referenced by other revisions, though) */ + svn_revnum_t revision; + + /* number of nodes that reference this representation */ + apr_uint32_t ref_count; + + /* length of the PLAIN / DELTA line in the source file in bytes */ + apr_uint16_t header_size; + + /* classification of the representation. values of rep_kind_t */ + char kind; + +} rep_stats_t; + +/* Represents a single revision. + * There will be only one instance per revision. */ +typedef struct revision_info_t +{ + /* number of this revision */ + svn_revnum_t revision; + + /* pack file offset (manifest value), 0 for non-packed files */ + apr_off_t offset; + + /* length of the changes list on bytes */ + apr_uint64_t changes_len; + + /* offset of the changes list relative to OFFSET */ + apr_uint64_t change_count; + + /* first offset behind the revision data in the pack file (file length + * for non-packed revs) */ + apr_off_t end; + + /* number of directory noderevs in this revision */ + apr_uint64_t dir_noderev_count; + + /* number of file noderevs in this revision */ + apr_uint64_t file_noderev_count; + + /* total size of directory noderevs (i.e. the structs - not the rep) */ + apr_uint64_t dir_noderev_size; + + /* total size of file noderevs (i.e. the structs - not the rep) */ + apr_uint64_t file_noderev_size; + + /* all rep_stats_t of this revision (in no particular order), + * i.e. those that point back to this struct */ + apr_array_header_t *representations; + + /* Temporary rev / pack file access object, used in phys. addressing + * mode only. NULL when done reading this revision. */ + svn_fs_fs__revision_file_t *rev_file; +} revision_info_t; + +/* Root data structure containing all information about a given repository. + * We use it as a wrapper around svn_fs_t and pass it around where we would + * otherwise just use a svn_fs_t. + */ +typedef struct query_t +{ + /* FS API object*/ + svn_fs_t *fs; + + /* The HEAD revision. */ + svn_revnum_t head; + + /* Number of revs per shard; 0 for non-sharded repos. */ + int shard_size; + + /* First non-packed revision. */ + svn_revnum_t min_unpacked_rev; + + /* all revisions */ + apr_array_header_t *revisions; + + /* empty representation. + * Used as a dummy base for DELTA reps without base. */ + rep_stats_t *null_base; + + /* collected statistics */ + svn_fs_fs__stats_t *stats; + + /* Progress notification callback to call after each shard. May be NULL. */ + svn_fs_progress_notify_func_t progress_func; + + /* Baton for PROGRESS_FUNC. */ + void *progress_baton; + + /* Cancellation support callback to call once in a while. May be NULL. */ + svn_cancel_func_t cancel_func; + + /* Baton for CANCEL_FUNC. */ + void *cancel_baton; +} query_t; + +/* Return the length of REV_FILE in *FILE_SIZE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_file_size(apr_off_t *file_size, + svn_fs_fs__revision_file_t *rev_file, + apr_pool_t *scratch_pool) +{ + apr_finfo_t finfo; + + SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, rev_file->file, + scratch_pool)); + + *file_size = finfo.size; + return SVN_NO_ERROR; +} + +/* Initialize the LARGEST_CHANGES member in STATS with a capacity of COUNT + * entries. Allocate the result in RESULT_POOL. + */ +static void +initialize_largest_changes(svn_fs_fs__stats_t *stats, + apr_size_t count, + apr_pool_t *result_pool) +{ + apr_size_t i; + + stats->largest_changes = apr_pcalloc(result_pool, + sizeof(*stats->largest_changes)); + stats->largest_changes->count = count; + stats->largest_changes->min_size = 1; + stats->largest_changes->changes + = apr_palloc(result_pool, count * sizeof(*stats->largest_changes->changes)); + + /* allocate *all* entries before the path stringbufs. This increases + * cache locality and enhances performance significantly. */ + for (i = 0; i < count; ++i) + stats->largest_changes->changes[i] + = apr_palloc(result_pool, sizeof(**stats->largest_changes->changes)); + + /* now initialize them and allocate the stringbufs */ + for (i = 0; i < count; ++i) + { + stats->largest_changes->changes[i]->size = 0; + stats->largest_changes->changes[i]->revision = SVN_INVALID_REVNUM; + stats->largest_changes->changes[i]->path + = svn_stringbuf_create_ensure(1024, result_pool); + } +} + +/* Add entry for SIZE to HISTOGRAM. + */ +static void +add_to_histogram(svn_fs_fs__histogram_t *histogram, + apr_int64_t size) +{ + apr_int64_t shift = 0; + + while (((apr_int64_t)(1) << shift) <= size) + shift++; + + histogram->total.count++; + histogram->total.sum += size; + histogram->lines[(apr_size_t)shift].count++; + histogram->lines[(apr_size_t)shift].sum += size; +} + +/* Update data aggregators in STATS with this representation of type KIND, + * on-disk REP_SIZE and expanded node size EXPANDED_SIZE for PATH in REVSION. + * PLAIN_ADDED indicates whether the node has a deltification predecessor. + */ +static void +add_change(svn_fs_fs__stats_t *stats, + apr_uint64_t rep_size, + apr_uint64_t expanded_size, + svn_revnum_t revision, + const char *path, + rep_kind_t kind, + svn_boolean_t plain_added) +{ + /* identify largest reps */ + if (rep_size >= stats->largest_changes->min_size) + { + apr_size_t i; + svn_fs_fs__largest_changes_t *largest_changes = stats->largest_changes; + svn_fs_fs__large_change_info_t *info + = largest_changes->changes[largest_changes->count - 1]; + info->size = rep_size; + info->revision = revision; + svn_stringbuf_set(info->path, path); + + /* linear insertion but not too bad since count is low and insertions + * near the end are more likely than close to front */ + for (i = largest_changes->count - 1; i > 0; --i) + if (largest_changes->changes[i-1]->size >= rep_size) + break; + else + largest_changes->changes[i] = largest_changes->changes[i-1]; + + largest_changes->changes[i] = info; + largest_changes->min_size + = largest_changes->changes[largest_changes->count-1]->size; + } + + /* global histograms */ + add_to_histogram(&stats->rep_size_histogram, rep_size); + add_to_histogram(&stats->node_size_histogram, expanded_size); + + if (plain_added) + { + add_to_histogram(&stats->added_rep_size_histogram, rep_size); + add_to_histogram(&stats->added_node_size_histogram, expanded_size); + } + + /* specific histograms by type */ + switch (kind) + { + case unused_rep: + add_to_histogram(&stats->unused_rep_histogram, rep_size); + break; + case dir_property_rep: + add_to_histogram(&stats->dir_prop_rep_histogram, rep_size); + add_to_histogram(&stats->dir_prop_histogram, expanded_size); + break; + case file_property_rep: + add_to_histogram(&stats->file_prop_rep_histogram, rep_size); + add_to_histogram(&stats->file_prop_histogram, expanded_size); + break; + case dir_rep: + add_to_histogram(&stats->dir_rep_histogram, rep_size); + add_to_histogram(&stats->dir_histogram, expanded_size); + break; + case file_rep: + add_to_histogram(&stats->file_rep_histogram, rep_size); + add_to_histogram(&stats->file_histogram, expanded_size); + break; + } + + /* by extension */ + if (kind == file_rep) + { + /* determine extension */ + svn_fs_fs__extension_info_t *info; + const char * file_name = strrchr(path, '/'); + const char * extension = file_name ? strrchr(file_name, '.') : NULL; + + if (extension == NULL || extension == file_name + 1) + extension = "(none)"; + + /* get / auto-insert entry for this extension */ + info = apr_hash_get(stats->by_extension, extension, APR_HASH_KEY_STRING); + if (info == NULL) + { + apr_pool_t *pool = apr_hash_pool_get(stats->by_extension); + info = apr_pcalloc(pool, sizeof(*info)); + info->extension = apr_pstrdup(pool, extension); + + apr_hash_set(stats->by_extension, info->extension, + APR_HASH_KEY_STRING, info); + } + + /* update per-extension histogram */ + add_to_histogram(&info->node_histogram, expanded_size); + add_to_histogram(&info->rep_histogram, rep_size); + } +} + +/* Comparator used for binary search comparing the absolute file offset + * of a representation to some other offset. DATA is a *rep_stats_t, + * KEY is a pointer to an apr_off_t. + */ +static int +compare_representation_offsets(const void *data, const void *key) +{ + apr_off_t lhs = (*(const rep_stats_t *const *)data)->offset; + apr_off_t rhs = *(const apr_off_t *)key; + + if (lhs < rhs) + return -1; + return (lhs > rhs ? 1 : 0); +} + +/* Find the revision_info_t object to the given REVISION in QUERY and + * return it in *REVISION_INFO. For performance reasons, we skip the + * lookup if the info is already provided. + * + * In that revision, look for the rep_stats_t object for offset OFFSET. + * If it already exists, set *IDX to its index in *REVISION_INFO's + * representations list and return the representation object. Otherwise, + * set the index to where it must be inserted and return NULL. + */ +static rep_stats_t * +find_representation(int *idx, + query_t *query, + revision_info_t **revision_info, + svn_revnum_t revision, + apr_off_t offset) +{ + revision_info_t *info; + *idx = -1; + + /* first let's find the revision */ + info = revision_info ? *revision_info : NULL; + if (info == NULL || info->revision != revision) + { + info = APR_ARRAY_IDX(query->revisions, revision, revision_info_t*); + if (revision_info) + *revision_info = info; + } + + /* not found -> no result */ + if (info == NULL) + return NULL; + + /* look for the representation */ + *idx = svn_sort__bsearch_lower_bound(info->representations, + &offset, + compare_representation_offsets); + if (*idx < info->representations->nelts) + { + /* return the representation, if this is the one we were looking for */ + rep_stats_t *result + = APR_ARRAY_IDX(info->representations, *idx, rep_stats_t *); + if (result->offset == offset) + return result; + } + + /* not parsed, yet */ + return NULL; +} + +/* Find / auto-construct the representation stats for REP in QUERY and + * return it in *REPRESENTATION. + * + * If necessary, allocate the result in RESULT_POOL; use SCRATCH_POOL for + * temporary allocations. + */ +static svn_error_t * +parse_representation(rep_stats_t **representation, + query_t *query, + representation_t *rep, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + rep_stats_t *result; + int idx; + + /* read location (revision, offset) and size */ + + /* look it up */ + result = find_representation(&idx, query, &revision_info, rep->revision, + (apr_off_t)rep->item_index); + if (!result) + { + /* not parsed, yet (probably a rep in the same revision). + * Create a new rep object and determine its base rep as well. + */ + result = apr_pcalloc(result_pool, sizeof(*result)); + result->revision = rep->revision; + result->expanded_size = (rep->expanded_size ? rep->expanded_size + : rep->size); + result->offset = (apr_off_t)rep->item_index; + result->size = rep->size; + + /* In phys. addressing mode, follow link to the actual representation. + * In log. addressing mode, we will find it already as part of our + * linear walk through the whole file. */ + if (!svn_fs_fs__use_log_addressing(query->fs)) + { + svn_fs_fs__rep_header_t *header; + apr_off_t offset = revision_info->offset + result->offset; + + SVN_ERR_ASSERT(revision_info->rev_file); + SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET, + &offset, scratch_pool)); + SVN_ERR(svn_fs_fs__read_rep_header(&header, + revision_info->rev_file->stream, + scratch_pool, scratch_pool)); + + result->header_size = header->header_size; + } + + svn_sort__array_insert(revision_info->representations, &result, idx); + } + + *representation = result; + + return SVN_NO_ERROR; +} + + +/* forward declaration */ +static svn_error_t * +read_noderev(query_t *query, + svn_stringbuf_t *noderev_str, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Read the noderev item at OFFSET in REVISION_INFO from the filesystem + * provided by QUERY. Return it in *NODEREV, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. + * + * The textual representation of the noderev will be used to determine + * the on-disk size of the noderev. Only called in phys. addressing mode. + */ +static svn_error_t * +read_phsy_noderev(svn_stringbuf_t **noderev, + query_t *query, + apr_off_t offset, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *noderev_str = svn_stringbuf_create_empty(result_pool); + svn_stringbuf_t *line; + svn_boolean_t eof; + + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Navigate the file stream to the start of noderev. */ + SVN_ERR_ASSERT(revision_info->rev_file); + + offset += revision_info->offset; + SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET, + &offset, scratch_pool)); + + /* Read it (terminated by an empty line) */ + do + { + svn_pool_clear(iterpool); + + SVN_ERR(svn_stream_readline(revision_info->rev_file->stream, &line, + "\n", &eof, iterpool)); + svn_stringbuf_appendstr(noderev_str, line); + svn_stringbuf_appendbyte(noderev_str, '\n'); + } + while (line->len > 0 && !eof); + + /* Return the result. */ + *noderev = noderev_str; + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Starting at the directory in NODEREV's text, read all DAG nodes, + * directories and representations linked in that tree structure. + * Store them in QUERY and REVISION_INFO. Also, read them only once. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +parse_dir(query_t *query, + node_revision_t *noderev, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + int i; + apr_array_header_t *entries; + SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, query->fs, noderev, + scratch_pool, scratch_pool)); + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); + + if (svn_fs_fs__id_rev(dirent->id) == revision_info->revision) + { + svn_stringbuf_t *noderev_str; + svn_pool_clear(iterpool); + + SVN_ERR(read_phsy_noderev(&noderev_str, query, + svn_fs_fs__id_item(dirent->id), + revision_info, iterpool, iterpool)); + SVN_ERR(read_noderev(query, noderev_str, revision_info, + result_pool, iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Parse the noderev given as NODEREV_STR and store the info in QUERY and + * REVISION_INFO. In phys. addressing mode, continue reading all DAG nodes, + * directories and representations linked in that tree structure. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_noderev(query_t *query, + svn_stringbuf_t *noderev_str, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + rep_stats_t *text = NULL; + rep_stats_t *props = NULL; + node_revision_t *noderev; + + svn_stream_t *stream = svn_stream_from_stringbuf(noderev_str, scratch_pool); + SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, scratch_pool, + scratch_pool)); + + if (noderev->data_rep) + { + SVN_ERR(parse_representation(&text, query, + noderev->data_rep, revision_info, + result_pool, scratch_pool)); + + /* if we are the first to use this rep, mark it as "text rep" */ + if (++text->ref_count == 1) + text->kind = noderev->kind == svn_node_dir ? dir_rep : file_rep; + } + + if (noderev->prop_rep) + { + SVN_ERR(parse_representation(&props, query, + noderev->prop_rep, revision_info, + result_pool, scratch_pool)); + + /* if we are the first to use this rep, mark it as "prop rep" */ + if (++props->ref_count == 1) + props->kind = noderev->kind == svn_node_dir ? dir_property_rep + : file_property_rep; + } + + /* record largest changes */ + if (text && text->ref_count == 1) + add_change(query->stats, text->size, text->expanded_size, text->revision, + noderev->created_path, text->kind, !noderev->predecessor_id); + if (props && props->ref_count == 1) + add_change(query->stats, props->size, props->expanded_size, + props->revision, noderev->created_path, props->kind, + !noderev->predecessor_id); + + /* if this is a directory and has not been processed, yet, read and + * process it recursively */ + if ( noderev->kind == svn_node_dir && text && text->ref_count == 1 + && !svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(parse_dir(query, noderev, revision_info, result_pool, + scratch_pool)); + + /* update stats */ + if (noderev->kind == svn_node_dir) + { + revision_info->dir_noderev_size += noderev_str->len; + revision_info->dir_noderev_count++; + } + else + { + revision_info->file_noderev_size += noderev_str->len; + revision_info->file_noderev_count++; + } + + return SVN_NO_ERROR; +} + +/* For the revision given as REVISION_INFO within QUERY, determine the number + * of entries in its changed paths list and store that info in REVISION_INFO. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_phys_change_count(query_t *query, + revision_info_t *revision_info, + apr_pool_t *scratch_pool) +{ + /* We are going to use our own sub-pool here because the changes object + * may well be >100MB and SCRATCH_POOL may not get cleared until all other + * info has been read by read_phys_revision(). Therefore, tidy up early. + */ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + apr_array_header_t *changes; + + SVN_ERR(svn_fs_fs__get_changes(&changes, query->fs, + revision_info->revision, subpool)); + revision_info->change_count = changes->nelts; + + /* Release potentially tons of memory. */ + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + +/* Read header information for the revision stored in FILE_CONTENT (one + * whole revision). Return the offsets within FILE_CONTENT for the + * *ROOT_NODEREV, the list of *CHANGES and its len in *CHANGES_LEN. + * Use POOL for temporary allocations. */ +static svn_error_t * +read_phys_revision(query_t *query, + revision_info_t *info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char buf[64]; + apr_off_t root_node_offset; + apr_off_t changes_offset; + svn_stringbuf_t *trailer; + svn_stringbuf_t *noderev_str; + + /* Read the last 64 bytes of the revision (if long enough). */ + apr_off_t start = MAX(info->offset, info->end - sizeof(buf)); + apr_size_t len = (apr_size_t)(info->end - start); + SVN_ERR(svn_io_file_seek(info->rev_file->file, APR_SET, &start, + scratch_pool)); + SVN_ERR(svn_io_file_read_full2(info->rev_file->file, buf, len, NULL, NULL, + scratch_pool)); + trailer = svn_stringbuf_ncreate(buf, len, scratch_pool); + + /* Parse that trailer. */ + SVN_ERR(svn_fs_fs__parse_revision_trailer(&root_node_offset, + &changes_offset, trailer, + info->revision)); + SVN_ERR(get_phys_change_count(query, info, scratch_pool)); + + /* Calculate the length of the changes list. */ + trailer = svn_fs_fs__unparse_revision_trailer(root_node_offset, + changes_offset, + scratch_pool); + info->changes_len = info->end - info->offset - changes_offset + - trailer->len; + + /* Recursively read nodes added in this rev. */ + SVN_ERR(read_phsy_noderev(&noderev_str, query, root_node_offset, info, + scratch_pool, scratch_pool)); + SVN_ERR(read_noderev(query, noderev_str, info, result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the content of the pack file staring at revision BASE physical + * addressing mode and store it in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_phys_pack_file(query_t *query, + svn_revnum_t base, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + apr_off_t file_size = 0; + svn_fs_fs__revision_file_t *rev_file; + + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base, + scratch_pool, scratch_pool)); + SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool)); + + /* process each revision in the pack file */ + for (i = 0; i < query->shard_size; ++i) + { + revision_info_t *info; + + /* cancellation support */ + if (query->cancel_func) + SVN_ERR(query->cancel_func(query->cancel_baton)); + + /* create the revision info for the current rev */ + info = apr_pcalloc(result_pool, sizeof(*info)); + info->representations = apr_array_make(result_pool, 4, + sizeof(rep_stats_t*)); + info->rev_file = rev_file; + + info->revision = base + i; + SVN_ERR(svn_fs_fs__get_packed_offset(&info->offset, query->fs, base + i, + iterpool)); + if (i + 1 == query->shard_size) + info->end = file_size; + else + SVN_ERR(svn_fs_fs__get_packed_offset(&info->end, query->fs, + base + i + 1, iterpool)); + + SVN_ERR(read_phys_revision(query, info, result_pool, iterpool)); + + info->representations = apr_array_copy(result_pool, + info->representations); + + /* Done with this revision. */ + info->rev_file = NULL; + + /* put it into our container */ + APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; + + /* destroy temps */ + svn_pool_clear(iterpool); + } + + /* Done with this pack file. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + /* one more pack file processed */ + if (query->progress_func) + query->progress_func(base, query->progress_baton, scratch_pool); + + return SVN_NO_ERROR; +} + +/* Read the content of the file for REVISION in physical addressing mode + * and store its contents in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_phys_revision_file(query_t *query, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info)); + apr_off_t file_size = 0; + svn_fs_fs__revision_file_t *rev_file; + + /* cancellation support */ + if (query->cancel_func) + SVN_ERR(query->cancel_func(query->cancel_baton)); + + /* read the whole pack file into memory */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, revision, + scratch_pool, scratch_pool)); + SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool)); + + /* create the revision info for the current rev */ + info->representations = apr_array_make(result_pool, 4, sizeof(rep_stats_t*)); + + info->rev_file = rev_file; + info->revision = revision; + info->offset = 0; + info->end = file_size; + + SVN_ERR(read_phys_revision(query, info, result_pool, scratch_pool)); + + /* Done with this revision. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + info->rev_file = NULL; + + /* put it into our container */ + APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; + + /* show progress every 1000 revs or so */ + if (query->progress_func) + { + if (query->shard_size && (revision % query->shard_size == 0)) + query->progress_func(revision, query->progress_baton, scratch_pool); + if (!query->shard_size && (revision % 1000 == 0)) + query->progress_func(revision, query->progress_baton, scratch_pool); + } + + return SVN_NO_ERROR; +} + +/* Given the unparsed changes list in CHANGES with LEN chars, return the + * number of changed paths encoded in it. Only used in log. addressing + * mode. + */ +static apr_uint64_t +get_log_change_count(const char *changes, + apr_size_t len) +{ + apr_size_t lines = 0; + const char *end = changes + len; + + /* line count */ + for (; changes < end; ++changes) + if (*changes == '\n') + ++lines; + + /* two lines per change */ + return lines / 2; +} + +/* Read the item described by ENTRY from the REV_FILE and return the + * respective byte sequence in *CONTENTS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations + */ +static svn_error_t * +read_item(svn_stringbuf_t **contents, + svn_fs_fs__revision_file_t *rev_file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *item = svn_stringbuf_create_ensure(entry->size, + result_pool); + item->len = entry->size; + item->data[item->len] = 0; + + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, rev_file->block_size, + NULL, entry->offset, scratch_pool)); + SVN_ERR(svn_io_file_read_full2(rev_file->file, item->data, item->len, + NULL, NULL, scratch_pool)); + + *contents = item; + + return SVN_NO_ERROR; +} + +/* Process the logically addressed revision contents of revisions BASE to + * BASE + COUNT - 1 in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_log_rev_or_packfile(query_t *query, + svn_revnum_t base, + int count, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = query->fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_off_t max_offset; + apr_off_t offset = 0; + int i; + svn_fs_fs__revision_file_t *rev_file; + + /* we will process every revision in the rev / pack file */ + for (i = 0; i < count; ++i) + { + /* create the revision info for the current rev */ + revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info)); + info->representations = apr_array_make(result_pool, 4, + sizeof(rep_stats_t*)); + info->revision = base + i; + + APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; + } + + /* open the pack / rev file that is covered by the p2l index */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base, + scratch_pool, iterpool)); + SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, query->fs, rev_file, + base, scratch_pool)); + + /* record the whole pack size in the first rev so the total sum will + still be correct */ + APR_ARRAY_IDX(query->revisions, base, revision_info_t*)->end = max_offset; + + /* for all offsets in the file, get the P2L index entries and process + the interesting items (change lists, noderevs) */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + + svn_pool_clear(iterpool); + + /* cancellation support */ + if (query->cancel_func) + SVN_ERR(query->cancel_func(query->cancel_baton)); + + /* get all entries for the current block */ + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, query->fs, rev_file, base, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + + /* process all entries (and later continue with the next block) */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + + /* skip bits we previously processed */ + if (i == 0 && entry->offset < offset) + continue; + + /* skip zero-sized entries */ + if (entry->size == 0) + continue; + + /* read and process interesting items */ + if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV) + { + svn_stringbuf_t *item; + revision_info_t *info = APR_ARRAY_IDX(query->revisions, + entry->item.revision, + revision_info_t*); + SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool)); + SVN_ERR(read_noderev(query, item, info, result_pool, iterpool)); + } + else if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES) + { + svn_stringbuf_t *item; + revision_info_t *info = APR_ARRAY_IDX(query->revisions, + entry->item.revision, + revision_info_t*); + SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool)); + info->change_count + = get_log_change_count(item->data + 0, item->len); + info->changes_len += entry->size; + } + + /* advance offset */ + offset += entry->size; + } + } + + /* clean up and close file handles */ + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Read the content of the pack file staring at revision BASE logical + * addressing mode and store it in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_log_pack_file(query_t *query, + svn_revnum_t base, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + SVN_ERR(read_log_rev_or_packfile(query, base, query->shard_size, + result_pool, scratch_pool)); + + /* one more pack file processed */ + if (query->progress_func) + query->progress_func(base, query->progress_baton, scratch_pool); + + return SVN_NO_ERROR; +} + +/* Read the content of the file for REVISION in logical addressing mode + * and store its contents in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_log_revision_file(query_t *query, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + SVN_ERR(read_log_rev_or_packfile(query, revision, 1, + result_pool, scratch_pool)); + + /* show progress every 1000 revs or so */ + if (query->progress_func) + { + if (query->shard_size && (revision % query->shard_size == 0)) + query->progress_func(revision, query->progress_baton, scratch_pool); + if (!query->shard_size && (revision % 1000 == 0)) + query->progress_func(revision, query->progress_baton, scratch_pool); + } + + return SVN_NO_ERROR; +} + +/* Read the repository and collect the stats info in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. + */ +static svn_error_t * +read_revisions(query_t *query, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_revnum_t revision; + + /* read all packed revs */ + for ( revision = 0 + ; revision < query->min_unpacked_rev + ; revision += query->shard_size) + { + svn_pool_clear(iterpool); + + if (svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(read_log_pack_file(query, revision, result_pool, iterpool)); + else + SVN_ERR(read_phys_pack_file(query, revision, result_pool, iterpool)); + } + + /* read non-packed revs */ + for ( ; revision <= query->head; ++revision) + { + svn_pool_clear(iterpool); + + if (svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(read_log_revision_file(query, revision, result_pool, + iterpool)); + else + SVN_ERR(read_phys_revision_file(query, revision, result_pool, + iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Accumulate stats of REP in STATS. + */ +static void +add_rep_pack_stats(svn_fs_fs__rep_pack_stats_t *stats, + rep_stats_t *rep) +{ + stats->count++; + + stats->packed_size += rep->size; + stats->expanded_size += rep->expanded_size; + stats->overhead_size += rep->header_size + 7 /* ENDREP\n */; +} + +/* Accumulate stats of REP in STATS. + */ +static void +add_rep_stats(svn_fs_fs__representation_stats_t *stats, + rep_stats_t *rep) +{ + add_rep_pack_stats(&stats->total, rep); + if (rep->ref_count == 1) + add_rep_pack_stats(&stats->uniques, rep); + else + add_rep_pack_stats(&stats->shared, rep); + + stats->references += rep->ref_count; + stats->expanded_size += rep->ref_count * rep->expanded_size; +} + +/* Aggregate the info the in revision_info_t * array REVISIONS into the + * respectve fields of STATS. + */ +static void +aggregate_stats(const apr_array_header_t *revisions, + svn_fs_fs__stats_t *stats) +{ + int i, k; + + /* aggregate info from all revisions */ + stats->revision_count = revisions->nelts; + for (i = 0; i < revisions->nelts; ++i) + { + revision_info_t *revision = APR_ARRAY_IDX(revisions, i, + revision_info_t *); + + /* data gathered on a revision level */ + stats->change_count += revision->change_count; + stats->change_len += revision->changes_len; + stats->total_size += revision->end - revision->offset; + + stats->dir_node_stats.count += revision->dir_noderev_count; + stats->dir_node_stats.size += revision->dir_noderev_size; + stats->file_node_stats.count += revision->file_noderev_count; + stats->file_node_stats.size += revision->file_noderev_size; + stats->total_node_stats.count += revision->dir_noderev_count + + revision->file_noderev_count; + stats->total_node_stats.size += revision->dir_noderev_size + + revision->file_noderev_size; + + /* process representations */ + for (k = 0; k < revision->representations->nelts; ++k) + { + rep_stats_t *rep = APR_ARRAY_IDX(revision->representations, k, + rep_stats_t *); + + /* accumulate in the right bucket */ + switch(rep->kind) + { + case file_rep: + add_rep_stats(&stats->file_rep_stats, rep); + break; + case dir_rep: + add_rep_stats(&stats->dir_rep_stats, rep); + break; + case file_property_rep: + add_rep_stats(&stats->file_prop_rep_stats, rep); + break; + case dir_property_rep: + add_rep_stats(&stats->dir_prop_rep_stats, rep); + break; + default: + break; + } + + add_rep_stats(&stats->total_rep_stats, rep); + } + } +} + +/* Return a new svn_fs_fs__stats_t instance, allocated in RESULT_POOL. + */ +static svn_fs_fs__stats_t * +create_stats(apr_pool_t *result_pool) +{ + svn_fs_fs__stats_t *stats = apr_pcalloc(result_pool, sizeof(*stats)); + + initialize_largest_changes(stats, 64, result_pool); + stats->by_extension = apr_hash_make(result_pool); + + return stats; +} + +/* Create a *QUERY, allocated in RESULT_POOL, reading filesystem FS and + * collecting results in STATS. Store the optional PROCESS_FUNC and + * PROGRESS_BATON as well as CANCEL_FUNC and CANCEL_BATON in *QUERY, too. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +create_query(query_t **query, + svn_fs_t *fs, + svn_fs_fs__stats_t *stats, + svn_fs_progress_notify_func_t progress_func, + void *progress_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *query = apr_pcalloc(result_pool, sizeof(**query)); + + /* Read repository dimensions. */ + (*query)->shard_size = svn_fs_fs__shard_size(fs); + SVN_ERR(svn_fs_fs__youngest_rev(&(*query)->head, fs, scratch_pool)); + SVN_ERR(svn_fs_fs__min_unpacked_rev(&(*query)->min_unpacked_rev, fs, + scratch_pool)); + + /* create data containers and caches + * Note: this assumes that int is at least 32-bits and that we only support + * 32-bit wide revision numbers (actually 31-bits due to the signedness + * of both the nelts field of the array and our revision numbers). This + * means this code will fail on platforms where int is less than 32-bits + * and the repository has more revisions than int can hold. */ + (*query)->revisions = apr_array_make(result_pool, (int) (*query)->head + 1, + sizeof(revision_info_t *)); + (*query)->null_base = apr_pcalloc(result_pool, + sizeof(*(*query)->null_base)); + + /* Store other parameters */ + (*query)->fs = fs; + (*query)->stats = stats; + (*query)->progress_func = progress_func; + (*query)->progress_baton = progress_baton; + (*query)->cancel_func = cancel_func; + (*query)->cancel_baton = cancel_baton; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__get_stats(svn_fs_fs__stats_t **stats, + svn_fs_t *fs, + svn_fs_progress_notify_func_t progress_func, + void *progress_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + query_t *query; + + *stats = create_stats(result_pool); + SVN_ERR(create_query(&query, fs, *stats, progress_func, progress_baton, + cancel_func, cancel_baton, scratch_pool, + scratch_pool)); + SVN_ERR(read_revisions(query, scratch_pool, scratch_pool)); + aggregate_stats(query->revisions, *stats); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/structure b/contrib/subversion/subversion/libsvn_fs_fs/structure index 41caf1d7b..7b5129f17 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/structure +++ b/contrib/subversion/subversion/libsvn_fs_fs/structure @@ -43,7 +43,7 @@ repository) is: .pack/ Pack directory, if the repo has been packed (see below) . Pack file, if the repository has been packed (see below) manifest Pack manifest file, if a pack file exists (see below) - revprops.db SQLite database of the packed revision properties + revprops.db SQLite database of the packed revprops (format 5 only) transactions/ Subdirectory containing transactions .txn/ Directory containing transaction txn-protorevs/ Subdirectory containing transaction proto-revision files @@ -58,12 +58,13 @@ repository) is: current File specifying current revision and next node/copy id fs-type File identifying this filesystem as an FSFS filesystem write-lock Empty file, locked to serialise writers + pack-lock Empty file, locked to serialise 'svnadmin pack' (f. 7+) txn-current-lock Empty file, locked to serialise 'txn-current' - uuid File containing the UUID of the repository + uuid File containing the repository IDs format File containing the format number of this filesystem fsfs.conf Configuration file min-unpacked-rev File containing the oldest revision not in a pack file - min-unpacked-revprop File containing the oldest revision of unpacked revprop + min-unpacked-revprop Same for revision properties (format 5 only) rep-cache.db SQLite database mapping rep checksums to locations Files in the revprops directory are in the hash dump format used by @@ -84,9 +85,19 @@ The "write-lock" file is an empty file which is locked before the final stage of a commit and unlocked after the new "current" file has been moved into place to indicate that a new revision is present. It is also locked during a revprop propchange while the revprop file is -read in, mutated, and written out again. Note that readers are never -blocked by any operation - writers must ensure that the filesystem is -always in a consistent state. +read in, mutated, and written out again. Furthermore, it will be used +to serialize the repository structure changes during 'svnadmin pack' +(see also next section). Note that readers are never blocked by any +operation - writers must ensure that the filesystem is always in a +consistent state. + +The "pack-lock" file is an empty file which is locked before an 'svnadmin +pack' operation commences. Thus, only one process may attempt to modify +the repository structure at a time while other processes may still read +and write (commit) to the repository during most of the pack procedure. +It is only available with format 7 and newer repositories. Older formats +use the global write-lock instead which disables commits completely +for the duration of the pack process. The "txn-current" file is a file with a single line of text that contains only a base-36 number. The current value will be used in the @@ -138,6 +149,7 @@ The formats are: Format 4, understood by Subversion 1.6+ Format 5, understood by Subversion 1.7-dev, never released Format 6, understood by Subversion 1.8 + Format 7, understood by Subversion 1.9 The differences between the formats are: @@ -148,6 +160,7 @@ Delta representation in revision files Format options Formats 1-2: none permitted Format 3+: "layout" option + Format 7+: "addressing" option Transaction name reuse Formats 1-2: transaction names may be reused @@ -176,6 +189,7 @@ Mergeinfo metadata: Revision changed paths list: Format 1-3: Does not contain the node's kind. Format 4+: Contains the node's kind. + Format 7+: Contains the mergeinfo-mod flag. Shard packing: Format 4: Applied to revision data only. @@ -183,15 +197,25 @@ Shard packing: Format 6+: Applied equally to revision data and revprop data (i.e. same min packed revision) +Addressing: + Format 1-6: Physical addressing; uses fixed positions within a rev file + Format 7+: Logical addressing; uses item index that will be translated + on-the-fly to the actual rev / pack file location + +Repository IDs: + Format 1+: The first line of db/uuid contains the repository UUID + Format 7+: The second line contains the instance ID (in UUID formatting) + # Incomplete list. See SVN_FS_FS__MIN_*_FORMAT Filesystem format options ------------------------- -Currently, the only recognised format option is "layout", which -specifies the paths that will be used to store the revision files and -revision property files. +Currently, the only recognised format options are "layout" and "addressing". +The first specifies the paths that will be used to store the revision +files and revision property files. The second specifies that logical to +physical address translation is required. The "layout" option is followed by the name of the filesystem layout and any required parameters. The default layout, if no "layout" @@ -219,19 +243,92 @@ The known layouts, and the parameters they require, are as follows: revs/0/ directory will contain revisions 0-999, revs/1/ will contain 1000-1999, and so on. +The "addressing" option is followed by the name of the addressing mode +and any required parameters. The default addressing, if no "addressing" +keyword is specified, is the 'physical' addressing. + +The supported modes, and the parameters they require, are as follows: + +"physical" + All existing and future revision files will use the traditional + physical addressing scheme. All references are given as rev/offset + pairs with "offset" being the byte offset relative to the beginning of + the revision in the respective rev or pack file. + +"logical" + All existing and future revision files will use logical + addressing. It is illegal to use logical addressing on non-sharded + repositories. + + +Addressing modes +---------------- + +Two addressing modes are supported in format 7: physical and logical +addressing. Both use the same address format but apply a different +interpretation to it. Older formats only support physical addressing. + +All items are addressed using pairs. In physical +addressing mode, item_index is the (ASCII decimal) number of bytes from +the start of the revision file to the start of the respective item. For +non-packed files that is also the absolute file offset. Revision pack +files simply concatenate multiple rev files, i.e. the absolute file offset +is determined as + + absolute offset = rev offset taken from manifest + item_index + +This simple addressing scheme makes it hard to change the location of +any item since that may break references from later revisions. + +Logical addressing uses an index file to translate the rev / item_index +pairs into absolute file offsets. There is one such index for every rev / +pack file using logical addressing and both are created in sync. That +makes it possible to reorder items during pack file creation, particularly +to mix items from different revisions. + +Some item_index values are pre-defined and apply to every revision: + + 0 ... not used / invalid + 1 ... changed path list + 2 ... root node revision + +A reverse index (phys-to-log) is being created as well that allows for +translating arbitrary file locations into item descriptions (type, rev, +item_index, on-disk length). Known item types + + 0 ... unused / empty section + 1 ... file representation + 2 ... directory representation + 3 ... file property representation + 4 ... directory property representation + 5 ... node revision + 6 ... changed paths list + +The various representation types all share the same morphology. The +distinction is only made to allow for more effective reordering heuristics. +Zero-length items are allowed. + + Packing revisions ----------------- A filesystem can optionally be "packed" to conserve space on disk. The packing process concatenates all the revision files in each full shard to -create pack files. A manifest file is also created for each shard which +create a pack file. The original shard is removed, and reads are +redirected to the pack file. + +With physical addressing, a manifest file is created for each shard which records the indexes of the corresponding revision files in the pack file. -In addition, the original shard is removed, and reads are redirected to the -pack file. +The manifest file consists of a list of offsets, one for each revision in +the pack file. The offsets are stored as ASCII decimal, and separated by +a newline character. + +Revision pack files using logical addressing don't use manifest files but +appends index data to the revision contents. The revisions inside a pack +file will also get interleaved to reduce I/O for typical access patterns. +There is no structural difference between packed and non-packed revision +files in that mode. -The manifest file consists of a list of offsets, one for each revision in the -pack file. The offsets are stored as ASCII decimal, and separated by a newline -character. Packing revision properties (format 5: SQLite) --------------------------- @@ -341,13 +438,12 @@ Within a new transaction: Within a revision: Within a revision file, node-revs have a txn-id field of the form - "r/", to support easy lookup. The is the (ASCII - decimal) number of bytes from the start of the revision file to the - start of the node-rev. + "r/", to support easy lookup. See addressing modes + for details. During the final phase of a commit, node-revision IDs are rewritten to have repository-wide unique node-ID and copy-ID fields, and to have - "r/" txn-id fields. + "r/" txn-id fields. In Format 3 and above, this uniqueness is done by changing a temporary id of "_" to "-". Note that this means that the @@ -429,13 +525,14 @@ A revision file contains a concatenation of various kinds of data: * Text and property representations * Node-revisions * The changed-path data - * Two offsets at the very end + * Index data (logical addressing only) + * Revision / pack file footer (logical addressing only) A representation begins with a line containing either "PLAIN\n" or -"DELTA\n" or "DELTA \n", where , , -and give the location of the delta base of the representation -and the amount of data it contains (not counting the header or -trailer). If no base location is given for a delta, the base is the +"DELTA\n" or "DELTA \n", where , +, and give the location of the delta base of the +representation and the amount of data it contains (not counting the header +or trailer). If no base location is given for a delta, the base is the empty stream. After the initial line comes raw svndiff data, followed by a cosmetic trailer "ENDREP\n". @@ -459,12 +556,11 @@ defined: type "file" or "dir" pred The ID of the predecessor node-rev count Count of node-revs since the base of the node - text " " for text rep - props " " for props rep - and give location of rep + text " " for text rep + props " " for props rep + and give location of rep gives length of rep, sans header and trailer - gives size of expanded rep; may be 0 if equal - to the length + gives size of expanded rep (*) gives hex MD5 digest of expanded rep ### in formats >=4, also present: gives hex SHA1 digest of expanded rep @@ -476,6 +572,16 @@ defined: which have svn:mergeinfo. minfo-here Exists if this node itself has svn:mergeinfo. +(*) Earlier versions of this document would state that may be 0 + if the actual value matches . This is only true for property + and directory representations and should be avoided in general. File + representations may not be handled correctly by SVN before 1.7.20, + 1.8.12 and 1.9.0, if they have 0 fields for non-empty contents. + Releases 1.8.0 through 1.8.11 may have falsely created instances of + that (see issue #4554). Finally, 0 fields are only ever legal + for DELTA representations if the reconstructed full-text is actually + empty. + The predecessor of a node-rev crosses both soft and true copies; together with the count field, it allows efficient determination of the base for skip-deltas. The first node-rev of a node contains no @@ -489,28 +595,40 @@ of the copy; it may be omitted if the node-rev is its own copy root of revision 0). Copy roots are identified by revision and created-path, not by node-rev ID, because a copy root may be a node-rev which exists later on within the same revision file, meaning -its offset is not yet known. +its location is not yet known. The changed-path data is represented as a series of changed-path items, each consisting of two lines. The first line has the format -" \n", where is the -node-rev ID of the new node-rev, is "add", "delete", -"replace", or "modify", and are "true" or -"false" indicating whether the text and/or properties changed, and - is the changed pathname. For deletes, is the node-rev ID -of the deleted node-rev, and and are always -"false". The second line has the format " \n" containing -the node-rev's copyfrom information if it has any; if it does not, the -second line is blank. +" \n", +where is the node-rev ID of the new node-rev, is "add", +"delete", "replace", or "modify", , , and + are "true" or "false" indicating whether the text, +properties and/or mergeinfo changed, and is the changed pathname. +For deletes, is the node-rev ID of the deleted node-rev, and + and are always "false". The second line has the +format " \n" containing the node-rev's copyfrom information +if it has any; if it does not, the second line is blank. Starting with FS format 4, may contain the kind ("file" or "dir") of the node, after a hyphen; for example, an added directory may be represented as "add-dir". -At the very end of a rev file is a pair of lines containing -"\n \n", where is the offset of -the root directory node revision and is the offset of the -changed-path data. +Prior to FS format 7, flag is not available. It may +also be missing in revisions upgraded from pre-f7 formats. + +In physical addressing mode, at the very end of a rev file is a pair of +lines containing "\n \n", where is +the offset of the root directory node revision and is the +offset of the changed-path data. + +In logical addressing mode, the revision footer has the form + + + +The terminal byte contains the length (as plain 8 bit value) of the footer +excluding that length byte. The first offset is the start of the log-to- +phys index, followed by the digest of the MD5 checksum over its content. +The other pair gives the same of for the phys-to-log index. All numbers in the rev file format are unsigned and are represented as ASCII decimal. @@ -521,6 +639,7 @@ Transaction layout A transaction directory has the following layout: props Transaction props + props-final Final transaction props (optional) next-ids Next temporary node-ID and copy-ID changes Changed-path information so far node.. New node-rev data for node @@ -533,19 +652,29 @@ In FS formats 1 and 2, it also contains: rev Prototype rev file with new text reps rev-lock Lockfile for writing to the above -In newer formats, these files are in the txn-protorevs/ directory. +(In newer formats, these files are in the txn-protorevs/ directory.) + +In format 7+ logical addressing mode, it contains two additional index +files (see structure-indexes for a detailed description) and one more +counter file: + + itemidx Next item_index value as decimal integer + index.l2p Log-to-phys proto-index + index.p2l Phys-to-log proto-index The prototype rev file is used to store the text representations as they are received from the client. To ensure that only one client is writing to the file at a given time, the "rev-lock" file is locked for the duration of each write. -The two kinds of props files are all in hash dump format. The "props" +The three kinds of props files are all in hash dump format. The "props" file will always be present. The "node...props" file will -only be present if the node-rev properties have been changed. +only be present if the node-rev properties have been changed. The +"props-final" only exists while converting the transaction into a revision. + The files have been introduced in FS format 6. Their content -is that of text rep references: " " +is that of text rep references: " " They will be written for text reps in the current transaction and be used to eliminate duplicate reps within that transaction. @@ -619,3 +748,15 @@ reference the same path as above, but look for a list of children in that file (instead of lock information). Children are listed as MD5 digests, too, so you would simply iterate over those digests and consult the files they reference for lock information. + + +Index Data +---------- + +Format 7 introduces logical addressing that requires item indexes +to be translated / mapped to physical rev / pack file offsets. +These indexes are appended to the respective rev / pack file. + +Details of the binary format used by these index files can be +found in structure-indexes. + diff --git a/contrib/subversion/subversion/libsvn_fs_fs/structure-indexes b/contrib/subversion/subversion/libsvn_fs_fs/structure-indexes new file mode 100644 index 000000000..25490c7ca --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/structure-indexes @@ -0,0 +1,352 @@ +This file describes the design, data model, and storage formats of FSFS +index data. + + +Design +====== + +Each pack and each rev file using logical addressing contains exactly +two index sections. One, the log-to-phys index, maps the (rev, item_index) +pairs to absolute file offsets. The other, phys-to-log, is a reverse +index that gives basic information on any file location. This is enough +to read and cache any data without traversing DAGs. + +Rev and pack files are immutable, so the same is true for index data. +During a transaction or while packing a file, a proto index file gets +written (actually, one log-to-phys and one phys-to-log). Its format is +a simple concatenation of runtime structs and as such, an implementation +detail subject to change. A proto index basically aggregates all the +information that must later be transformed into the final index. + + +General design concerns +----------------------- + +In Subversion, there is no limit to the size of a revision; even practical +limits are in the order of millions of changes at least. Index data for +these would be multiple megabytes in size with pack file indexes possibly +approaching 1 GB. To ensure we still get roughly O(1) access time, we +need a hierarchical data structure. + +Therefore, the indexes will start with a header containing an array of +references to sub-sections or pages. The length of these pages varies +but is limited to a size configurable in fsfs.conf. + +Finally, it is assumed that whole pages can be cached efficiently and +with a high cache hit rate. So, although a page may have a thousand or +more entries, the access time is still amortized O(1) in many scenarios. + + +Items and item types +-------------------- + +The index implementation treats item_index and item type as simple ints, +except for SVN_FS_FS__ITEM_INDEX_UNUSED and SVN_FS_FS__ITEM_TYPE_UNUSED. +Since they have been defined as 0, the code may test for "used" etc. +by simply comparing with 0. + +See section "addressing modes" in structure to a list of item types +and pre-defined item_index values. + + +Encoding +-------- + +The final index data format is tuned for space and decoding efficiency. +Indexes are stored as a sequence of variable integers. The encoding is +as follows: + +* Unsigned integers are stored in little endian order with a variable + length 7b/8b encoding. If most significant bit a byte has been set, + the next byte has also belongs to the same value. + + 0x00 .. 0x7f -> 0x00 .. 0x7f ( 7 bits stored in 8 bits) + 0x80 .. 0xff -> 0x80 0x01 .. 0xff 0x01 (14 bits stored in 16 bits) + 0x100 .. 0x3fff -> 0x80 0x02 .. 0xff 0x7f (14 bits stored in 16 bits) + 0x100000000 -> 0x80 0x80 0x80 0x80 0x10 (35 bits stored in 40 bits) + + Technically, we can represent integers of arbitrary lengths. Currently, + we only generate and parse up to 64 bits. + +* Signed integers are mapped onto the unsigned value space as follows: + + x >= 0 -> 2 * x + x < 0 -> -2 * x - 1 + + Again, we can represent arbitrary length numbers that way but the code + is currently restricted to 64 bits. + +Most data is unsigned by nature but will be stored differentially using +signed integers. + + +Encoding in proto-index files +----------------------------- + +These have a much simpler encoding. Throughout the files, all records have +the same length (but different between L2P and P2L). All records contain +unsigned 64 bit integers only, stored in little endian byte order. + + +Log-to-phys index +================= + +This index has to map (rev, item_index) -> offset. It assumes that the +item_index values per revision are dense and start at 0. There may be +unused item_index values, though; the data structure simply gets less +space-efficient when the more sparse the value space gets. + + +Index data model +---------------- + +hierarchy: + + header -> per-revision info -> page -> offset + + There is one entry per revision in the header. Per revision there are + one or more pages (exclusive to that revision) containing up to a known, + fixed limit of entries (= page size). The total access path is: + + pages = header->pages[revision]; + offsets = page = pages[item_index / page_size]; + offset = offsets[item_index % page_size]; + + Different log-to-phys indexes in the same repository may have different + page sizes but within any given index, the page size is the same and + immutable. + +header: + + ... first revision covered by this index + ... number of revision covered by this index + ... maximum number of entries per page + ... array, for each revision containing the index in + of the first page that belongs to + this revision. This has +1 + entries to terminate the last revision. + ... array of page headers. It has + [] entries. + +page table: + + ... absolute position of the page contents within the + index + ... number of offset entries in the page. + Must match
. unless this is + the last page for the respective revision. + ... length in bytes of the on-disk page description. + Note that this is redundant with the . + +page: + + ... number of offset entries in the page. + Must match
. unless this is + the last page for the respective revision. + Redundant with . + ... array of absolute file positions within the rev / + pack file. This has entries. + + +Index on-disk format +-------------------- + + index := "L2P-INDEX\n" header revisions pages offsets + + header := u(
.) \ + u(
.) \ + u(
.) \ + u(s(
.)) + + revisions := u(
.[k+1] + -
.[k]), + for k in 0 ..
.-1 + + pages := u(
.[k].) \ + u(
.[k].), + for k in 0 .. s(
.)-1 + + offsets := page(k), + for k in 0 .. s(
.)-1 + + page(k) := i(
.[k].[0]) \ + i(
.[k].[l] \ + -
.[k].[l - 1]), + for l in 1 .. s(
.[k].)-1 + + u(x) ... unsigned int x in 7b/8b encoding + i(x) ... signed int x in 7b/8b encoding + s(x) ... number of entries in array x + + +Proto index file format +----------------------- + +The index will be created from a "revision-less" proto index file +containing () pairs only. + +All mappings belonging to the same revision must be written in one go but +there is no restriction on the order of those entries. To signify that +a new revision begins, a (0, 0) mapping must be written. A (0, 0) entry +at the beginning of the file is optional and will be ignored. + + /* begin of proto index file for revision r and following */ + (0, 0) /* mark start of revision r, optional for first rev */ + (off, item)* /* zero to many mappings in random order */ + (0, 0) /* mark start of revision r + 1 */ + (off, item)* /* zero to many mappings in random order */ + (0, 0) /* mark start of revision r + 2 */ + (off, item)* /* zero to many mappings in random order */ + ... + /* end of file. */ + +All entries are pairs of 64 bit unsigned integers in little endian order. + + +Phys-to-log index +================= + +This index has to map offset -> (rev, item_index, type, len, checksum). + + +Index data model +---------------- + +hierarchy: + + header -> page -> item info + + Logically, the index splits up index rev / pack file into pages of a + fixed size. That page size may differ from the FS's block size. The + index will describe each rev / pack file page with one index page. + + page = header->pages[offset % page_size]; + item info = binary_search(page.data, offset) + + Note that the current implementation will not return any data if the + offset is does not match any actual item start. To simplify the lookup, + the last index page will have an "unused item" entry for the section + behind EOF. Holes aren't allowed as well, i.e. every byte of the rev / + pack is expected to be covered by the index. + + Also, there may be items stretching across page borders or even over + multiple pages. The data model solves this issue by storing the item + descriptions as a "primary array" and then representing the pages as + ranges within that array. Thus multiple pages may reference the same + item description. + +header: + + ... first revision covered by this index + ... size of the rev / pack file in bytes + ... number of bytes in the rev / pack file covered by + each index page + ... number of pages + ... array of page offsets, i.e. locations the page + data within the index. + This array has + 1 entries. + +page: + + ... array of item descriptions, ordered by offset. + First and last entry may cross page boundaries. + +entry: + + ... absolute position in the pack / rev file + ... on-disk size of the item in the pack / rev file + ... item type + ... modified 32 bit FNV-1a checksum of that section + of the pack / rev file (see below). 0 for empty + or zero-length items + ... revision that this item belongs to + ... item_index within that revision + + +Index on-disk format +-------------------- + + index := "P2L-INDEX\n" header pages items + + header := u(
.) \ + u(
.) \ + u(
.) \ + u(
.) + + pages := u(
.[k+1] -
.[k]), + for k in 0 ..
. -1 + + items := u([0].) \ + u([l].) \ + i(c([l]) - c([l-1])) \ + i( [l]. + - [l-1].), \ + u(FNV checksum) + for l in 0 .. s()-1, + for k in 0 ..
.-1 + + u(x) ... unsigned int x in 7b/8b encoding + i(x) ... signed int x in 7b/8b encoding + s(x) ... number of entries in collection x + c(x) ... compound function := x. * 8 + x. + + Access to negative indexes gives a 0 value. + + are in strict ascending offset order. Items that + started after the begin of a given page and overlap with the next page + will not be stored in the start page. The runtime representation will + duplicate items overlapping page boundaries; the on-disk representation + will not. Thus, pages inside large items will have zero entries on disk. + + +Proto index file format +----------------------- + +The index will be created from a proto index file containing simple +instances of svn_fs_fs__p2l_entry_t with the following element order: + + item offset as uint64 + item size as uint64 + item type as uint64 + modified FNV1a checksum as uint64 + revision as uint64, with SVN_INVALID_REVNUM mapped to 0 + and revisions >= 0 stored as rev+1 + item index as uint64 + +All values are stored in little endian order. + +Page table and header information, except start revision and page size, +can easily be derived from that information. + +All entries must be written in strict offset order. Overlapping entries +are not allowed; zero-length items are. + +In transactions, the final revision number may not be known when writing +the proto index file (e.g. while still writing the proto rev file). Items +with revision set to SVN_INVALID_REVNUM will therefore be automatically +updated when creating the final index. This is possible in conjunction +with rev files but not for pack files. + + +FNV checksum +------------ + +FNV-1a can be found here: http://www.isthe.com/chongo/tech/comp/fnv/ +For performance reasons we use a modified version: + +* split the input byte stream [b0 .. bN] into 4 sub-streams of equal + length and up to 3 remnants: + + [b0 b4 b8 ..], [b1 b5 b9 ..], [b2 b6 b10 ..], [b3 b7 b11 ..], [remnant] + +* calculate 32 bit FNV-1a checksums for the 4 substreams: + + h0 = fnv_1a([b0 b4 b8 ..]), ..., h3 = fnv_1a([b3 b7 b11 ..]) + +* combine the big endian representation of these checksums plus the + remnant of the original stream into a 12 to 15 byte long intermediate + + [i0 .. iK], 12 <= K+1 <= 15 + +* FNV checksum = fnv_1a([i0 .. iK]) in big endian representation + diff --git a/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.c b/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.c index 017814381..4e7ae2d1a 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.c @@ -24,15 +24,18 @@ #include "svn_pools.h" #include "svn_hash.h" - -#include "id.h" +#include "svn_sorts.h" #include "svn_fs.h" #include "private/svn_fs_util.h" +#include "private/svn_sorts_private.h" #include "private/svn_temp_serializer.h" #include "private/svn_subr_private.h" +#include "id.h" #include "temp_serializer.h" +#include "low_level.h" +#include "cached_data.h" /* Utility to encode a signed NUMBER into a variable-length sequence of * 8-bit chars in KEY_BUFFER and return the last writen position. @@ -106,13 +109,12 @@ serialize_svn_string(svn_temp_serializer__context_t *context, /* the "string" content may actually be arbitrary binary data. * Thus, we cannot use svn_temp_serializer__add_string. */ - svn_temp_serializer__push(context, - (const void * const *)&string->data, - string->len + 1); + svn_temp_serializer__add_leaf(context, + (const void * const *)&string->data, + string->len + 1); /* back to the caller's nesting level */ svn_temp_serializer__pop(context); - svn_temp_serializer__pop(context); } /* Utility function to deserialize the STRING inside the BUFFER. @@ -127,44 +129,6 @@ deserialize_svn_string(void *buffer, svn_string_t **string) svn_temp_deserializer__resolve(*string, (void **)&(*string)->data); } -/* Utility function to serialize checkum CS within the given serialization - * CONTEXT. - */ -static void -serialize_checksum(svn_temp_serializer__context_t *context, - svn_checksum_t * const *cs) -{ - const svn_checksum_t *checksum = *cs; - if (checksum == NULL) - return; - - svn_temp_serializer__push(context, - (const void * const *)cs, - sizeof(*checksum)); - - /* The digest is arbitrary binary data. - * Thus, we cannot use svn_temp_serializer__add_string. */ - svn_temp_serializer__push(context, - (const void * const *)&checksum->digest, - svn_checksum_size(checksum)); - - /* return to the caller's nesting level */ - svn_temp_serializer__pop(context); - svn_temp_serializer__pop(context); -} - -/* Utility function to deserialize the checksum CS inside the BUFFER. - */ -static void -deserialize_checksum(void *buffer, svn_checksum_t **cs) -{ - svn_temp_deserializer__resolve(buffer, (void **)cs); - if (*cs == NULL) - return; - - svn_temp_deserializer__resolve(*cs, (void **)&(*cs)->digest); -} - /* Utility function to serialize the REPRESENTATION within the given * serialization CONTEXT. */ @@ -177,48 +141,17 @@ serialize_representation(svn_temp_serializer__context_t *context, return; /* serialize the representation struct itself */ - svn_temp_serializer__push(context, - (const void * const *)representation, - sizeof(*rep)); - - /* serialize sub-structures */ - serialize_checksum(context, &rep->md5_checksum); - serialize_checksum(context, &rep->sha1_checksum); - - svn_temp_serializer__add_string(context, &rep->txn_id); - svn_temp_serializer__add_string(context, &rep->uniquifier); - - /* return to the caller's nesting level */ - svn_temp_serializer__pop(context); -} - -/* Utility function to deserialize the REPRESENTATIONS inside the BUFFER. - */ -static void -deserialize_representation(void *buffer, - representation_t **representation) -{ - representation_t *rep; - - /* fixup the reference to the representation itself */ - svn_temp_deserializer__resolve(buffer, (void **)representation); - rep = *representation; - if (rep == NULL) - return; - - /* fixup of sub-structures */ - deserialize_checksum(rep, &rep->md5_checksum); - deserialize_checksum(rep, &rep->sha1_checksum); - - svn_temp_deserializer__resolve(rep, (void **)&rep->txn_id); - svn_temp_deserializer__resolve(rep, (void **)&rep->uniquifier); + svn_temp_serializer__add_leaf(context, + (const void * const *)representation, + sizeof(*rep)); } -/* auxilliary structure representing the content of a directory hash */ -typedef struct hash_data_t +/* auxiliary structure representing the content of a directory array */ +typedef struct dir_data_t { - /* number of entries in the directory */ - apr_size_t count; + /* number of entries in the directory + * (it's int because the directory is an APR array) */ + int count; /* number of unused dir entry buckets in the index */ apr_size_t over_provision; @@ -238,14 +171,7 @@ typedef struct hash_data_t /* size of the serialized entries and don't be too wasteful * (needed since the entries are no longer in sequence) */ apr_uint32_t *lengths; -} hash_data_t; - -static int -compare_dirent_id_names(const void *lhs, const void *rhs) -{ - return strcmp((*(const svn_fs_dirent_t *const *)lhs)->name, - (*(const svn_fs_dirent_t *const *)rhs)->name); -} +} dir_data_t; /* Utility function to serialize the *ENTRY_P into a the given * serialization CONTEXT. Return the serialized size of the @@ -276,91 +202,85 @@ serialize_dir_entry(svn_temp_serializer__context_t *context, * context to be returned. Allocation will be made form POOL. */ static svn_temp_serializer__context_t * -serialize_dir(apr_hash_t *entries, apr_pool_t *pool) +serialize_dir(apr_array_header_t *entries, apr_pool_t *pool) { - hash_data_t hash_data; - apr_hash_index_t *hi; - apr_size_t i = 0; + dir_data_t dir_data; + int i = 0; svn_temp_serializer__context_t *context; /* calculate sizes */ - apr_size_t count = apr_hash_count(entries); + int count = entries->nelts; apr_size_t over_provision = 2 + count / 4; apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*); apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t); - /* copy the hash entries to an auxilliary struct of known layout */ - hash_data.count = count; - hash_data.over_provision = over_provision; - hash_data.operations = 0; - hash_data.entries = apr_palloc(pool, entries_len); - hash_data.lengths = apr_palloc(pool, lengths_len); + /* copy the hash entries to an auxiliary struct of known layout */ + dir_data.count = count; + dir_data.over_provision = over_provision; + dir_data.operations = 0; + dir_data.entries = apr_palloc(pool, entries_len); + dir_data.lengths = apr_palloc(pool, lengths_len); - for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi), ++i) - hash_data.entries[i] = svn__apr_hash_index_val(hi); - - /* sort entry index by ID name */ - qsort(hash_data.entries, - count, - sizeof(*hash_data.entries), - compare_dirent_id_names); + for (i = 0; i < count; ++i) + dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); /* Serialize that aux. structure into a new one. Also, provide a good * estimate for the size of the buffer that we will need. */ - context = svn_temp_serializer__init(&hash_data, - sizeof(hash_data), + context = svn_temp_serializer__init(&dir_data, + sizeof(dir_data), 50 + count * 200 + entries_len, pool); /* serialize entries references */ svn_temp_serializer__push(context, - (const void * const *)&hash_data.entries, + (const void * const *)&dir_data.entries, entries_len); /* serialize the individual entries and their sub-structures */ for (i = 0; i < count; ++i) serialize_dir_entry(context, - &hash_data.entries[i], - &hash_data.lengths[i]); + &dir_data.entries[i], + &dir_data.lengths[i]); svn_temp_serializer__pop(context); /* serialize entries references */ svn_temp_serializer__push(context, - (const void * const *)&hash_data.lengths, + (const void * const *)&dir_data.lengths, lengths_len); return context; } -/* Utility function to reconstruct a dir entries hash from serialized data - * in BUFFER and HASH_DATA. Allocation will be made form POOL. +/* Utility function to reconstruct a dir entries array from serialized data + * in BUFFER and DIR_DATA. Allocation will be made form POOL. */ -static apr_hash_t * -deserialize_dir(void *buffer, hash_data_t *hash_data, apr_pool_t *pool) +static apr_array_header_t * +deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool) { - apr_hash_t *result = svn_hash__make(pool); + apr_array_header_t *result + = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *)); apr_size_t i; apr_size_t count; svn_fs_dirent_t *entry; svn_fs_dirent_t **entries; /* resolve the reference to the entries array */ - svn_temp_deserializer__resolve(buffer, (void **)&hash_data->entries); - entries = hash_data->entries; + svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries); + entries = dir_data->entries; /* fixup the references within each entry and add it to the hash */ - for (i = 0, count = hash_data->count; i < count; ++i) + for (i = 0, count = dir_data->count; i < count; ++i) { svn_temp_deserializer__resolve(entries, (void **)&entries[i]); - entry = hash_data->entries[i]; + entry = dir_data->entries[i]; /* pointer fixup */ svn_temp_deserializer__resolve(entry, (void **)&entry->name); svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id); /* add the entry to the hash */ - svn_hash_sets(result, entry->name, entry); + APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = entry; } /* return the now complete hash */ @@ -413,14 +333,63 @@ svn_fs_fs__noderev_deserialize(void *buffer, /* fixup of sub-structures */ svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->id); svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->predecessor_id); - deserialize_representation(noderev, &noderev->prop_rep); - deserialize_representation(noderev, &noderev->data_rep); + svn_temp_deserializer__resolve(noderev, (void **)&noderev->prop_rep); + svn_temp_deserializer__resolve(noderev, (void **)&noderev->data_rep); svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path); svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path); svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path); } +svn_error_t * +svn_fs_fs__serialize_raw_window(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool) +{ + svn_fs_fs__raw_cached_window_t *window = item; + svn_stringbuf_t *serialized; + + /* initialize the serialization process and allocate a buffer large + * enough to do prevent re-allocations. */ + svn_temp_serializer__context_t *context = + svn_temp_serializer__init(window, + sizeof(*window), + sizeof(*window) + window->window.len + 16, + pool); + + /* serialize the sub-structure(s) */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&window->window.data, + window->window.len + 1); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *buffer = serialized->data; + *buffer_size = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_raw_window(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool) +{ + svn_fs_fs__raw_cached_window_t *window = + (svn_fs_fs__raw_cached_window_t *)buffer; + + /* pointer reference fixup */ + svn_temp_deserializer__resolve(window, (void **)&window->window.data); + + /* done */ + *item = buffer; + + return SVN_NO_ERROR; +} + /* Utility function to serialize COUNT svn_txdelta_op_t objects * at OPS in the given serialization CONTEXT. @@ -434,10 +403,9 @@ serialize_txdelta_ops(svn_temp_serializer__context_t *context, return; /* the ops form a contiguous chunk of memory with no further references */ - svn_temp_serializer__push(context, - (const void * const *)ops, - count * sizeof(svn_txdelta_op_t)); - svn_temp_serializer__pop(context); + svn_temp_serializer__add_leaf(context, + (const void * const *)ops, + count * sizeof(svn_txdelta_op_t)); } /* Utility function to serialize W in the given serialization CONTEXT. @@ -551,7 +519,7 @@ svn_fs_fs__deserialize_manifest(void **out, return SVN_NO_ERROR; } -/* Auxilliary structure representing the content of a properties hash. +/* Auxiliary structure representing the content of a properties hash. This structure is much easier to (de-)serialize than an apr_hash. */ typedef struct properties_data_t @@ -621,7 +589,7 @@ svn_fs_fs__serialize_properties(void **data, svn_stringbuf_t *serialized; apr_size_t i; - /* create our auxilliary data structure */ + /* create our auxiliary data structure */ properties.count = apr_hash_count(hash); properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1)); properties.values = apr_palloc(pool, sizeof(const char*) * properties.count); @@ -629,8 +597,8 @@ svn_fs_fs__serialize_properties(void **data, /* populate it with the hash entries */ for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i) { - properties.keys[i] = svn__apr_hash_index_key(hi); - properties.values[i] = svn__apr_hash_index_val(hi); + properties.keys[i] = apr_hash_this_key(hi); + properties.values[i] = apr_hash_this_val(hi); } /* serialize it */ @@ -662,7 +630,7 @@ svn_fs_fs__deserialize_properties(void **out, properties_data_t *properties = (properties_data_t *)data; size_t i; - /* de-serialize our auxilliary data structure */ + /* de-serialize our auxiliary data structure */ svn_temp_deserializer__resolve(properties, (void**)&properties->keys); svn_temp_deserializer__resolve(properties, (void**)&properties->values); @@ -670,10 +638,10 @@ svn_fs_fs__deserialize_properties(void **out, for (i = 0; i < properties->count; ++i) { apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1; - svn_temp_deserializer__resolve((void*)properties->keys, + svn_temp_deserializer__resolve(properties->keys, (void**)&properties->keys[i]); - deserialize_svn_string((void*)properties->values, + deserialize_svn_string(properties->values, (svn_string_t **)&properties->values[i]); apr_hash_set(hash, @@ -785,7 +753,7 @@ return_serialized_dir_context(svn_temp_serializer__context_t *context, *data = serialized->data; *data_len = serialized->blocksize; - ((hash_data_t *)serialized->data)->len = serialized->len; + ((dir_data_t *)serialized->data)->len = serialized->len; return SVN_NO_ERROR; } @@ -796,7 +764,7 @@ svn_fs_fs__serialize_dir_entries(void **data, void *in, apr_pool_t *pool) { - apr_hash_t *dir = in; + apr_array_header_t *dir = in; /* serialize the dir content into a new serialization context * and return the serialized data */ @@ -812,10 +780,10 @@ svn_fs_fs__deserialize_dir_entries(void **out, apr_pool_t *pool) { /* Copy the _full_ buffer as it also contains the sub-structures. */ - hash_data_t *hash_data = (hash_data_t *)data; + dir_data_t *dir_data = (dir_data_t *)data; /* reconstruct the hash from the serialized data */ - *out = deserialize_dir(hash_data, hash_data, pool); + *out = deserialize_dir(dir_data, dir_data, pool); return SVN_NO_ERROR; } @@ -888,22 +856,22 @@ svn_fs_fs__extract_dir_entry(void **out, void *baton, apr_pool_t *pool) { - const hash_data_t *hash_data = data; + const dir_data_t *dir_data = data; const char* name = baton; svn_boolean_t found; /* resolve the reference to the entries array */ const svn_fs_dirent_t * const *entries = - svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->entries); + svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries); /* resolve the reference to the lengths array */ const apr_uint32_t *lengths = - svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->lengths); + svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths); /* binary search for the desired entry by name */ apr_size_t pos = find_entry((svn_fs_dirent_t **)entries, name, - hash_data->count, + dir_data->count, &found); /* de-serialize that entry or return NULL, if no match has been found */ @@ -942,14 +910,33 @@ slowly_replace_dir_entry(void **data, apr_pool_t *pool) { replace_baton_t *replace_baton = (replace_baton_t *)baton; - hash_data_t *hash_data = (hash_data_t *)*data; - apr_hash_t *dir; + dir_data_t *dir_data = (dir_data_t *)*data; + apr_array_header_t *dir; + int idx = -1; + svn_fs_dirent_t *entry; SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir, *data, - hash_data->len, + dir_data->len, pool)); - svn_hash_sets(dir, replace_baton->name, replace_baton->new_entry); + + entry = svn_fs_fs__find_dir_entry(dir, replace_baton->name, &idx); + + /* Replacement or removal? */ + if (replace_baton->new_entry) + { + /* Replace ENTRY with / insert the NEW_ENTRY */ + if (entry) + APR_ARRAY_IDX(dir, idx, svn_fs_dirent_t *) = replace_baton->new_entry; + else + svn_sort__array_insert(dir, &replace_baton->new_entry, idx); + } + else + { + /* Remove the old ENTRY. */ + if (entry) + svn_sort__array_delete(dir, idx, 1); + } return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool); } @@ -961,7 +948,7 @@ svn_fs_fs__replace_dir_entry(void **data, apr_pool_t *pool) { replace_baton_t *replace_baton = (replace_baton_t *)baton; - hash_data_t *hash_data = (hash_data_t *)*data; + dir_data_t *dir_data = (dir_data_t *)*data; svn_boolean_t found; svn_fs_dirent_t **entries; apr_uint32_t *lengths; @@ -971,23 +958,23 @@ svn_fs_fs__replace_dir_entry(void **data, svn_temp_serializer__context_t *context; /* after quite a number of operations, let's re-pack everything. - * This is to limit the number of vasted space as we cannot overwrite + * This is to limit the number of wasted space as we cannot overwrite * existing data but must always append. */ - if (hash_data->operations > 2 + hash_data->count / 4) + if (dir_data->operations > 2 + dir_data->count / 4) return slowly_replace_dir_entry(data, data_len, baton, pool); /* resolve the reference to the entries array */ entries = (svn_fs_dirent_t **) - svn_temp_deserializer__ptr((const char *)hash_data, - (const void *const *)&hash_data->entries); + svn_temp_deserializer__ptr(dir_data, + (const void *const *)&dir_data->entries); /* resolve the reference to the lengths array */ lengths = (apr_uint32_t *) - svn_temp_deserializer__ptr((const char *)hash_data, - (const void *const *)&hash_data->lengths); + svn_temp_deserializer__ptr(dir_data, + (const void *const *)&dir_data->lengths); /* binary search for the desired entry by name */ - pos = find_entry(entries, replace_baton->name, hash_data->count, &found); + pos = find_entry(entries, replace_baton->name, dir_data->count, &found); /* handle entry removal (if found at all) */ if (replace_baton->new_entry == NULL) @@ -997,14 +984,14 @@ svn_fs_fs__replace_dir_entry(void **data, /* remove reference to the entry from the index */ memmove(&entries[pos], &entries[pos + 1], - sizeof(entries[pos]) * (hash_data->count - pos)); + sizeof(entries[pos]) * (dir_data->count - pos)); memmove(&lengths[pos], &lengths[pos + 1], - sizeof(lengths[pos]) * (hash_data->count - pos)); + sizeof(lengths[pos]) * (dir_data->count - pos)); - hash_data->count--; - hash_data->over_provision++; - hash_data->operations++; + dir_data->count--; + dir_data->over_provision++; + dir_data->operations++; } return SVN_NO_ERROR; @@ -1016,27 +1003,27 @@ svn_fs_fs__replace_dir_entry(void **data, /* fallback to slow operation if there is no place left to insert an * new entry to index. That will automatically give add some spare * entries ("overprovision"). */ - if (hash_data->over_provision == 0) + if (dir_data->over_provision == 0) return slowly_replace_dir_entry(data, data_len, baton, pool); /* make entries[index] available for pointing to the new entry */ memmove(&entries[pos + 1], &entries[pos], - sizeof(entries[pos]) * (hash_data->count - pos)); + sizeof(entries[pos]) * (dir_data->count - pos)); memmove(&lengths[pos + 1], &lengths[pos], - sizeof(lengths[pos]) * (hash_data->count - pos)); + sizeof(lengths[pos]) * (dir_data->count - pos)); - hash_data->count++; - hash_data->over_provision--; - hash_data->operations++; + dir_data->count++; + dir_data->over_provision--; + dir_data->operations++; } /* de-serialize the new entry */ entries[pos] = replace_baton->new_entry; - context = svn_temp_serializer__init_append(hash_data, + context = svn_temp_serializer__init_append(dir_data, entries, - hash_data->len, + dir_data->len, *data_len, pool); serialize_dir_entry(context, &entries[pos], &length); @@ -1050,15 +1037,45 @@ svn_fs_fs__replace_dir_entry(void **data, * pointer may no longer point to the entry in that buffer. Therefore, * re-map it again and store the length value after that. */ - hash_data = (hash_data_t *)*data; + dir_data = (dir_data_t *)*data; lengths = (apr_uint32_t *) - svn_temp_deserializer__ptr((const char *)hash_data, - (const void *const *)&hash_data->lengths); + svn_temp_deserializer__ptr(dir_data, + (const void *const *)&dir_data->lengths); lengths[pos] = length; return SVN_NO_ERROR; } +svn_error_t * +svn_fs_fs__serialize_rep_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy)); + *copy = *(svn_fs_fs__rep_header_t *)in; + + *data_len = sizeof(svn_fs_fs__rep_header_t); + *data = copy; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__deserialize_rep_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy)); + SVN_ERR_ASSERT(data_len == sizeof(*copy)); + + *copy = *(svn_fs_fs__rep_header_t *)data; + *out = data; + + return SVN_NO_ERROR; +} + /* Utility function to serialize change CHANGE_P in the given serialization * CONTEXT. */ @@ -1076,10 +1093,10 @@ serialize_change(svn_temp_serializer__context_t *context, sizeof(*change)); /* serialize sub-structures */ - svn_fs_fs__id_serialize(context, &change->noderev_id); + svn_fs_fs__id_serialize(context, &change->info.node_rev_id); - svn_temp_serializer__add_string(context, &change->path); - svn_temp_serializer__add_string(context, &change->copyfrom_path); + svn_temp_serializer__add_string(context, &change->path.data); + svn_temp_serializer__add_string(context, &change->info.copyfrom_path); /* return to the caller's nesting level */ svn_temp_serializer__pop(context); @@ -1101,10 +1118,10 @@ deserialize_change(void *buffer, change_t **change_p) return; /* fix-up of sub-structures */ - svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->noderev_id); + svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->info.node_rev_id); - svn_temp_deserializer__resolve(change, (void **)&change->path); - svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path); + svn_temp_deserializer__resolve(change, (void **)&change->path.data); + svn_temp_deserializer__resolve(change, (void **)&change->info.copyfrom_path); } /* Auxiliary structure representing the content of a change_t array. @@ -1131,18 +1148,15 @@ svn_fs_fs__serialize_changes(void **data, svn_stringbuf_t *serialized; int i; - /* initialize our auxiliary data structure */ + /* initialize our auxiliary data structure and link it to the + * array elements */ changes.count = array->nelts; - changes.changes = apr_palloc(pool, sizeof(change_t*) * changes.count); - - /* populate it with the array elements */ - for (i = 0; i < changes.count; ++i) - changes.changes[i] = APR_ARRAY_IDX(array, i, change_t*); + changes.changes = (change_t **)array->elts; /* serialize it and all its elements */ context = svn_temp_serializer__init(&changes, sizeof(changes), - changes.count * 100, + changes.count * 250, pool); svn_temp_serializer__push(context, @@ -1171,19 +1185,21 @@ svn_fs_fs__deserialize_changes(void **out, { int i; changes_data_t *changes = (changes_data_t *)data; - apr_array_header_t *array = apr_array_make(pool, changes->count, - sizeof(change_t *)); + apr_array_header_t *array = apr_array_make(pool, 0, sizeof(change_t *)); /* de-serialize our auxiliary data structure */ svn_temp_deserializer__resolve(changes, (void**)&changes->changes); /* de-serialize each entry and add it to the array */ for (i = 0; i < changes->count; ++i) - { - deserialize_change((void*)changes->changes, - (change_t **)&changes->changes[i]); - APR_ARRAY_PUSH(array, change_t *) = changes->changes[i]; - } + deserialize_change(changes->changes, + (change_t **)&changes->changes[i]); + + /* Use the changes buffer as the array's data buffer + * (DATA remains valid for at least as long as POOL). */ + array->elts = (char *)changes->changes; + array->nelts = changes->count; + array->nalloc = changes->count; /* done */ *out = array; @@ -1252,7 +1268,7 @@ svn_fs_fs__serialize_mergeinfo(void **data, i = 0; for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) { - svn_rangelist_t *ranges = svn__apr_hash_index_val(hi); + svn_rangelist_t *ranges = apr_hash_this_val(hi); for (k = 0; k < ranges->nelts; ++k, ++i) merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*); } @@ -1274,22 +1290,19 @@ svn_fs_fs__serialize_mergeinfo(void **data, svn_temp_serializer__pop(context); /* key lengths array */ - svn_temp_serializer__push(context, - (const void * const *)&merges.key_lengths, - merges.count * sizeof(*merges.key_lengths)); - svn_temp_serializer__pop(context); + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.key_lengths, + merges.count * sizeof(*merges.key_lengths)); /* range counts array */ - svn_temp_serializer__push(context, - (const void * const *)&merges.range_counts, - merges.count * sizeof(*merges.range_counts)); - svn_temp_serializer__pop(context); + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.range_counts, + merges.count * sizeof(*merges.range_counts)); /* ranges */ - svn_temp_serializer__push(context, - (const void * const *)&merges.ranges, - range_count * sizeof(*merges.ranges)); - svn_temp_serializer__pop(context); + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.ranges, + range_count * sizeof(*merges.ranges)); /* return the serialized result */ serialized = svn_temp_serializer__get(context); @@ -1328,7 +1341,7 @@ svn_fs_fs__deserialize_mergeinfo(void **out, for (k = 0; k < merges->range_counts[i]; ++k, ++n) APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n]; - svn_temp_deserializer__resolve((void*)merges->keys, + svn_temp_deserializer__resolve(merges->keys, (void**)&merges->keys[i]); apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges); } diff --git a/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.h b/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.h index 1009d6340..4d14b01fe 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/temp_serializer.h @@ -49,9 +49,42 @@ void svn_fs_fs__noderev_deserialize(void *buffer, node_revision_t **noderev_p); + +/** + * Adds position information to the the raw window data in WINDOW. + */ +typedef struct +{ + /* the (unprocessed) txdelta window byte sequence cached / to be cached */ + svn_string_t window; + + /* the offset within the representation right after reading the window */ + apr_off_t end_offset; +} svn_fs_fs__raw_cached_window_t; + +/** + * Implements #svn_cache__serialize_func_t for + * #svn_fs_fs__raw_cached_window_t. + */ +svn_error_t * +svn_fs_fs__serialize_raw_window(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for + * #svn_fs_fs__raw_cached_window_t. + */ +svn_error_t * +svn_fs_fs__deserialize_raw_window(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool); + /** * #svn_txdelta_window_t is not sufficient for caching the data it - * represents because data read process needs auxilliary information. + * represents because data read process needs auxiliary information. */ typedef struct { @@ -159,7 +192,7 @@ svn_fs_fs__deserialize_node_revision(void **item, apr_pool_t *pool); /** - * Implements #svn_cache__serialize_func_t for a directory contents hash + * Implements #svn_cache__serialize_func_t for a directory contents array */ svn_error_t * svn_fs_fs__serialize_dir_entries(void **data, @@ -168,7 +201,7 @@ svn_fs_fs__serialize_dir_entries(void **data, apr_pool_t *pool); /** - * Implements #svn_cache__deserialize_func_t for a directory contents hash + * Implements #svn_cache__deserialize_func_t for a directory contents array */ svn_error_t * svn_fs_fs__deserialize_dir_entries(void **out, @@ -225,6 +258,24 @@ svn_fs_fs__replace_dir_entry(void **data, void *baton, apr_pool_t *pool); +/** + * Implements #svn_cache__serialize_func_t for a #svn_fs_fs__rep_header_t. + */ +svn_error_t * +svn_fs_fs__serialize_rep_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for a #svn_fs_fs__rep_header_t. + */ +svn_error_t * +svn_fs_fs__deserialize_rep_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + /** * Implements #svn_cache__serialize_func_t for an #apr_array_header_t of * #change_t *. diff --git a/contrib/subversion/subversion/libsvn_fs_fs/transaction.c b/contrib/subversion/subversion/libsvn_fs_fs/transaction.c new file mode 100644 index 000000000..3968c6fa5 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/transaction.c @@ -0,0 +1,3858 @@ +/* transaction.c --- transaction-related functions of FSFS + * + * ==================================================================== + * 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 "transaction.h" + +#include +#include + +#include "svn_hash.h" +#include "svn_props.h" +#include "svn_sorts.h" +#include "svn_time.h" +#include "svn_dirent_uri.h" + +#include "fs_fs.h" +#include "index.h" +#include "tree.h" +#include "util.h" +#include "id.h" +#include "low_level.h" +#include "temp_serializer.h" +#include "cached_data.h" +#include "lock.h" +#include "rep-cache.h" + +#include "private/svn_fs_util.h" +#include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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 svn_fs_fs__id_part_t *txn_id, + const unsigned char *sha1, + apr_pool_t *pool) +{ + svn_checksum_t checksum; + checksum.digest = sha1; + checksum.kind = svn_checksum_sha1; + + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + svn_checksum_to_cstring(&checksum, pool), + pool); +} + +static APR_INLINE const char * +path_txn_changes(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_CHANGES, pool); +} + +static APR_INLINE const char * +path_txn_props(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_TXN_PROPS, pool); +} + +static APR_INLINE const char * +path_txn_props_final(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_TXN_PROPS_FINAL, pool); +} + +static APR_INLINE const char * +path_txn_next_ids(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_NEXT_IDS, pool); +} + + +/* 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 +}; + +/* FSFS-specific data being attached to svn_fs_txn_t. + */ +typedef struct fs_txn_data_t +{ + /* Strongly typed representation of the TXN's ID member. */ + svn_fs_fs__id_part_t txn_id; +} fs_txn_data_t; + +const svn_fs_fs__id_part_t * +svn_fs_fs__txn_get_id(svn_fs_txn_t *txn) +{ + fs_txn_data_t *ftd = txn->fsap_data; + return &ftd->txn_id; +} + +/* 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 svn_fs_fs__id_part_t *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 (svn_fs_fs__id_part_eq(&txn->txn_id, txn_id)) + 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; + } + + txn->txn_id = *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 svn_fs_fs__id_part_t *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 (svn_fs_fs__id_part_eq(&txn->txn_id, txn_id)) + 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; +} + + +/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(), + which see. */ +struct unlock_proto_rev_baton +{ + svn_fs_fs__id_part_t 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; + apr_file_t *lockfile = b->lockcookie; + fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, &b->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'"), + svn_fs_fs__id_txn_unparse(&b->txn_id, pool)); + if (!txn->being_written) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Can't unlock nonlocked transaction '%s'"), + svn_fs_fs__id_txn_unparse(&b->txn_id, pool)); + + 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'"), + svn_fs_fs__id_txn_unparse(&b->txn_id, pool)); + 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'"), + svn_fs_fs__id_txn_unparse(&b->txn_id, pool)); + + 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 svn_fs_fs__id_part_t *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); +} + +/* A structure used by get_writable_proto_rev() and + get_writable_proto_rev_body(), which see. */ +struct get_writable_proto_rev_baton +{ + void **lockcookie; + svn_fs_fs__id_part_t 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; + void **lockcookie = b->lockcookie; + fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, &b->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"), + svn_fs_fs__id_txn_unparse(&b->txn_id, pool)); + + + /* 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 + = svn_fs_fs__path_txn_proto_rev_lock(fs, &b->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"), + svn_fs_fs__id_txn_unparse(&b->txn_id, + pool)); + + 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; + + return SVN_NO_ERROR; +} + +/* Make sure the length ACTUAL_LENGTH of the proto-revision file PROTO_REV + of transaction TXN_ID in filesystem FS matches the proto-index file. + Trim any crash / failure related extra data from the proto-rev file. + + If the prototype revision file is too short, we can't do much but bail out. + + Perform all allocations in POOL. */ +static svn_error_t * +auto_truncate_proto_rev(svn_fs_t *fs, + apr_file_t *proto_rev, + apr_off_t actual_length, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + /* Only relevant for newer FSFS formats. */ + if (svn_fs_fs__use_log_addressing(fs)) + { + /* Determine file range covered by the proto-index so far. Note that + we always append to both file, i.e. the last index entry also + corresponds to the last addition in the rev file. */ + const char *path = svn_fs_fs__path_p2l_proto_index(fs, txn_id, pool); + apr_file_t *file; + apr_off_t indexed_length; + + SVN_ERR(svn_fs_fs__p2l_proto_index_open(&file, path, pool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_next_offset(&indexed_length, file, + pool)); + SVN_ERR(svn_io_file_close(file, pool)); + + /* Handle mismatches. */ + if (indexed_length < actual_length) + SVN_ERR(svn_io_file_trunc(proto_rev, indexed_length, pool)); + else if (indexed_length > actual_length) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l proto index offset %s beyond proto" + "rev file size %s for TXN %s"), + apr_off_t_toa(pool, indexed_length), + apr_off_t_toa(pool, actual_length), + svn_fs_fs__id_txn_unparse(txn_id, pool)); + } + + return SVN_NO_ERROR; +} + +/* 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 svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + struct get_writable_proto_rev_baton b; + svn_error_t *err; + apr_off_t end_offset = 0; + + b.lockcookie = lockcookie; + b.txn_id = *txn_id; + + SVN_ERR(with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool)); + + /* Now open the prototype revision file and seek to the end. */ + err = svn_io_file_open(file, + svn_fs_fs__path_txn_proto_rev(fs, txn_id, pool), + APR_READ | 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) + err = svn_io_file_seek(*file, APR_END, &end_offset, pool); + + /* We don't want unused sections (such as leftovers from failed delta + stream) in our file. If we use log addressing, we would need an + index entry for the unused section and that section would need to + be all NUL by convention. So, detect and fix those cases by truncating + the protorev file. */ + if (!err) + err = auto_truncate_proto_rev(fs, *file, end_offset, txn_id, pool); + + if (err) + { + err = svn_error_compose_create( + err, + unlock_proto_rev(fs, txn_id, *lockcookie, pool)); + + *lockcookie = NULL; + } + + return svn_error_trace(err); +} + +/* 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 svn_fs_fs__id_part_t *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 svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return with_txnlist_lock(fs, purge_shared_txn_body, txn_id, pool); +} + + +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; + + noderev->is_fresh_txn_root = fresh_txn_root; + + if (! svn_fs_fs__id_is_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, + svn_fs_fs__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 SCATCH_POOL for temporary allocations. + */ +static svn_error_t * +store_sha1_rep_mapping(svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *scratch_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->has_sha1) + { + apr_file_t *rep_file; + const char *file_name = path_txn_sha1(fs, + &noderev->data_rep->txn_id, + noderev->data_rep->sha1_digest, + scratch_pool); + svn_stringbuf_t *rep_string + = svn_fs_fs__unparse_representation(noderev->data_rep, + ffd->format, + (noderev->kind == svn_node_dir), + scratch_pool, scratch_pool); + SVN_ERR(svn_io_file_open(&rep_file, file_name, + APR_WRITE | APR_CREATE | APR_TRUNCATE + | APR_BUFFERED, APR_OS_DEFAULT, scratch_pool)); + + SVN_ERR(svn_io_file_write_full(rep_file, rep_string->data, + rep_string->len, NULL, scratch_pool)); + + SVN_ERR(svn_io_file_close(rep_file, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +unparse_dir_entry(svn_fs_dirent_t *dirent, + svn_stream_t *stream, + apr_pool_t *pool) +{ + const char *val + = apr_psprintf(pool, "%s %s", + (dirent->kind == svn_node_file) ? SVN_FS_FS__KIND_FILE + : SVN_FS_FS__KIND_DIR, + svn_fs_fs__id_unparse(dirent->id, pool)->data); + + SVN_ERR(svn_stream_printf(stream, pool, "K %" APR_SIZE_T_FMT "\n%s\n" + "V %" APR_SIZE_T_FMT "\n%s\n", + strlen(dirent->name), dirent->name, + strlen(val), val)); + return SVN_NO_ERROR; +} + +/* Write the directory given as array of dirent structs in ENTRIES to STREAM. + Perform temporary allocations in POOL. */ +static svn_error_t * +unparse_dir_entries(apr_array_header_t *entries, + svn_stream_t *stream, + apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); + int i; + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_dirent_t *dirent; + + svn_pool_clear(iterpool); + dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); + SVN_ERR(unparse_dir_entry(dirent, stream, iterpool)); + } + + SVN_ERR(svn_stream_printf(stream, pool, "%s\n", SVN_HASH_TERMINATOR)); + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Return a deep copy of SOURCE and allocate it in RESULT_POOL. + */ +static svn_fs_path_change2_t * +path_change_dup(const svn_fs_path_change2_t *source, + apr_pool_t *result_pool) +{ + svn_fs_path_change2_t *result = apr_pmemdup(result_pool, source, + sizeof(*source)); + result->node_rev_id = svn_fs_fs__id_copy(source->node_rev_id, result_pool); + if (source->copyfrom_path) + result->copyfrom_path = apr_pstrdup(result_pool, source->copyfrom_path); + + return result; +} + +/* Merge the internal-use-only CHANGE into a hash of public-FS + svn_fs_path_change2_t CHANGED_PATHS, collapsing multiple changes into a + single summarical (is that real word?) change per path. DELETIONS is + also a path->svn_fs_path_change2_t hash and contains all the deletions + that got turned into a replacement. */ +static svn_error_t * +fold_change(apr_hash_t *changed_paths, + apr_hash_t *deletions, + const change_t *change) +{ + apr_pool_t *pool = apr_hash_pool_get(changed_paths); + svn_fs_path_change2_t *old_change, *new_change; + const svn_string_t *path = &change->path; + const svn_fs_path_change2_t *info = &change->info; + + if ((old_change = apr_hash_get(changed_paths, path->data, 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 ((! info->node_rev_id) + && (info->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 (info->node_rev_id + && (! svn_fs_fs__id_eq(old_change->node_rev_id, info->node_rev_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) + && (! ((info->change_kind == svn_fs_path_change_replace) + || (info->change_kind == svn_fs_path_change_reset) + || (info->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 ((info->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 (info->change_kind) + { + case svn_fs_path_change_reset: + /* A reset here will simply remove the path change from the + hash. */ + apr_hash_set(changed_paths, path->data, path->len, 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. (The caller will delete any child paths.) */ + apr_hash_set(changed_paths, path->data, path->len, NULL); + } + else if (old_change->change_kind == svn_fs_path_change_replace) + { + /* A deleting a 'replace' restore the original deletion. */ + new_change = apr_hash_get(deletions, path->data, path->len); + SVN_ERR_ASSERT(new_change); + apr_hash_set(changed_paths, path->data, path->len, new_change); + } + else + { + /* A deletion overrules a previous change (modify). */ + new_change = path_change_dup(info, pool); + apr_hash_set(changed_paths, path->data, path->len, new_change); + } + 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. Remember the original + deletion such that we are able to delete this path again + (the replacement may have changed node kind and id). */ + new_change = path_change_dup(info, pool); + new_change->change_kind = svn_fs_path_change_replace; + + apr_hash_set(changed_paths, path->data, path->len, new_change); + + /* Remember the original change. + * Make sure to allocate the hash key in a durable pool. */ + apr_hash_set(deletions, + apr_pstrmemdup(apr_hash_pool_get(deletions), + path->data, path->len), + path->len, old_change); + break; + + case svn_fs_path_change_modify: + default: + /* If the new change modifies some attribute of the node, set + the corresponding flag, whether it already was set or not. + Note: We do not reset a flag to FALSE if a change is undone. */ + if (info->text_mod) + old_change->text_mod = TRUE; + if (info->prop_mod) + old_change->prop_mod = TRUE; + if (info->mergeinfo_mod == svn_tristate_true) + old_change->mergeinfo_mod = svn_tristate_true; + break; + } + } + else + { + /* Add this path. The API makes no guarantees that this (new) key + will not be retained. Thus, we copy the key into the target pool + to ensure a proper lifetime. */ + apr_hash_set(changed_paths, + apr_pstrmemdup(pool, path->data, path->len), path->len, + path_change_dup(info, pool)); + } + + return SVN_NO_ERROR; +} + +/* Baton type to be used with process_changes(). */ +typedef struct process_changes_baton_t +{ + /* Folded list of path changes. */ + apr_hash_t *changed_paths; + + /* Path changes that are deletions and have been turned into + replacements. If those replacements get deleted again, this + container contains the record that we have to revert to. */ + apr_hash_t *deletions; +} process_changes_baton_t; + +/* An implementation of svn_fs_fs__change_receiver_t. + Examine all the changed path entries in CHANGES and store them in + *CHANGED_PATHS. Folding is done to remove redundant or unnecessary + data. Do all allocations in POOL. */ +static svn_error_t * +process_changes(void *baton_p, + change_t *change, + apr_pool_t *scratch_pool) +{ + process_changes_baton_t *baton = baton_p; + + SVN_ERR(fold_change(baton->changed_paths, baton->deletions, change)); + + /* 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->info.change_kind == svn_fs_path_change_delete) + || (change->info.change_kind == svn_fs_path_change_replace)) + { + 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 path_len = change->path.len; + apr_ssize_t min_child_len = path_len == 0 + ? 1 + : change->path.data[path_len-1] == '/' + ? path_len + 1 + : 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(scratch_pool, baton->changed_paths); + hi; + hi = apr_hash_next(hi)) + { + /* KEY is the path. */ + const void *path; + apr_ssize_t klen; + svn_fs_path_change2_t *old_change; + apr_hash_this(hi, &path, &klen, (void**)&old_change); + + /* If we come across a child of our path, remove it. + Call svn_fspath__skip_ancestor only if there is a chance that + this is actually a sub-path. + */ + if (klen >= min_child_len) + { + const char *child; + + child = svn_fspath__skip_ancestor(change->path.data, path); + if (child && child[0] != '\0') + { + apr_hash_set(baton->changed_paths, path, klen, NULL); + } + } + } + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__txn_changes_fetch(apr_hash_t **changed_paths_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + apr_file_t *file; + apr_hash_t *changed_paths = apr_hash_make(pool); + apr_pool_t *scratch_pool = svn_pool_create(pool); + process_changes_baton_t baton; + + baton.changed_paths = changed_paths; + baton.deletions = apr_hash_make(scratch_pool); + + SVN_ERR(svn_io_file_open(&file, + path_txn_changes(fs, txn_id, scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + SVN_ERR(svn_fs_fs__read_changes_incrementally( + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + process_changes, &baton, + scratch_pool)); + svn_pool_destroy(scratch_pool); + + *changed_paths_p = changed_paths; + + 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_pool_t *pool) +{ + apr_hash_t *changed_paths; + apr_array_header_t *changes; + int i; + + SVN_ERR(svn_fs_fs__get_changes(&changes, fs, rev, pool)); + + changed_paths = svn_hash__make(pool); + for (i = 0; i < changes->nelts; ++i) + { + change_t *change = APR_ARRAY_IDX(changes, i, change_t *); + apr_hash_set(changed_paths, change->path.data, change->path.len, + &change->info); + } + + *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 svn_fs_fs__id_part_t *txn_id, + svn_fs_id_t *src, + apr_pool_t *pool) +{ + node_revision_t *noderev; + const svn_fs_fs__id_part_t *node_id, *copy_id; + + SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, src, pool, pool)); + + if (svn_fs_fs__id_is_txn(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; + apr_uint64_t txn_number; + 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 + = svn_fs_fs__path_txn_current(cb->fs, pool); + char new_id_str[SVN_INT64_BUFFER_SIZE + 1]; /* add space for a newline */ + apr_size_t line_length; + + svn_stringbuf_t *buf; + SVN_ERR(svn_fs_fs__read_content(&buf, txn_current_filename, cb->pool)); + + /* assign the current txn counter value to our result */ + cb->txn_number = svn__base36toui64(NULL, buf->data); + + /* remove trailing newlines */ + line_length = svn__ui64tobase36(new_id_str, cb->txn_number+1); + new_id_str[line_length] = '\n'; + + /* Increment the key and add a trailing \n to the string so the + txn-current file has a newline in it. */ + SVN_ERR(svn_io_write_atomic(txn_current_filename, new_id_str, + line_length + 1, + txn_current_filename /* copy_perms path */, + 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 and *TXN_ID. 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_fs__id_part_t *txn_id, + 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(svn_fs_fs__with_txn_current_lock(fs, + get_and_increment_txn_key_body, + &cb, + pool)); + txn_id->revision = rev; + txn_id->number = cb.txn_number; + + *id_p = svn_fs_fs__id_txn_unparse(txn_id, pool); + txn_dir = svn_fs_fs__path_txn_dir(fs, txn_id, pool); + + 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 and *TXN_ID. 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_fs__id_part_t *txn_id, + 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 "/-.txn". */ + prefix = svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), + apr_psprintf(pool, "%ld", rev), pool); + + 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_ERR(svn_fs_fs__id_txn_parse(txn_id, *id_p)); + 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; + fs_txn_data_t *ftd; + svn_fs_id_t *root_id; + + txn = apr_pcalloc(pool, sizeof(*txn)); + ftd = apr_pcalloc(pool, sizeof(*ftd)); + + /* Get the txn_id. */ + if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) + SVN_ERR(create_txn_dir(&txn->id, &ftd->txn_id, fs, rev, pool)); + else + SVN_ERR(create_txn_dir_pre_1_5(&txn->id, &ftd->txn_id, fs, rev, pool)); + + txn->fs = fs; + txn->base_rev = rev; + + txn->vtable = &txn_vtable; + txn->fsap_data = ftd; + *txn_p = txn; + + /* Create a new root node for this transaction. */ + SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool, pool)); + SVN_ERR(create_new_txn_noderev_from_rev(fs, &ftd->txn_id, root_id, pool)); + + /* Create an empty rev file. */ + SVN_ERR(svn_io_file_create_empty( + svn_fs_fs__path_txn_proto_rev(fs, &ftd->txn_id, pool), + pool)); + + /* Create an empty rev-lock file. */ + SVN_ERR(svn_io_file_create_empty( + svn_fs_fs__path_txn_proto_rev_lock(fs, &ftd->txn_id, pool), + pool)); + + /* Create an empty changes file. */ + SVN_ERR(svn_io_file_create_empty(path_txn_changes(fs, &ftd->txn_id, pool), + pool)); + + /* Create the next-ids file. */ + return svn_io_file_create(path_txn_next_ids(fs, &ftd->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 svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + svn_stream_t *stream; + svn_error_t *err; + + /* Check for issue #3696. (When we find and fix the cause, we can change + * this to an assertion.) */ + if (!txn_id || !svn_fs_fs__id_txn_used(txn_id)) + 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. */ + err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_error_clear(svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed property list in transaction '%s'"), + path_txn_props(fs, txn_id, pool)); + } + + return svn_stream_close(stream); +} + +/* Save the property list PROPS as the revprops for transaction TXN_ID + in FS. Perform temporary allocations in POOL. */ +static svn_error_t * +set_txn_proplist(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_hash_t *props, + svn_boolean_t final, + apr_pool_t *pool) +{ + svn_stringbuf_t *buf; + svn_stream_t *stream; + + /* Write out the new file contents to BUF. */ + buf = svn_stringbuf_create_ensure(1024, pool); + stream = svn_stream_from_stringbuf(buf, pool); + SVN_ERR(svn_hash_write2(props, stream, SVN_HASH_TERMINATOR, pool)); + SVN_ERR(svn_stream_close(stream)); + + /* Open the transaction properties file and write new contents to it. */ + SVN_ERR(svn_io_write_atomic((final + ? path_txn_props_final(fs, txn_id, pool) + : path_txn_props(fs, txn_id, pool)), + buf->data, buf->len, + NULL /* copy_perms_path */, pool)); + return SVN_NO_ERROR; +} + + +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) +{ + fs_txn_data_t *ftd = txn->fsap_data; + apr_hash_t *txn_prop = apr_hash_make(pool); + int i; + svn_error_t *err; + + err = get_txn_proplist(txn_prop, txn->fs, &ftd->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); + + if (svn_hash_gets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE) + && !strcmp(prop->name, SVN_PROP_REVISION_DATE)) + svn_hash_sets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE, + svn_string_create("1", pool)); + + 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. */ + SVN_ERR(set_txn_proplist(txn->fs, &ftd->txn_id, txn_prop, FALSE, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__get_txn(transaction_t **txn_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *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)); + root_id = svn_fs_fs__id_txn_create_root(txn_id, pool); + + SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, root_id, pool, 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 svn_fs_fs__id_part_t *txn_id, + apr_uint64_t node_id, + apr_uint64_t copy_id, + apr_pool_t *pool) +{ + apr_file_t *file; + char buffer[2 * SVN_INT64_BUFFER_SIZE + 2]; + char *p = buffer; + + p += svn__ui64tobase36(p, node_id); + *(p++) = ' '; + p += svn__ui64tobase36(p, copy_id); + *(p++) = '\n'; + *(p++) = '\0'; + + SVN_ERR(svn_io_file_open(&file, + path_txn_next_ids(fs, txn_id, pool), + APR_WRITE | APR_TRUNCATE, + APR_OS_DEFAULT, pool)); + SVN_ERR(svn_io_file_write_full(file, buffer, p - buffer, NULL, pool)); + 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(apr_uint64_t *node_id, + apr_uint64_t *copy_id, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + svn_stringbuf_t *buf; + const char *str; + SVN_ERR(svn_fs_fs__read_content(&buf, + path_txn_next_ids(fs, txn_id, pool), + pool)); + + /* Parse this into two separate strings. */ + + str = buf->data; + *node_id = svn__base36toui64(&str, str); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("next-id file corrupt")); + + ++str; + *copy_id = svn__base36toui64(&str, str); + if (*str != '\n') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("next-id file corrupt")); + + 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(svn_fs_fs__id_part_t *node_id_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + apr_uint64_t node_id, copy_id; + + /* First read in the current next-ids file. */ + SVN_ERR(read_next_ids(&node_id, ©_id, fs, txn_id, pool)); + + node_id_p->revision = SVN_INVALID_REVNUM; + node_id_p->number = node_id; + + SVN_ERR(write_next_ids(fs, txn_id, ++node_id, copy_id, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__reserve_copy_id(svn_fs_fs__id_part_t *copy_id_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + apr_uint64_t node_id, copy_id; + + /* First read in the current next-ids file. */ + SVN_ERR(read_next_ids(&node_id, ©_id, fs, txn_id, pool)); + + /* this is an in-txn ID now */ + copy_id_p->revision = SVN_INVALID_REVNUM; + copy_id_p->number = copy_id; + + /* Update the ID counter file */ + SVN_ERR(write_next_ids(fs, txn_id, node_id, ++copy_id, pool)); + + 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 svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + svn_fs_fs__id_part_t 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_str, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_fs_fs__id_part_t txn_id; + SVN_ERR(svn_fs_fs__id_txn_parse(&txn_id, txn_id_str)); + + /* 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(svn_fs_fs__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( + svn_fs_fs__path_txn_proto_rev(fs, &txn_id, pool), + TRUE, pool)); + SVN_ERR(svn_io_remove_file2( + svn_fs_fs__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; +} + +/* Assign the UNIQUIFIER member of REP based on the current state of TXN_ID + * in FS. Allocate the uniquifier in POOL. + */ +static svn_error_t * +set_uniquifier(svn_fs_t *fs, + representation_t *rep, + apr_pool_t *pool) +{ + svn_fs_fs__id_part_t temp; + fs_fs_data_t *ffd = fs->fsap_data; + + if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT) + { + SVN_ERR(get_new_txn_node_id(&temp, fs, &rep->txn_id, pool)); + rep->uniquifier.noderev_txn_id = rep->txn_id; + rep->uniquifier.number = temp.number; + } + + return SVN_NO_ERROR; +} + +/* Return TRUE if the TXN_ID member of REP is in use. + */ +static svn_boolean_t +is_txn_rep(const representation_t *rep) +{ + return svn_fs_fs__id_txn_used(&rep->txn_id); +} + +/* Mark the TXN_ID member of REP as "unused". + */ +static void +reset_txn_in_rep(representation_t *rep) +{ + svn_fs_fs__id_txn_reset(&rep->txn_id); +} + +svn_error_t * +svn_fs_fs__set_entry(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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 + = svn_fs_fs__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 || !is_txn_rep(rep)) + { + apr_array_header_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, 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(unparse_dir_entries(entries, out, 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(set_uniquifier(fs, rep, pool)); + 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) + { + svn_fs_dirent_t entry; + entry.name = name; + entry.id = id; + entry.kind = kind; + + SVN_ERR(unparse_dir_entry(&entry, out, subpool)); + } + 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; +} + +svn_error_t * +svn_fs_fs__add_change(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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_boolean_t mergeinfo_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; + apr_hash_t *changes = apr_hash_make(pool); + + /* Not using APR_BUFFERED to append change in one atomic write operation. */ + SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool), + APR_APPEND | APR_WRITE | APR_CREATE, + 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->mergeinfo_mod = mergeinfo_mod + ? svn_tristate_true + : svn_tristate_false; + change->node_kind = node_kind; + change->copyfrom_known = TRUE; + change->copyfrom_rev = copyfrom_rev; + if (copyfrom_path) + change->copyfrom_path = apr_pstrdup(pool, copyfrom_path); + + svn_hash_sets(changes, path, change); + SVN_ERR(svn_fs_fs__write_changes(svn_stream_from_aprfile2(file, TRUE, pool), + fs, changes, FALSE, pool)); + + return svn_io_file_close(file, pool); +} + +/* If the transaction TXN_ID in FS uses logical addressing, store the + * (ITEM_INDEX, OFFSET) pair in the txn's log-to-phys proto index file. + * Use POOL for allocations. + */ +static svn_error_t * +store_l2p_index_entry(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_off_t offset, + apr_uint64_t item_index, + apr_pool_t *pool) +{ + if (svn_fs_fs__use_log_addressing(fs)) + { + const char *path = svn_fs_fs__path_l2p_proto_index(fs, txn_id, pool); + apr_file_t *file; + SVN_ERR(svn_fs_fs__l2p_proto_index_open(&file, path, pool)); + SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry(file, offset, + item_index, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } + + return SVN_NO_ERROR; +} + +/* If the transaction TXN_ID in FS uses logical addressing, store ENTRY + * in the phys-to-log proto index file of transaction TXN_ID. + * Use POOL for allocations. + */ +static svn_error_t * +store_p2l_index_entry(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + if (svn_fs_fs__use_log_addressing(fs)) + { + const char *path = svn_fs_fs__path_p2l_proto_index(fs, txn_id, pool); + apr_file_t *file; + SVN_ERR(svn_fs_fs__p2l_proto_index_open(&file, path, pool)); + SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(file, entry, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } + + return SVN_NO_ERROR; +} + +/* Allocate an item index for the given MY_OFFSET in the transaction TXN_ID + * of file system FS and return it in *ITEM_INDEX. For old formats, it + * will simply return the offset as item index; in new formats, it will + * increment the txn's item index counter file and store the mapping in + * the proto index file. Use POOL for allocations. + */ +static svn_error_t * +allocate_item_index(apr_uint64_t *item_index, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_off_t my_offset, + apr_pool_t *pool) +{ + if (svn_fs_fs__use_log_addressing(fs)) + { + apr_file_t *file; + char buffer[SVN_INT64_BUFFER_SIZE] = { 0 }; + svn_boolean_t eof = FALSE; + apr_size_t to_write; + apr_size_t read; + apr_off_t offset = 0; + + /* read number, increment it and write it back to disk */ + SVN_ERR(svn_io_file_open(&file, + svn_fs_fs__path_txn_item_index(fs, txn_id, pool), + APR_READ | APR_WRITE | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, pool)); + SVN_ERR(svn_io_file_read_full2(file, buffer, sizeof(buffer)-1, + &read, &eof, pool)); + if (read) + SVN_ERR(svn_cstring_atoui64(item_index, buffer)); + else + *item_index = SVN_FS_FS__ITEM_INDEX_FIRST_USER; + + to_write = svn__ui64toa(buffer, *item_index + 1); + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); + SVN_ERR(svn_io_file_write_full(file, buffer, to_write, NULL, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + + /* write log-to-phys index */ + SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, *item_index, pool)); + } + else + { + *item_index = (apr_uint64_t)my_offset; + } + + return SVN_NO_ERROR; +} + +/* Baton used by fnv1a_write_handler to calculate the FNV checksum + * before passing the data on to the INNER_STREAM. + */ +typedef struct fnv1a_stream_baton_t +{ + svn_stream_t *inner_stream; + svn_checksum_ctx_t *context; +} fnv1a_stream_baton_t; + +/* Implement svn_write_fn_t. + * Update checksum and pass data on to inner stream. + */ +static svn_error_t * +fnv1a_write_handler(void *baton, + const char *data, + apr_size_t *len) +{ + fnv1a_stream_baton_t *b = baton; + + SVN_ERR(svn_checksum_update(b->context, data, *len)); + SVN_ERR(svn_stream_write(b->inner_stream, data, len)); + + return SVN_NO_ERROR; +} + +/* Return a stream that calculates a FNV checksum in *CONTEXT + * over all data written to the stream and passes that data on + * to INNER_STREAM. Allocate objects in POOL. + */ +static svn_stream_t * +fnv1a_wrap_stream(svn_checksum_ctx_t **context, + svn_stream_t *inner_stream, + apr_pool_t *pool) +{ + svn_stream_t *outer_stream; + + fnv1a_stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton)); + baton->inner_stream = inner_stream; + baton->context = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, pool); + *context = baton->context; + + outer_stream = svn_stream_create(baton, pool); + svn_stream_set_write(outer_stream, fnv1a_write_handler); + + return outer_stream; +} + +/* Set *DIGEST to the FNV checksum calculated in CONTEXT. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +fnv1a_checksum_finalize(apr_uint32_t *digest, + svn_checksum_ctx_t *context, + apr_pool_t *scratch_pool) +{ + svn_checksum_t *checksum; + + SVN_ERR(svn_checksum_final(&checksum, context, scratch_pool)); + SVN_ERR_ASSERT(checksum->kind == svn_checksum_fnv1a_32x4); + *digest = ntohl(*(const apr_uint32_t *)(checksum->digest)); + + return SVN_NO_ERROR; +} + +/* 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; + + /* calculate a modified FNV-1a checksum of the on-disk representation */ + svn_checksum_ctx_t *fnv1a_checksum_ctx; + + /* Local / scratch pool, available for temporary allocations. */ + apr_pool_t *scratch_pool; + + /* Outer / result pool. */ + apr_pool_t *result_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); +} + +/* Set *SPANNED to the number of shards touched when walking WALK steps on + * NODEREV's predecessor chain in FS. Use POOL for temporary allocations. + */ +static svn_error_t * +shards_spanned(int *spanned, + svn_fs_t *fs, + node_revision_t *noderev, + int walk, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + int shard_size = ffd->max_files_per_dir ? ffd->max_files_per_dir : 1; + apr_pool_t *iterpool; + + int count = walk ? 1 : 0; /* The start of a walk already touches a shard. */ + svn_revnum_t shard, last_shard = ffd->youngest_rev_cache / shard_size; + iterpool = svn_pool_create(pool); + while (walk-- && noderev->predecessor_count) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, + noderev->predecessor_id, pool, + iterpool)); + shard = svn_fs_fs__id_rev(noderev->id) / shard_size; + if (shard != last_shard) + { + ++count; + last_shard = shard; + } + } + svn_pool_destroy(iterpool); + + *spanned = count; + return SVN_NO_ERROR; +} + +/* 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) +{ + /* The zero-based index (counting from the "oldest" end), along NODEREVs line + * predecessors, of the node-rev we will use as delta base. */ + int count; + /* The length of the linear part of a delta chain. (Delta chains use + * skip-delta bits for the high-order bits and are linear in the low-order + * bits.) */ + int walk; + node_revision_t *base; + fs_fs_data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool; + + /* If we have no predecessors, or that one is empty, 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); + + /* 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. */ + walk = noderev->predecessor_count - count; + if (walk > (int)ffd->max_deltification_walk) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* 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. */ + if (walk < (int)ffd->max_linear_deltification) + { + int shards; + SVN_ERR(shards_spanned(&shards, fs, noderev, walk, pool)); + + /* We also don't want the linear deltification to span more shards + than if deltas we used in a simple skip-delta scheme. */ + if ((1 << (--shards)) <= walk) + count = noderev->predecessor_count - 1; + } + + /* 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; + iterpool = svn_pool_create(pool); + while ((count++) < noderev->predecessor_count) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_fs__get_node_revision(&base, fs, + base->predecessor_id, pool, + iterpool)); + } + svn_pool_destroy(iterpool); + + /* return a suitable base representation */ + *rep = props ? base->prop_rep : base->data_rep; + + /* if we encountered a shared rep, its parent chain may be different + * from the node-rev parent chain. */ + if (*rep) + { + int chain_length = 0; + int shard_count = 0; + + /* Very short rep bases are simply not worth it as we are unlikely + * to re-coup the deltification space overhead of 20+ bytes. */ + svn_filesize_t rep_size = (*rep)->expanded_size + ? (*rep)->expanded_size + : (*rep)->size; + if (rep_size < 64) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* Check whether the length of the deltification chain is acceptable. + * Otherwise, shared reps may form a non-skipping delta chain in + * extreme cases. */ + SVN_ERR(svn_fs_fs__rep_chain_length(&chain_length, &shard_count, + *rep, fs, pool)); + + /* Some reasonable limit, depending on how acceptable longer linear + * chains are in this repo. Also, allow for some minimal chain. */ + if (chain_length >= 2 * (int)ffd->max_linear_deltification + 2) + *rep = NULL; + else + /* To make it worth opening additional shards / pack files, we + * require that the reps have a certain minimal size. To deltify + * against a rep in different shard, the lower limit is 512 bytes + * and doubles with every extra shard to visit along the delta + * chain. */ + if ( shard_count > 1 + && ((svn_filesize_t)128 << shard_count) >= rep_size) + *rep = NULL; + } + + 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; + svn_error_t *err; + + /* Truncate and close the protorevfile. */ + err = svn_io_file_trunc(b->file, b->rep_offset, b->scratch_pool); + err = svn_error_compose_create(err, svn_io_file_close(b->file, + b->scratch_pool)); + + /* Remove our lock regardless of any preceding 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, + svn_fs_fs__id_txn_id(b->noderev->id), + b->lockcookie, b->scratch_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; + 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; + svn_fs_fs__rep_header_t header = { 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->result_pool = pool; + b->scratch_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->scratch_pool)); + + b->file = file; + b->rep_stream = fnv1a_wrap_stream(&b->fnv1a_checksum_ctx, + svn_stream_from_aprfile2(file, TRUE, + b->scratch_pool), + b->scratch_pool); + + SVN_ERR(svn_fs_fs__get_file_offset(&b->rep_offset, file, b->scratch_pool)); + + /* Get the base for this delta. */ + SVN_ERR(choose_delta_base(&base_rep, fs, noderev, FALSE, b->scratch_pool)); + SVN_ERR(svn_fs_fs__get_contents(&source, fs, base_rep, TRUE, + b->scratch_pool)); + + /* Write out the rep header. */ + if (base_rep) + { + header.base_revision = base_rep->revision; + header.base_item_index = base_rep->item_index; + header.base_length = base_rep->size; + header.type = svn_fs_fs__rep_delta; + } + else + { + header.type = svn_fs_fs__rep_self_delta; + } + SVN_ERR(svn_fs_fs__write_rep_header(&header, b->rep_stream, + b->scratch_pool)); + + /* Now determine the offset of the actual svndiff data. */ + SVN_ERR(svn_fs_fs__get_file_offset(&b->delta_start, file, + b->scratch_pool)); + + /* Cleanup in case something goes wrong. */ + apr_pool_cleanup_register(b->scratch_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, + ffd->delta_compression_level, + pool); + + b->delta_stream = svn_txdelta_target_push(wh, whb, source, + b->scratch_pool); + + *wb_p = b; + + return SVN_NO_ERROR; +} + +/* For REP->SHA1_CHECKSUM, 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. + Use RESULT_POOL for *OLD_REP allocations and SCRATCH_POOL for temporaries. + The lifetime of *OLD_REP is limited by both, RESULT_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 *result_pool, + apr_pool_t *scratch_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_digest, + APR_SHA1_DIGESTSIZE); + + /* If we haven't found anything yet, try harder and consult our DB. */ + if (*old_rep == NULL) + { + svn_checksum_t checksum; + checksum.digest = rep->sha1_digest; + checksum.kind = svn_checksum_sha1; + err = svn_fs_fs__get_rep_reference(old_rep, fs, &checksum, result_pool); + /* ### Other error codes that we shouldn't mask out? */ + if (err == SVN_NO_ERROR) + { + if (*old_rep) + SVN_ERR(svn_fs_fs__check_rep(*old_rep, fs, NULL, scratch_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 && is_txn_rep(rep)) + { + svn_node_kind_t kind; + const char *file_name + = path_txn_sha1(fs, &rep->txn_id, rep->sha1_digest, scratch_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, scratch_pool)); + if (kind == svn_node_file) + { + svn_stringbuf_t *rep_string; + SVN_ERR(svn_stringbuf_from_file2(&rep_string, file_name, + scratch_pool)); + SVN_ERR(svn_fs_fs__parse_representation(old_rep, rep_string, + result_pool, scratch_pool)); + } + } + + if (!*old_rep) + return SVN_NO_ERROR; + + /* We don't want 0-length PLAIN representations to replace non-0-length + ones (see issue #4554). Take into account that EXPANDED_SIZE may be + 0 in which case we have to check the on-disk SIZE. Also, this doubles + as a simple guard against general rep-cache induced corruption. */ + if ( ((*old_rep)->expanded_size != rep->expanded_size) + || ((rep->expanded_size == 0) && ((*old_rep)->size != rep->size))) + { + *old_rep = NULL; + } + else + { + /* Add information that is missing in the cached data. + Use the old rep for this content. */ + memcpy((*old_rep)->md5_digest, rep->md5_digest, sizeof(rep->md5_digest)); + (*old_rep)->uniquifier = rep->uniquifier; + } + + return SVN_NO_ERROR; +} + +/* Copy the hash sum calculation results from MD5_CTX, SHA1_CTX into REP. + * Use POOL for allocations. + */ +static svn_error_t * +digests_final(representation_t *rep, + const svn_checksum_ctx_t *md5_ctx, + const svn_checksum_ctx_t *sha1_ctx, + apr_pool_t *pool) +{ + svn_checksum_t *checksum; + + SVN_ERR(svn_checksum_final(&checksum, md5_ctx, pool)); + memcpy(rep->md5_digest, checksum->digest, svn_checksum_size(checksum)); + SVN_ERR(svn_checksum_final(&checksum, sha1_ctx, pool)); + rep->has_sha1 = checksum != NULL; + if (rep->has_sha1) + memcpy(rep->sha1_digest, checksum->digest, svn_checksum_size(checksum)); + + 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; + representation_t *rep; + representation_t *old_rep; + apr_off_t offset; + + rep = apr_pcalloc(b->result_pool, sizeof(*rep)); + + /* 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(svn_fs_fs__get_file_offset(&offset, b->file, b->scratch_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(set_uniquifier(b->fs, rep, b->scratch_pool)); + rep->revision = SVN_INVALID_REVNUM; + + /* Finalize the checksum. */ + SVN_ERR(digests_final(rep, b->md5_checksum_ctx, b->sha1_checksum_ctx, + b->result_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->result_pool, + b->scratch_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->scratch_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")); + SVN_ERR(allocate_item_index(&rep->item_index, b->fs, &rep->txn_id, + b->rep_offset, b->scratch_pool)); + + b->noderev->data_rep = rep; + } + + /* Remove cleanup callback. */ + apr_pool_cleanup_kill(b->scratch_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->scratch_pool)); + if (!old_rep) + { + svn_fs_fs__p2l_entry_t entry; + + entry.offset = b->rep_offset; + SVN_ERR(svn_fs_fs__get_file_offset(&offset, b->file, b->scratch_pool)); + entry.size = offset - b->rep_offset; + entry.type = SVN_FS_FS__ITEM_TYPE_FILE_REP; + entry.item.revision = SVN_INVALID_REVNUM; + entry.item.number = rep->item_index; + SVN_ERR(fnv1a_checksum_finalize(&entry.fnv1_checksum, + b->fnv1a_checksum_ctx, + b->scratch_pool)); + + SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->scratch_pool)); + SVN_ERR(store_p2l_index_entry(b->fs, &rep->txn_id, &entry, + b->scratch_pool)); + } + + SVN_ERR(svn_io_file_close(b->file, b->scratch_pool)); + SVN_ERR(unlock_proto_rev(b->fs, &rep->txn_id, b->lockcookie, + b->scratch_pool)); + svn_pool_destroy(b->scratch_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_is_txn(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 svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *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 + = svn_fs_fs__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 || !is_txn_rep(noderev->prop_rep)) + { + 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; +} + +/* This baton is used by the stream created for write_container_rep. */ +struct write_container_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_container_rep stream. BATON is a + write_container_baton, DATA has the data to write and *LEN is the number + of bytes to write. */ +static svn_error_t * +write_container_handler(void *baton, + const char *data, + apr_size_t *len) +{ + struct write_container_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; +} + +/* Callback function type. Write the data provided by BATON into STREAM. */ +typedef svn_error_t * +(* collection_writer_t)(svn_stream_t *stream, void *baton, apr_pool_t *pool); + +/* Implement collection_writer_t writing the C string->svn_string_t hash + given as BATON. */ +static svn_error_t * +write_hash_to_stream(svn_stream_t *stream, + void *baton, + apr_pool_t *pool) +{ + apr_hash_t *hash = baton; + SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool)); + + return SVN_NO_ERROR; +} + +/* Implement collection_writer_t writing the svn_fs_dirent_t* array given + as BATON. */ +static svn_error_t * +write_directory_to_stream(svn_stream_t *stream, + void *baton, + apr_pool_t *pool) +{ + apr_array_header_t *dir = baton; + SVN_ERR(unparse_dir_entries(dir, stream, pool)); + + return SVN_NO_ERROR; +} + +/* Write out the COLLECTION as a text representation to file FILE using + WRITER. In the process, record position, the total size of the dump and + MD5 as well as SHA1 in REP. Add the representation of type ITEM_TYPE to + the indexes if necessary. 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 SCRATCH_POOL. */ +static svn_error_t * +write_container_rep(representation_t *rep, + apr_file_t *file, + void *collection, + collection_writer_t writer, + svn_fs_t *fs, + apr_hash_t *reps_hash, + apr_uint32_t item_type, + apr_pool_t *scratch_pool) +{ + svn_stream_t *stream; + struct write_container_baton *whb; + svn_checksum_ctx_t *fnv1a_checksum_ctx; + representation_t *old_rep; + apr_off_t offset = 0; + + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); + + whb = apr_pcalloc(scratch_pool, sizeof(*whb)); + + whb->stream = fnv1a_wrap_stream(&fnv1a_checksum_ctx, + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + scratch_pool); + whb->size = 0; + whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool); + + stream = svn_stream_create(whb, scratch_pool); + svn_stream_set_write(stream, write_container_handler); + + SVN_ERR(svn_stream_puts(whb->stream, "PLAIN\n")); + + SVN_ERR(writer(stream, collection, scratch_pool)); + + /* Store the results. */ + SVN_ERR(digests_final(rep, whb->md5_ctx, whb->sha1_ctx, scratch_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, scratch_pool, + scratch_pool)); + + if (old_rep) + { + /* We need to erase from the protorev the data we just wrote. */ + SVN_ERR(svn_io_file_trunc(file, offset, scratch_pool)); + + /* Use the old rep for this content. */ + memcpy(rep, old_rep, sizeof (*rep)); + } + else + { + svn_fs_fs__p2l_entry_t entry; + + /* Write out our cosmetic end marker. */ + SVN_ERR(svn_stream_puts(whb->stream, "ENDREP\n")); + + SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id, + offset, scratch_pool)); + + entry.offset = offset; + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); + entry.size = offset - entry.offset; + entry.type = item_type; + entry.item.revision = SVN_INVALID_REVNUM; + entry.item.number = rep->item_index; + SVN_ERR(fnv1a_checksum_finalize(&entry.fnv1_checksum, + fnv1a_checksum_ctx, + scratch_pool)); + + SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool)); + + /* update the representation */ + rep->size = whb->size; + rep->expanded_size = whb->size; + } + + return SVN_NO_ERROR; +} + +/* Write out the COLLECTION pertaining to the NODEREV in FS as a deltified + text representation to file FILE using WRITER. In the process, record the + total size and the md5 digest in REP and add the representation of type + ITEM_TYPE to the indexes if necessary. 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 ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume + that we want to a props representation as the base for our delta. + Perform temporary allocations in SCRATCH_POOL. + */ +static svn_error_t * +write_container_delta_rep(representation_t *rep, + apr_file_t *file, + void *collection, + collection_writer_t writer, + svn_fs_t *fs, + node_revision_t *noderev, + apr_hash_t *reps_hash, + apr_uint32_t item_type, + apr_pool_t *scratch_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_checksum_ctx_t *fnv1a_checksum_ctx; + svn_stream_t *source; + svn_fs_fs__rep_header_t header = { 0 }; + + apr_off_t rep_end = 0; + apr_off_t delta_start = 0; + apr_off_t offset = 0; + + struct write_container_baton *whb; + fs_fs_data_t *ffd = fs->fsap_data; + int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0; + svn_boolean_t is_props = (item_type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS) + || (item_type == SVN_FS_FS__ITEM_TYPE_DIR_PROPS); + + /* Get the base for this delta. */ + SVN_ERR(choose_delta_base(&base_rep, fs, noderev, is_props, scratch_pool)); + SVN_ERR(svn_fs_fs__get_contents(&source, fs, base_rep, FALSE, scratch_pool)); + + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); + + /* Write out the rep header. */ + if (base_rep) + { + header.base_revision = base_rep->revision; + header.base_item_index = base_rep->item_index; + header.base_length = base_rep->size; + header.type = svn_fs_fs__rep_delta; + } + else + { + header.type = svn_fs_fs__rep_self_delta; + } + + file_stream = fnv1a_wrap_stream(&fnv1a_checksum_ctx, + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + scratch_pool); + SVN_ERR(svn_fs_fs__write_rep_header(&header, file_stream, scratch_pool)); + SVN_ERR(svn_fs_fs__get_file_offset(&delta_start, file, scratch_pool)); + + /* Prepare to write the svndiff data. */ + svn_txdelta_to_svndiff3(&diff_wh, + &diff_whb, + file_stream, + diff_version, + ffd->delta_compression_level, + scratch_pool); + + whb = apr_pcalloc(scratch_pool, sizeof(*whb)); + whb->stream = svn_txdelta_target_push(diff_wh, diff_whb, source, + scratch_pool); + whb->size = 0; + whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool); + + /* serialize the hash */ + stream = svn_stream_create(whb, scratch_pool); + svn_stream_set_write(stream, write_container_handler); + + SVN_ERR(writer(stream, collection, scratch_pool)); + SVN_ERR(svn_stream_close(whb->stream)); + + /* Store the results. */ + SVN_ERR(digests_final(rep, whb->md5_ctx, whb->sha1_ctx, scratch_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, scratch_pool, + scratch_pool)); + + if (old_rep) + { + /* We need to erase from the protorev the data we just wrote. */ + SVN_ERR(svn_io_file_trunc(file, offset, scratch_pool)); + + /* Use the old rep for this content. */ + memcpy(rep, old_rep, sizeof (*rep)); + } + else + { + svn_fs_fs__p2l_entry_t entry; + + /* Write out our cosmetic end marker. */ + SVN_ERR(svn_fs_fs__get_file_offset(&rep_end, file, scratch_pool)); + SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n")); + + SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id, + offset, scratch_pool)); + + entry.offset = offset; + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); + entry.size = offset - entry.offset; + entry.type = item_type; + entry.item.revision = SVN_INVALID_REVNUM; + entry.item.number = rep->item_index; + SVN_ERR(fnv1a_checksum_finalize(&entry.fnv1_checksum, + fnv1a_checksum_ctx, + scratch_pool)); + + SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool)); + + /* 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, 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; +} + +/* Given the potentially txn-local id PART, update that to a permanent ID + * based on the REVISION currently being written and the START_ID for that + * revision. Use the repo FORMAT to decide which implementation to use. + */ +static void +get_final_id(svn_fs_fs__id_part_t *part, + svn_revnum_t revision, + apr_uint64_t start_id, + int format) +{ + if (part->revision == SVN_INVALID_REVNUM) + { + if (format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + part->revision = revision; + } + else + { + part->revision = 0; + part->number += start_id; + } + } +} + +/* 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, + apr_uint64_t start_node_id, + apr_uint64_t 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; + const svn_fs_id_t *new_id; + svn_fs_fs__id_part_t node_id, copy_id, rev_item; + fs_fs_data_t *ffd = fs->fsap_data; + const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__id_txn_id(id); + svn_stream_t *file_stream; + svn_checksum_ctx_t *fnv1a_checksum_ctx; + apr_pool_t *subpool; + + *new_id_p = NULL; + + /* Check to see if this is a transaction node. */ + if (! svn_fs_fs__id_is_txn(id)) + return SVN_NO_ERROR; + + subpool = svn_pool_create(pool); + SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, id, pool, subpool)); + + if (noderev->kind == svn_node_dir) + { + apr_array_header_t *entries; + int i; + + /* This is a directory. Write out all the children first. */ + + SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool, + subpool)); + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_dirent_t *dirent + = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); + + 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); + } + + if (noderev->data_rep && is_txn_rep(noderev->data_rep)) + { + /* Write out the contents of this directory as a text rep. */ + noderev->data_rep->revision = rev; + if (ffd->deltify_directories) + SVN_ERR(write_container_delta_rep(noderev->data_rep, file, + entries, + write_directory_to_stream, + fs, noderev, NULL, + SVN_FS_FS__ITEM_TYPE_DIR_REP, + pool)); + else + SVN_ERR(write_container_rep(noderev->data_rep, file, entries, + write_directory_to_stream, fs, NULL, + SVN_FS_FS__ITEM_TYPE_DIR_REP, pool)); + + reset_txn_in_rep(noderev->data_rep); + } + } + 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 && is_txn_rep(noderev->data_rep)) + { + reset_txn_in_rep(noderev->data_rep); + noderev->data_rep->revision = rev; + + if (!svn_fs_fs__use_log_addressing(fs)) + { + /* See issue 3845. Some unknown mechanism caused the + protorev file to get truncated, so check for that + here. */ + if (noderev->data_rep->item_index + noderev->data_rep->size + > initial_offset) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Truncated protorev file detected")); + } + } + } + + svn_pool_destroy(subpool); + + /* Fix up the property reps. */ + if (noderev->prop_rep && is_txn_rep(noderev->prop_rep)) + { + apr_hash_t *proplist; + apr_uint32_t item_type = noderev->kind == svn_node_dir + ? SVN_FS_FS__ITEM_TYPE_DIR_PROPS + : SVN_FS_FS__ITEM_TYPE_FILE_PROPS; + SVN_ERR(svn_fs_fs__get_proplist(&proplist, fs, noderev, pool)); + + noderev->prop_rep->revision = rev; + + if (ffd->deltify_properties) + SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist, + write_hash_to_stream, fs, noderev, + reps_hash, item_type, pool)); + else + SVN_ERR(write_container_rep(noderev->prop_rep, file, proplist, + write_hash_to_stream, fs, reps_hash, + item_type, pool)); + + reset_txn_in_rep(noderev->prop_rep); + } + + /* Convert our temporary ID into a permanent revision one. */ + node_id = *svn_fs_fs__id_node_id(noderev->id); + get_final_id(&node_id, rev, start_node_id, ffd->format); + copy_id = *svn_fs_fs__id_copy_id(noderev->id); + get_final_id(©_id, rev, start_copy_id, ffd->format); + + if (noderev->copyroot_rev == SVN_INVALID_REVNUM) + noderev->copyroot_rev = rev; + + /* root nodes have a fixed ID in log addressing mode */ + SVN_ERR(svn_fs_fs__get_file_offset(&my_offset, file, pool)); + if (svn_fs_fs__use_log_addressing(fs) && at_root) + { + /* reference the root noderev from the log-to-phys index */ + rev_item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE; + SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, + rev_item.number, pool)); + } + else + SVN_ERR(allocate_item_index(&rev_item.number, fs, txn_id, + my_offset, pool)); + + rev_item.revision = rev; + new_id = svn_fs_fs__id_rev_create(&node_id, ©_id, &rev_item, 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_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->has_sha1 = FALSE; + + /* don't serialize SHA1 for props to disk (waste of space) */ + if (noderev->prop_rep) + noderev->prop_rep->has_sha1 = FALSE; + + /* 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)); + + file_stream = fnv1a_wrap_stream(&fnv1a_checksum_ctx, + svn_stream_from_aprfile2(file, TRUE, pool), + pool); + SVN_ERR(svn_fs_fs__write_noderev(file_stream, noderev, ffd->format, + svn_fs_fs__fs_supports_mergeinfo(fs), + pool)); + + /* reference the root noderev from the log-to-phys index */ + if (svn_fs_fs__use_log_addressing(fs)) + { + svn_fs_fs__p2l_entry_t entry; + rev_item.revision = SVN_INVALID_REVNUM; + + entry.offset = my_offset; + SVN_ERR(svn_fs_fs__get_file_offset(&my_offset, file, pool)); + entry.size = my_offset - entry.offset; + entry.type = SVN_FS_FS__ITEM_TYPE_NODEREV; + entry.item = rev_item; + SVN_ERR(fnv1a_checksum_finalize(&entry.fnv1_checksum, + fnv1a_checksum_ctx, + pool)); + + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool)); + } + + /* Return our ID that references the revision file. */ + *new_id_p = noderev->id; + + return SVN_NO_ERROR; +} + +/* Write the changed path info CHANGED_PATHS from transaction TXN_ID to the + permanent rev-file FILE in filesystem FS. *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 svn_fs_fs__id_part_t *txn_id, + apr_hash_t *changed_paths, + apr_pool_t *pool) +{ + apr_off_t offset; + svn_stream_t *stream; + svn_checksum_ctx_t *fnv1a_checksum_ctx; + + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, pool)); + + /* write to target file & calculate checksum */ + stream = fnv1a_wrap_stream(&fnv1a_checksum_ctx, + svn_stream_from_aprfile2(file, TRUE, pool), + pool); + SVN_ERR(svn_fs_fs__write_changes(stream, fs, changed_paths, TRUE, pool)); + + *offset_p = offset; + + /* reference changes from the indexes */ + if (svn_fs_fs__use_log_addressing(fs)) + { + svn_fs_fs__p2l_entry_t entry; + + entry.offset = offset; + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, pool)); + entry.size = offset - entry.offset; + entry.type = SVN_FS_FS__ITEM_TYPE_CHANGES; + entry.item.revision = SVN_INVALID_REVNUM; + entry.item.number = SVN_FS_FS__ITEM_INDEX_CHANGES; + SVN_ERR(fnv1a_checksum_finalize(&entry.fnv1_checksum, + fnv1a_checksum_ctx, + pool)); + + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool)); + SVN_ERR(store_l2p_index_entry(fs, txn_id, entry.offset, + SVN_FS_FS__ITEM_INDEX_CHANGES, pool)); + } + + return SVN_NO_ERROR; +} + +/* 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, + 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 svn_fs_fs__id_part_t *txn_id, + svn_revnum_t rev, + apr_uint64_t start_node_id, + apr_uint64_t start_copy_id, + apr_pool_t *pool) +{ + apr_uint64_t txn_node_id; + apr_uint64_t txn_copy_id; + fs_fs_data_t *ffd = fs->fsap_data; + + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + return svn_fs_fs__write_current(fs, rev, 0, 0, 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)); + + start_node_id += txn_node_id; + start_copy_id += txn_copy_id; + + return svn_fs_fs__write_current(fs, rev, start_node_id, start_copy_id, + pool); +} + +/* Verify that the user registered with FS has all the locks necessary to + permit all the changes associated 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 svn_fs_fs__id_part_t *txn_id, + apr_hash_t *changed_paths, + apr_pool_t *pool) +{ + apr_pool_t *iterpool; + apr_array_header_t *changed_paths_sorted; + svn_stringbuf_t *last_recursed = NULL; + int i; + + /* Make an array of the changed paths, and sort them depth-first-ily. */ + changed_paths_sorted = svn_sort__hash(changed_paths, + svn_sort_compare_items_as_paths, + pool); + + /* 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. */ + iterpool = svn_pool_create(pool); + for (i = 0; i < changed_paths_sorted->nelts; i++) + { + const svn_sort__item_t *item; + const char *path; + svn_fs_path_change2_t *change; + svn_boolean_t recurse = TRUE; + + svn_pool_clear(iterpool); + + item = &APR_ARRAY_IDX(changed_paths_sorted, i, svn_sort__item_t); + + /* Fetch the change associated with our path. */ + path = item->key; + change = item->value; + + /* 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_fspath__skip_ancestor(last_recursed->data, path)) + continue; + + /* 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, + iterpool)); + + /* 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(iterpool); + return SVN_NO_ERROR; +} + +/* Return in *PATH the path to a file containing the properties that + make up the final revision properties file. This involves setting + svn:date and removing any temporary properties associated with the + commit flags. */ +static svn_error_t * +write_final_revprop(const char **path, + svn_fs_txn_t *txn, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + apr_hash_t *txnprops; + svn_boolean_t final_mods = FALSE; + svn_string_t date; + svn_string_t *client_date; + + SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool)); + + /* Remove any temporary txn props representing 'flags'. */ + if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD)) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, NULL); + final_mods = TRUE; + } + + if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS)) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL); + final_mods = TRUE; + } + + client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE); + if (client_date) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE, NULL); + final_mods = TRUE; + } + + /* Update commit time to ensure that svn:date revprops remain ordered if + requested. */ + if (!client_date || strcmp(client_date->data, "1")) + { + date.data = svn_time_to_cstring(apr_time_now(), pool); + date.len = strlen(date.data); + svn_hash_sets(txnprops, SVN_PROP_REVISION_DATE, &date); + final_mods = TRUE; + } + + if (final_mods) + { + SVN_ERR(set_txn_proplist(txn->fs, txn_id, txnprops, TRUE, pool)); + *path = path_txn_props_final(txn->fs, txn_id, pool); + } + else + { + *path = path_txn_props(txn->fs, txn_id, pool); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__add_index_data(svn_fs_t *fs, + apr_file_t *file, + const char *l2p_proto_index, + const char *p2l_proto_index, + svn_revnum_t revision, + apr_pool_t *pool) +{ + apr_off_t l2p_offset; + apr_off_t p2l_offset; + svn_stringbuf_t *footer; + unsigned char footer_length; + svn_checksum_t *l2p_checksum; + svn_checksum_t *p2l_checksum; + + /* Append the actual index data to the pack file. */ + l2p_offset = 0; + SVN_ERR(svn_io_file_seek(file, APR_END, &l2p_offset, pool)); + SVN_ERR(svn_fs_fs__l2p_index_append(&l2p_checksum, fs, file, + l2p_proto_index, revision, + pool, pool)); + + p2l_offset = 0; + SVN_ERR(svn_io_file_seek(file, APR_END, &p2l_offset, pool)); + SVN_ERR(svn_fs_fs__p2l_index_append(&p2l_checksum, fs, file, + p2l_proto_index, revision, + pool, pool)); + + /* Append footer. */ + footer = svn_fs_fs__unparse_footer(l2p_offset, l2p_checksum, + p2l_offset, p2l_checksum, pool, pool); + SVN_ERR(svn_io_file_write_full(file, footer->data, footer->len, NULL, + pool)); + + footer_length = footer->len; + SVN_ERR_ASSERT(footer_length == footer->len); + SVN_ERR(svn_io_file_write_full(file, &footer_length, 1, NULL, pool)); + + 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; + apr_uint64_t start_node_id; + apr_uint64_t start_copy_id; + svn_revnum_t old_rev, new_rev; + apr_file_t *proto_file; + void *proto_file_lockcookie; + apr_off_t initial_offset, changed_path_offset; + const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn); + apr_hash_t *changed_paths; + + /* Re-Read the current repository format. All our repo upgrade and + config evaluation strategies are such that existing information in + FS and FFD remains valid. + + Although we don't recommend upgrading hot repositories, people may + still do it and we must make sure to either handle them gracefully + or to error out. + + Committing pre-format 3 txns will fail after upgrade to format 3+ + because the proto-rev cannot be found; no further action needed. + Upgrades from pre-f7 to f7+ means a potential change in addressing + mode for the final rev. We must be sure to detect that cause because + the failure would only manifest once the new revision got committed. + */ + SVN_ERR(svn_fs_fs__read_format_file(cb->fs, pool)); + + /* Read the current youngest revision and, possibly, the next available + node id and copy id (for old format filesystems). Update the cached + value for the youngest revision, because we have just checked it. */ + SVN_ERR(svn_fs_fs__read_current(&old_rev, &start_node_id, &start_copy_id, + cb->fs, pool)); + ffd->youngest_rev_cache = old_rev; + + /* 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")); + + /* We need the changes list for verification as well as for writing it + to the final rev file. */ + SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, cb->fs, txn_id, + pool)); + + /* 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, txn_id, changed_paths, 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, txn_id, pool)); + SVN_ERR(svn_fs_fs__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_root(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, txn_id, changed_paths, + pool)); + + if (svn_fs_fs__use_log_addressing(cb->fs)) + { + /* Append the index data to the rev file. */ + SVN_ERR(svn_fs_fs__add_index_data(cb->fs, proto_file, + svn_fs_fs__path_l2p_proto_index(cb->fs, txn_id, pool), + svn_fs_fs__path_p2l_proto_index(cb->fs, txn_id, pool), + new_rev, pool)); + } + else + { + /* Write the final line. */ + + svn_stringbuf_t *trailer + = svn_fs_fs__unparse_revision_trailer + ((apr_off_t)svn_fs_fs__id_item(new_root_id), + changed_path_offset, + pool); + SVN_ERR(svn_io_file_write_full(proto_file, trailer->data, trailer->len, + 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. */ + + /* 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 + = svn_fs_fs__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(! svn_fs_fs__is_packed_revprop(cb->fs, new_rev)); + { + const char *new_dir + = svn_fs_fs__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. + + ### This "breaks" the transaction by removing the protorev file + ### but the revision is not yet complete. If this commit does + ### not complete for any reason the transaction will be lost. */ + old_rev_filename = svn_fs_fs__path_rev_absolute(cb->fs, old_rev, pool); + rev_filename = svn_fs_fs__path_rev(cb->fs, new_rev, pool); + proto_filename = svn_fs_fs__path_txn_proto_rev(cb->fs, txn_id, pool); + SVN_ERR(svn_fs_fs__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, txn_id, proto_file_lockcookie, pool)); + + /* Move the revprops file into place. */ + SVN_ERR_ASSERT(! svn_fs_fs__is_packed_revprop(cb->fs, new_rev)); + SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, txn_id, pool)); + final_revprop = svn_fs_fs__path_revprops(cb->fs, new_rev, pool); + SVN_ERR(svn_fs_fs__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, 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 *); + + SVN_ERR(svn_fs_fs__set_rep_reference(fs, rep, 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 . + */ + /* ### A commit that touches thousands of files will starve other + (reader/writer) commits for the duration of the below call. + Maybe write in batches? */ + 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__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_fs_fs__path_txns_dir(fs, 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 = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(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; + fs_txn_data_t *ftd; + svn_node_kind_t kind; + transaction_t *local_txn; + svn_fs_fs__id_part_t txn_id; + + SVN_ERR(svn_fs_fs__id_txn_parse(&txn_id, name)); + + /* First check to see if the directory exists. */ + SVN_ERR(svn_io_check_path(svn_fs_fs__path_txn_dir(fs, &txn_id, 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)); + ftd = apr_pcalloc(pool, sizeof(*ftd)); + ftd->txn_id = txn_id; + + /* 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, &txn_id, pool)); + + txn->base_rev = svn_fs_fs__id_rev(local_txn->base_id); + + txn->vtable = &txn_vtable; + txn->fsap_data = ftd; + *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, svn_fs_fs__txn_get_id(txn), + 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, pool)); + + /* Delete any mutable property representation. */ + if (noderev->prop_rep && is_txn_rep(noderev->prop_rep)) + SVN_ERR(svn_io_remove_file2(svn_fs_fs__path_txn_node_props(fs, id, pool), + FALSE, pool)); + + /* Delete any mutable data representation. */ + if (noderev->data_rep && is_txn_rep(noderev->data_rep) + && noderev->kind == svn_node_dir) + { + fs_fs_data_t *ffd = fs->fsap_data; + SVN_ERR(svn_io_remove_file2(svn_fs_fs__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(svn_fs_fs__path_txn_node_rev(fs, id, pool), + FALSE, 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 svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + transaction_t *txn; + SVN_ERR(svn_fs_fs__get_txn(&txn, fs, txn_id, 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; + fs_txn_data_t *ftd; + apr_hash_t *props = apr_hash_make(pool); + + 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); + + svn_hash_sets(props, SVN_PROP_REVISION_DATE, &date); + + /* Set temporary txn props that represent the requested 'flags' + behaviors. */ + if (flags & SVN_FS_TXN_CHECK_OOD) + svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_OOD, + svn_string_create("true", pool)); + + if (flags & SVN_FS_TXN_CHECK_LOCKS) + svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_LOCKS, + svn_string_create("true", pool)); + + if (flags & SVN_FS_TXN_CLIENT_DATE) + svn_hash_sets(props, SVN_FS__PROP_TXN_CLIENT_DATE, + svn_string_create("0", pool)); + + ftd = (*txn_p)->fsap_data; + return svn_error_trace(set_txn_proplist(fs, &ftd->txn_id, props, FALSE, + pool)); +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/transaction.h b/contrib/subversion/subversion/libsvn_fs_fs/transaction.h new file mode 100644 index 000000000..f96b04dd2 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/transaction.h @@ -0,0 +1,294 @@ +/* transaction.h --- transaction-related functions of FSFS + * + * ==================================================================== + * 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_FS__TRANSACTION_H +#define SVN_LIBSVN_FS__TRANSACTION_H + +#include "fs.h" + +/* Return the transaction ID of TXN. + */ +const svn_fs_fs__id_part_t * +svn_fs_fs__txn_get_id(svn_fs_txn_t *txn); + +/* Store NODEREV as the node-revision for the node whose id is ID in + FS, after setting its is_fresh_txn_root to FRESH_TXN_ROOT. Do any + necessary temporary allocation in POOL. */ +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); + +/* Find the paths which were changed in transaction TXN_ID of + filesystem FS and store them in *CHANGED_PATHS_P. + Get any temporary allocations from POOL. */ +svn_error_t * +svn_fs_fs__txn_changes_fetch(apr_hash_t **changed_paths_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Find the paths which were changed in revision REV of filesystem FS + and store them in *CHANGED_PATHS_P. Get any temporary allocations + from POOL. */ +svn_error_t * +svn_fs_fs__paths_changed(apr_hash_t **changed_paths_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Create a new transaction in filesystem FS, based on revision REV, + and store it in *TXN_P. Allocate all necessary variables from + POOL. */ +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); + +/* Set the transaction property NAME to the value VALUE in transaction + TXN. Perform temporary allocations from POOL. */ +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); + +/* Change transaction properties in transaction TXN based on PROPS. + Perform temporary allocations from 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); + +/* Store a transaction record in *TXN_P for the transaction identified + by TXN_ID in filesystem FS. Allocate everything from POOL. */ +svn_error_t * +svn_fs_fs__get_txn(transaction_t **txn_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the next available copy_id in *COPY_ID for the transaction + TXN_ID in filesystem FS. Allocate space in POOL. */ +svn_error_t * +svn_fs_fs__reserve_copy_id(svn_fs_fs__id_part_t *copy_id_p, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Create an entirely new mutable node in the filesystem FS, whose + node-revision is NODEREV. Set *ID_P to the new node revision's ID. + Use POOL for any temporary allocation. COPY_ID is the copy_id to + use in the node revision ID. TXN_ID is the Subversion transaction + under which this occurs. */ +svn_error_t * +svn_fs_fs__create_node(const svn_fs_id_t **id_p, + svn_fs_t *fs, + node_revision_t *noderev, + const svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Remove all references to the transaction TXN_ID from filesystem FS. + Temporary allocations are from POOL. */ +svn_error_t * +svn_fs_fs__purge_txn(svn_fs_t *fs, + const char *txn_id, + apr_pool_t *pool); + +/* Abort the existing transaction TXN, performing any temporary + allocations in POOL. */ +svn_error_t * +svn_fs_fs__abort_txn(svn_fs_txn_t *txn, + apr_pool_t *pool); + +/* Add or set in filesystem FS, transaction TXN_ID, in directory + PARENT_NODEREV a directory entry for NAME pointing to ID of type + KIND. Allocations are done in POOL. */ +svn_error_t * +svn_fs_fs__set_entry(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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); + +/* Add a change to the changes record for filesystem FS in transaction + TXN_ID. Mark path PATH, having node-id ID, as changed according to + the type in CHANGE_KIND. If the text representation was changed set + TEXT_MOD to TRUE, and likewise for PROP_MOD as well as MERGEINFO_MOD. + If this change was the result of a copy, set COPYFROM_REV and + COPYFROM_PATH to the revision and path of the copy source, otherwise + they should be set to SVN_INVALID_REVNUM and NULL. Perform any + temporary allocations from POOL. */ +svn_error_t * +svn_fs_fs__add_change(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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_boolean_t mergeinfo_mod, + svn_node_kind_t node_kind, + svn_revnum_t copyfrom_rev, + const char *copyfrom_path, + apr_pool_t *pool); + +/* Return a writable stream in *STREAM that allows storing the text + representation of node-revision NODEREV in filesystem FS. + Allocations are from POOL. */ +svn_error_t * +svn_fs_fs__set_contents(svn_stream_t **stream, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *pool); + +/* Create a node revision in FS which is an immediate successor of + OLD_ID, whose contents are NEW_NR. Set *NEW_ID_P to the new node + revision's ID. Use POOL for any temporary allocation. + + COPY_ID, if non-NULL, is a key into the `copies' table, and + indicates that this new node is being created as the result of a + copy operation, and specifically which operation that was. If + COPY_ID is NULL, then re-use the copy ID from the predecessor node. + + TXN_ID is the Subversion transaction under which this occurs. + + After this call, the deltification code assumes that the new node's + contents will change frequently, and will avoid representing other + nodes as deltas against this node's contents. */ +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 svn_fs_fs__id_part_t *copy_id, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Write a new property list PROPLIST for node-revision NODEREV in + filesystem FS. Perform any temporary allocations in POOL. */ +svn_error_t * +svn_fs_fs__set_proplist(svn_fs_t *fs, + node_revision_t *noderev, + apr_hash_t *proplist, + apr_pool_t *pool); + +/* Append the L2P and P2L indexes given by their proto index file names + * L2P_PROTO_INDEX and P2L_PROTO_INDEX to the revision / pack FILE. + * The latter contains revision(s) starting at REVISION in FS. + * Use POOL for temporary allocations. */ +svn_error_t * +svn_fs_fs__add_index_data(svn_fs_t *fs, + apr_file_t *file, + const char *l2p_proto_index, + const char *p2l_proto_index, + svn_revnum_t revision, + apr_pool_t *pool); + +/* Commit the transaction TXN in filesystem FS and return its new + revision number in *REV. If the transaction is out of date, return + the error SVN_ERR_FS_TXN_OUT_OF_DATE. Use POOL for temporary + allocations. */ +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); + +/* Set *NAMES_P to an array of names which are all the active + transactions in filesystem FS. Allocate the array from POOL. */ +svn_error_t * +svn_fs_fs__list_transactions(apr_array_header_t **names_p, + svn_fs_t *fs, + apr_pool_t *pool); + +/* Open the transaction named NAME in filesystem FS. Set *TXN_P to + * the transaction. If there is no such transaction, return +` * SVN_ERR_FS_NO_SUCH_TRANSACTION. Allocate the new transaction in + * POOL. */ +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); + +/* Return the property list from transaction TXN and store it in + *PROPLIST. Allocate the property list from POOL. */ +svn_error_t * +svn_fs_fs__txn_proplist(apr_hash_t **table_p, + svn_fs_txn_t *txn, + apr_pool_t *pool); + +/* Delete the mutable node-revision referenced by ID, along with any + mutable props or directory contents associated with it. Perform + temporary allocations in POOL. */ +svn_error_t * +svn_fs_fs__delete_node_revision(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool); + +/* Retrieve information about the Subversion transaction SVN_TXN from + the `transactions' table of FS, allocating from POOL. Set + *ROOT_ID_P to the ID of the transaction's root directory. Set + *BASE_ROOT_ID_P to the ID of the root directory of the + transaction's base revision. + + If there is no such transaction, SVN_ERR_FS_NO_SUCH_TRANSACTION is + the error returned. + + Returns SVN_ERR_FS_TRANSACTION_NOT_MUTABLE if TXN_NAME refers to a + transaction that has already been committed. + + Allocate *ROOT_ID_P and *BASE_ROOT_ID_P in POOL. */ +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 svn_fs_fs__id_part_t *txn_name, + apr_pool_t *pool); + +/* Find the value of the property named PROPNAME in transaction TXN. + Return the contents in *VALUE_P. The contents will be allocated + from POOL. */ +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); + +/* Begin a new transaction in filesystem FS, based on existing + revision REV. The new transaction is returned in *TXN_P. Allocate + the new transaction structure from POOL. */ +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); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/tree.c b/contrib/subversion/subversion/libsvn_fs_fs/tree.c index acd1eb411..0047bef25 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/tree.c +++ b/contrib/subversion/subversion/libsvn_fs_fs/tree.c @@ -49,15 +49,19 @@ #include "svn_mergeinfo.h" #include "svn_fs.h" #include "svn_props.h" +#include "svn_sorts.h" #include "fs.h" -#include "key-gen.h" +#include "cached_data.h" #include "dag.h" #include "lock.h" #include "tree.h" #include "fs_fs.h" #include "id.h" +#include "pack.h" #include "temp_serializer.h" +#include "transaction.h" +#include "util.h" #include "private/svn_mergeinfo_private.h" #include "private/svn_subr_private.h" @@ -66,26 +70,6 @@ #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. @@ -100,23 +84,12 @@ 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 dag_node_t fs_rev_root_data_t; typedef struct fs_txn_root_data_t { - const char *txn_id; + /* TXN_ID value from the main struct but as a struct instead of a string */ + svn_fs_fs__id_part_t txn_id; /* Cache of txn DAG nodes (without their nested noderevs, because * it's mutable). Same keys/values as ffd->rev_node_cache. */ @@ -134,10 +107,18 @@ static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev, 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, + svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn, + svn_revnum_t base_rev, + apr_uint32_t flags, apr_pool_t *pool); +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); + /*** Node Caching ***/ @@ -196,6 +177,13 @@ struct fs_fs_dag_cache_t /* 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; + + /* Position of the last bucket hit that actually had a DAG node in it. + LAST_HIT may refer to a bucket that matches path@rev but has not + its NODE element set, yet. + This value is a mere hint for optimistic lookup and any value is + valid (as long as it is < BUCKET_COUNT). */ + apr_size_t last_non_empty; }; fs_fs_dag_cache_t* @@ -245,11 +233,36 @@ cache_lookup( fs_fs_dag_cache_t *cache && (result->path_len == path_len) && !memcmp(result->path, path, path_len)) { + /* Remember the position of the last node we found in this cache. */ + if (result->node) + cache->last_non_empty = cache->last_hit; + return result; } /* need to do a full lookup. Calculate the hash value - (HASH_VALUE has been initialized to REVISION). */ + (HASH_VALUE has been initialized to REVISION). + + Note that the actual hash function is arbitrary as long as its result + in HASH_VALUE only depends on REVISION and *PATH. However, we try to + make as much of *PATH influence the result as possible to get an "even" + spread across the hash buckets (maximizes our cache retention rate and + thus the hit rates). + + When chunked access is possible (independent of the PATH pointer's + value!), we read 4 bytes at once and multiply the hash value with a + FACTOR that mirror / pattern / shift all 4 input bytes to various bits + of the result. The final result will be taken from the MSBs. + + When chunked access is not possible (not supported by CPU or odd bytes + at the end of *PATH), we use the simple traditional "* 33" hash + function that works very well with texts / paths and that e.g. APR uses. + + Please note that the bytewise and the chunked calculation are *NOT* + interchangeable as they will yield different results for the same input. + For any given machine and *PATH, we must use a fixed combination of the + two functions. + */ i = 0; #if SVN_UNALIGNED_ACCESS_IS_OK /* We relax the dependency chain between iterations by processing @@ -295,14 +308,44 @@ cache_lookup( fs_fs_dag_cache_t *cache cache->insertions++; } + else if (result->node) + { + /* This bucket is valid & has a suitable DAG node in it. + Remember its location. */ + cache->last_non_empty = bucket_index; + } return result; } +/* Optimistic lookup using the last seen non-empty location in CACHE. + Return the node of that entry, if it is still in use and matches PATH. + Return NULL otherwise. Since the caller usually already knows the path + length, provide it in PATH_LEN. */ +static dag_node_t * +cache_lookup_last_path(fs_fs_dag_cache_t *cache, + const char *path, + apr_size_t path_len) +{ + cache_entry_t *result = &cache->buckets[cache->last_non_empty]; + assert(strlen(path) == path_len); + + if ( result->node + && (result->path_len == path_len) + && !memcmp(result->path, path, path_len)) + { + return result->node; + } + + return NULL; +} + /* 2nd level cache */ /* Find and return the DAG node cache for ROOT and the key that - should be used for PATH. */ + should be used for PATH. + + Pool will only be used for allocating a new keys if necessary */ static void locate_cache(svn_cache__t **cache, const char **key, @@ -313,20 +356,25 @@ locate_cache(svn_cache__t **cache, 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; + + 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); + + if (cache) + *cache = ffd->rev_node_cache; + if (key && path) + *key = svn_fs_fs__combine_number_and_string(root->rev, path, pool); } } -/* 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. */ +/* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL + if the node isn't cached. *NODE_P is allocated in POOL. */ static svn_error_t * dag_node_cache_get(dag_node_t **node_p, svn_fs_root_t *root, @@ -356,7 +404,7 @@ dag_node_cache_get(dag_node_t **node_p, if (found && node) { /* Patch up the FS, since this might have come from an old FS - * object. */ + * object. */ svn_fs_fs__dag_set_fs(node, root->fs); /* Retain the DAG node in L1 cache. */ @@ -380,7 +428,7 @@ dag_node_cache_get(dag_node_t **node_p, if (found && node) { /* Patch up the FS, since this might have come from an old FS - * object. */ + * object. */ svn_fs_fs__dag_set_fs(node, root->fs); } } @@ -403,27 +451,23 @@ dag_node_cache_set(svn_fs_root_t *root, 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. */ +/* Baton for find_descendants_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 +/* If the given item is a descendant 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, +find_descendants_in_cache(void *baton, const void *key, apr_ssize_t klen, void *val, @@ -458,16 +502,16 @@ dag_node_cache_invalidate(svn_fs_root_t *root, locate_cache(&cache, NULL, root, NULL, b.pool); - SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache, + SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_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 *); + const char *descendant = APR_ARRAY_IDX(b.list, i, const char *); svn_pool_clear(iterpool); - SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool)); + SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool)); } svn_pool_destroy(iterpool); @@ -498,7 +542,8 @@ svn_fs_fs__txn_root(svn_fs_root_t **root_p, flags |= SVN_FS_TXN_CHECK_LOCKS; } - return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool); + return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn), + txn->base_rev, flags, pool); } @@ -523,6 +568,15 @@ svn_fs_fs__revision_root(svn_fs_root_t **root_p, /* Getting dag nodes for roots. */ +/* Return the transaction ID to a given transaction ROOT. */ +static const svn_fs_fs__id_part_t * +root_txn_id(svn_fs_root_t *root) +{ + fs_txn_root_data_t *frd = root->fsap_data; + assert(root->is_txn_root); + + return &frd->txn_id; +} /* Set *NODE_P to a freshly opened dag node referring to the root directory of ROOT, allocating from POOL. */ @@ -534,14 +588,15 @@ root_node(dag_node_t **node_p, 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); + return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root), + 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); + dag_node_t *root_dir = root->fsap_data; + *node_p = svn_fs_fs__dag_dup(root_dir, pool); return SVN_NO_ERROR; } } @@ -557,7 +612,11 @@ mutable_root_node(dag_node_t **node_p, apr_pool_t *pool) { if (root->is_txn_root) - return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool); + { + /* It's a transaction root. Open a fresh copy. */ + return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root), + 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); @@ -645,25 +704,23 @@ parent_path_relpath(parent_path_t *child, 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. */ + node). 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 svn_fs_fs__id_part_t *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); + SVN_ERR_ASSERT(child && child->parent); /* Initialize some convenience variables. */ child_id = svn_fs_fs__dag_get_id(child->node); @@ -672,7 +729,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, 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)) + if (svn_fs_fs__id_is_txn(child_id)) { *inherit_p = copy_id_inherit_self; *copy_src_path = NULL; @@ -686,14 +743,14 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, /* Special case: if the child's copy ID is '0', use the parent's copy ID. */ - if (strcmp(child_copy_id, "0") == 0) + if (svn_fs_fs__id_part_is_root(child_copy_id)) 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) + if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id)) return SVN_NO_ERROR; /* If the child is on the same branch that the parent is on, the @@ -709,7 +766,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p, SVN_ERR(get_dag(©root_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) + if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated) return SVN_NO_ERROR; /* Determine if we are looking at the child via its original path or @@ -763,9 +820,63 @@ typedef enum open_path_flags_t { /* The caller does not care about the parent node chain but only the final DAG node. */ - open_path_node_only = 4 + open_path_node_only = 4, + + /* The caller wants a NULL path object instead of an error if the + path cannot be found. */ + open_path_allow_null = 8 } open_path_flags_t; +/* Try a short-cut for the open_path() function using the last node accessed. + * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is + * its "created path", return the node in *NODE_P. Set it to NULL otherwise. + * + * This function is used to support ra_serf-style access patterns where we + * are first asked for path@rev and then for path@c_rev of the same node. + * The shortcut works by ignoring the "rev" part of the cache key and then + * checking whether we got lucky. Lookup and verification are both quick + * plus there are many early outs for common types of mismatch. + */ +static svn_error_t * +try_match_last_node(dag_node_t **node_p, + svn_fs_root_t *root, + const char *path, + apr_size_t path_len, + apr_pool_t *scratch_pool) +{ + fs_fs_data_t *ffd = root->fs->fsap_data; + + /* Optimistic lookup: if the last node returned from the cache applied to + the same PATH, return it in NODE. */ + dag_node_t *node + = cache_lookup_last_path(ffd->dag_node_cache, path, path_len); + + /* Did we get a bucket with a committed node? */ + if (node && !svn_fs_fs__dag_check_mutable(node)) + { + /* Get the path&rev pair at which this node was created. + This is repository location for which this node is _known_ to be + the right lookup result irrespective of how we found it. */ + const char *created_path + = svn_fs_fs__dag_get_created_path(node); + svn_revnum_t revision; + SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool)); + + /* Is it an exact match? */ + if (revision == root->rev && strcmp(created_path, path) == 0) + { + /* Cache it under its full path@rev access path. */ + SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool)); + + *node_p = node; + return SVN_NO_ERROR; + } + } + + *node_p = NULL; + return SVN_NO_ERROR; +} + /* 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 @@ -773,10 +884,9 @@ typedef enum open_path_flags_t { *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. + modified, or if copy ID inheritance information is otherwise needed, + IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, 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 @@ -800,38 +910,80 @@ open_path(parent_path_t **parent_path_p, svn_fs_root_t *root, const char *path, int flags, - const char *txn_id, + svn_boolean_t is_txn_path, 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 = "/"; + const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */ 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; + /* path to the currently processed entry without trailing '/'. + We will reuse this across iterations by simply putting a NUL terminator + at the respective position and replacing that with a '/' in the next + iteration. This is correct as we assert() PATH to be canonical. */ + svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool); + apr_size_t path_len = path_so_far->len; + + /* Callers often traverse the DAG in some path-based order or along the + history segments. That allows us to try a few guesses about where to + find the next item. This is only useful if the caller didn't request + the full parent chain. */ assert(svn_fs__is_canonical_abspath(path)); + path_so_far->len = 0; /* "" */ if (flags & open_path_node_only) { + const char *directory; + + /* First attempt: Assume that we access the DAG for the same path as + in the last lookup but for a different revision that happens to be + the last revision that touched the respective node. This is a + common pattern when e.g. checking out over ra_serf. Note that this + will only work for committed data as the revision info for nodes in + txns is bogus. + + This shortcut is quick and will exit this function upon success. + So, try it first. */ + if (!root->is_txn_root) + { + dag_node_t *node; + SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool)); + + /* Did the shortcut work? */ + if (node) + { + /* Construct and return the result. */ + svn_pool_destroy(iterpool); + + parent_path = make_parent_path(node, 0, 0, pool); + parent_path->copy_inherit = copy_id_inherit_self; + *parent_path_p = parent_path; + + return SVN_NO_ERROR; + } + } + + /* Second attempt: Try starting the lookup immediately at the parent + node. We will often have recently accessed either a sibling or + said parent DIRECTORY itself for the same revision. */ directory = svn_dirent_dirname(path, pool); if (directory[1] != 0) /* root nodes are covered anyway */ - SVN_ERR(dag_node_cache_get(&here, root, directory, pool)); + { + SVN_ERR(dag_node_cache_get(&here, root, directory, pool)); + + /* Did the shortcut work? */ + if (here) + { + apr_size_t dirname_len = strlen(directory); + path_so_far->len = dirname_len; + rest = path + dirname_len + 1; + } + } } /* did the shortcut work? */ - if (here) - { - path_so_far = directory; - rest = path + strlen(directory) + 1; - } - else + if (!here) { /* Make a parent_path item for the root node, using its own current copy id. */ @@ -839,6 +991,7 @@ open_path(parent_path_t **parent_path_p, rest = path + 1; /* skip the leading '/', it saves in iteration */ } + path_so_far->data[path_so_far->len] = '\0'; parent_path = make_parent_path(here, 0, 0, pool); parent_path->copy_inherit = copy_id_inherit_self; @@ -858,8 +1011,10 @@ open_path(parent_path_t **parent_path_p, /* 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); + /* Update the path traversed thus far. */ + path_so_far->data[path_so_far->len] = '/'; + path_so_far->len += strlen(entry) + 1; + path_so_far->data[path_so_far->len] = '\0'; if (*entry == '\0') { @@ -873,7 +1028,6 @@ open_path(parent_path_t **parent_path_p, { 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 @@ -882,22 +1036,20 @@ open_path(parent_path_t **parent_path_p, 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, pool)); - + SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data, + pool)); if (cached_node) child = cached_node; else - err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool); + SVN_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 (child == NULL) { /* 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')) { @@ -905,6 +1057,11 @@ open_path(parent_path_t **parent_path_p, pool); break; } + else if (flags & open_path_allow_null) + { + parent_path = NULL; + break; + } else { /* Build a better error message than svn_fs_fs__dag_open @@ -913,22 +1070,19 @@ open_path(parent_path_t **parent_path_p, } } - /* Other errors we return normally. */ - SVN_ERR(err); - if (flags & open_path_node_only) { - /* Shortcut: the caller only wan'ts the final DAG node. */ + /* Shortcut: the caller only wants 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) + if (is_txn_path) { SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs, - parent_path, txn_id, iterpool)); + parent_path, iterpool)); parent_path->copy_inherit = inherit; parent_path->copy_src_path = apr_pstrdup(pool, copy_path); } @@ -936,7 +1090,8 @@ open_path(parent_path_t **parent_path_p, /* 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)); + SVN_ERR(dag_node_cache_set(root, path_so_far->data, child, + iterpool)); } /* Are we finished traversing the path? */ @@ -945,7 +1100,7 @@ open_path(parent_path_t **parent_path_p, /* 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), + SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data), apr_psprintf(iterpool, _("Failure opening '%s'"), path)); rest = next; @@ -970,7 +1125,7 @@ make_path_mutable(svn_fs_root_t *root, apr_pool_t *pool) { dag_node_t *clone; - const char *txn_id = root->txn; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(root); /* Is the node mutable already? */ if (svn_fs_fs__dag_check_mutable(parent_path->node)) @@ -980,7 +1135,8 @@ make_path_mutable(svn_fs_root_t *root, if (parent_path->parent) { const svn_fs_id_t *parent_id, *child_id, *copyroot_id; - const char *copy_id = NULL; + svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 }; + svn_fs_fs__id_part_t *copy_id_ptr = ©_id; copy_id_inherit_t inherit = parent_path->copy_inherit; const char *clone_path, *copyroot_path; svn_revnum_t copyroot_rev; @@ -997,7 +1153,7 @@ make_path_mutable(svn_fs_root_t *root, { 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); + copy_id = *svn_fs_fs__id_copy_id(parent_id); break; case copy_id_inherit_new: @@ -1006,7 +1162,7 @@ make_path_mutable(svn_fs_root_t *root, break; case copy_id_inherit_self: - copy_id = NULL; + copy_id_ptr = NULL; break; case copy_id_inherit_unknown: @@ -1024,8 +1180,8 @@ make_path_mutable(svn_fs_root_t *root, 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) + if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id), + svn_fs_fs__id_node_id(copyroot_id))) is_parent_copyroot = TRUE; /* Now make this node mutable. */ @@ -1034,7 +1190,7 @@ make_path_mutable(svn_fs_root_t *root, parent_path->parent->node, clone_path, parent_path->entry, - copy_id, txn_id, + copy_id_ptr, txn_id, is_parent_copyroot, pool)); @@ -1074,14 +1230,13 @@ get_dag(dag_node_t **dag_node_p, 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, pool)); - } + /* Canonicalize the input PATH. As it turns out, >95% of all paths + * seen here during e.g. svnadmin verify are non-canonical, i.e. + * miss the leading '/'. Unconditional canonicalization has a net + * performance benefit over previously checking path for being + * canonical. */ + path = svn_fs__canonicalize_abspath(path, pool); + SVN_ERR(dag_node_cache_get(&node, root, path, pool)); if (! node) { @@ -1089,7 +1244,7 @@ get_dag(dag_node_t **dag_node_p, * 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)); + FALSE, pool)); node = parent_path->node; /* No need to cache our find -- open_path() will do that for us. */ @@ -1107,19 +1262,20 @@ get_dag(dag_node_t **dag_node_p, /* 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. */ + deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or + MERGEINFO_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 svn_fs_fs__id_part_t *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_boolean_t mergeinfo_mod, svn_node_kind_t node_kind, svn_revnum_t copyfrom_rev, const char *copyfrom_path, @@ -1127,7 +1283,8 @@ add_change(svn_fs_t *fs, { return svn_fs_fs__add_change(fs, txn_id, svn_fs__canonicalize_abspath(path, pool), - noderev_id, change_kind, text_mod, prop_mod, + noderev_id, change_kind, + text_mod, prop_mod, mergeinfo_mod, node_kind, copyfrom_rev, copyfrom_path, pool); } @@ -1151,8 +1308,8 @@ svn_fs_fs__node_id(const svn_fs_id_t **id_p, 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); + dag_node_t *root_dir = root->fsap_data; + *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool); } else { @@ -1164,6 +1321,82 @@ svn_fs_fs__node_id(const svn_fs_id_t **id_p, return SVN_NO_ERROR; } +static svn_error_t * +fs_node_relation(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, const char *path_a, + svn_fs_root_t *root_b, const char *path_b, + apr_pool_t *pool) +{ + dag_node_t *node; + const svn_fs_id_t *id_a, *id_b; + svn_fs_fs__id_part_t node_id_a, node_id_b; + + /* Root paths are a common special case. */ + svn_boolean_t a_is_root_dir + = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0')); + svn_boolean_t b_is_root_dir + = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0')); + + /* Another useful thing to know: Both are txns but not the same txn. */ + svn_boolean_t different_txn + = root_a->is_txn_root && root_b->is_txn_root + && strcmp(root_a->txn, root_b->txn); + + /* Path from different repository are always unrelated. */ + if (root_a->fs != root_b->fs) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + /* Are both (!) root paths? Then, they are related and we only test how + * direct the relation is. */ + if (a_is_root_dir && b_is_root_dir) + { + /* For txn roots, root->REV is the base revision of that TXN. */ + *relation = ( (root_a->rev == root_b->rev) + && (root_a->is_txn_root == root_b->is_txn_root) + && !different_txn) + ? svn_fs_node_unchanged + : svn_fs_node_common_ancestor; + return SVN_NO_ERROR; + } + + /* We checked for all separations between ID spaces (repos, txn). + * Now, we can simply test for the ID values themselves. */ + SVN_ERR(get_dag(&node, root_a, path_a, pool)); + id_a = svn_fs_fs__dag_get_id(node); + node_id_a = *svn_fs_fs__id_node_id(id_a); + + SVN_ERR(get_dag(&node, root_b, path_b, pool)); + id_b = svn_fs_fs__dag_get_id(node); + node_id_b = *svn_fs_fs__id_node_id(id_b); + + /* Noderevs from different nodes are unrelated. */ + if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b)) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + /* Noderevs have the same node-ID now. So, they *seem* to be related. + * + * Special case: Different txns may create the same (txn-local) node ID. + * Only when they are committed can they actually be related to others. */ + if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + /* The noderevs are actually related. Are they the same? */ + if (svn_fs_fs__id_eq(id_a, id_b)) + *relation = svn_fs_node_unchanged; + else + *relation = svn_fs_node_common_ancestor; + + return SVN_NO_ERROR; +} svn_error_t * svn_fs_fs__node_created_rev(svn_revnum_t *revision, @@ -1282,6 +1515,19 @@ fs_node_proplist(apr_hash_t **table_p, return SVN_NO_ERROR; } +static svn_error_t * +fs_node_has_props(svn_boolean_t *has_props, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + dag_node_t *node; + + SVN_ERR(get_dag(&node, root, path, scratch_pool)); + + return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node, + scratch_pool)); +} static svn_error_t * increment_mergeinfo_up_tree(parent_path_t *pp, @@ -1310,14 +1556,15 @@ fs_change_node_prop(svn_fs_root_t *root, { parent_path_t *parent_path; apr_hash_t *proplist; - const char *txn_id; + const svn_fs_fs__id_part_t *txn_id; + svn_boolean_t mergeinfo_mod = FALSE; if (! root->is_txn_root) return SVN_FS__NOT_TXN(root); - txn_id = root->txn; + txn_id = root_txn_id(root); path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool)); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool)); /* Check (non-recursively) to see if path is locked; if so, check that we can use it. */ @@ -1354,6 +1601,8 @@ fs_change_node_prop(svn_fs_root_t *root, SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node, (value != NULL), pool)); } + + mergeinfo_mod = TRUE; } /* Set the property. */ @@ -1366,7 +1615,7 @@ fs_change_node_prop(svn_fs_root_t *root, /* 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_path_change_modify, FALSE, TRUE, mergeinfo_mod, svn_fs_fs__dag_node_kind(parent_path->node), SVN_INVALID_REVNUM, NULL, pool); } @@ -1382,6 +1631,7 @@ fs_props_changed(svn_boolean_t *changed_p, const char *path1, svn_fs_root_t *root2, const char *path2, + svn_boolean_t strict, apr_pool_t *pool) { dag_node_t *node1, *node2; @@ -1395,7 +1645,7 @@ fs_props_changed(svn_boolean_t *changed_p, 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); + node1, node2, strict, pool); } @@ -1423,6 +1673,53 @@ conflict_err(svn_stringbuf_t *conflict_path, _("Conflict at '%s'"), path); } +/* Compare the directory representations at nodes LHS and RHS and set + * *CHANGED to TRUE, if at least one entry has been added or removed them. + * Use POOL for temporary allocations. + */ +static svn_error_t * +compare_dir_structure(svn_boolean_t *changed, + dag_node_t *lhs, + dag_node_t *rhs, + apr_pool_t *pool) +{ + apr_array_header_t *lhs_entries; + apr_array_header_t *rhs_entries; + int i; + + SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool)); + SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool)); + + /* different number of entries -> some addition / removal */ + if (lhs_entries->nelts != rhs_entries->nelts) + { + *changed = TRUE; + return SVN_NO_ERROR; + } + + /* Since directories are sorted by name, we can simply compare their + entries one-by-one without binary lookup etc. */ + for (i = 0; i < lhs_entries->nelts; ++i) + { + svn_fs_dirent_t *lhs_entry + = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *); + svn_fs_dirent_t *rhs_entry + = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *); + + if (strcmp(lhs_entry->name, rhs_entry->name) + || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id), + svn_fs_fs__id_node_id(rhs_entry->id)) + || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id), + svn_fs_fs__id_copy_id(rhs_entry->id))) + { + *changed = TRUE; + return SVN_NO_ERROR; + } + } + + *changed = FALSE; + return SVN_NO_ERROR; +} /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR * and TARGET must be distinct node revisions. TARGET_PATH should @@ -1452,13 +1749,13 @@ merge(svn_stringbuf_t *conflict_p, dag_node_t *target, dag_node_t *source, dag_node_t *ancestor, - const char *txn_id, + const svn_fs_fs__id_part_t *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; + apr_array_header_t *s_entries, *t_entries, *a_entries; + int i, s_idx = -1, t_idx = -1; svn_fs_t *fs; apr_pool_t *iterpool; apr_int64_t mergeinfo_increment = 0; @@ -1585,22 +1882,43 @@ merge(svn_stringbuf_t *conflict_p, */ { node_revision_t *tgt_nr, *anc_nr, *src_nr; + svn_boolean_t same; + apr_pool_t *scratch_pool; /* 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)); + scratch_pool = svn_pool_create(pool); + SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool, + scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool, + scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool, + scratch_pool)); + svn_pool_destroy(scratch_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)) + different contents. */ + SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, pool)); + if (! same) return conflict_err(conflict_p, target_path); + + /* The directory entries got changed in the repository but the directory + properties did not. */ + SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, pool)); + if (! same) + { + /* There is an incoming prop change for this directory. + We will accept it only if the directory changes were mere updates + to its entries, i.e. there were no additions or removals. + Those could cause update problems to the working copy. */ + svn_boolean_t changed; + SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool)); + + if (changed) + return conflict_err(conflict_p, target_path); + } } /* ### todo: it would be more efficient to simply check for a NULL @@ -1614,27 +1932,19 @@ merge(svn_stringbuf_t *conflict_p, /* 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)) + for (i = 0; i < a_entries->nelts; ++i) { 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); + a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *); + s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx); + t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx); /* 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; + continue; /* A change was made to this entry while the transaction was in process, but the transaction did not touch this entry. */ @@ -1665,15 +1975,16 @@ merge(svn_stringbuf_t *conflict_p, mergeinfo_increment += mergeinfo_end; } - SVN_ERR(svn_fs_fs__dag_set_entry(target, name, + SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name, s_entry->id, s_entry->kind, txn_id, - iterpool)); + pool)); } else { - SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool)); + SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id, + iterpool)); } } @@ -1706,14 +2017,14 @@ merge(svn_stringbuf_t *conflict_p, /* 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) + if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id), + svn_fs_fs__id_node_id(a_entry->id)) + || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id), + svn_fs_fs__id_copy_id(a_entry->id)) + || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id), + svn_fs_fs__id_node_id(a_entry->id)) + || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id), + svn_fs_fs__id_copy_id(a_entry->id))) return conflict_err(conflict_p, svn_fspath__join(target_path, a_entry->name, @@ -1737,29 +2048,23 @@ merge(svn_stringbuf_t *conflict_p, 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)) + for (i = 0; i < s_entries->nelts; ++i) { - 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); + svn_fs_dirent_t *a_entry, *s_entry, *t_entry; 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); + s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *); + a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx); + t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx); + + /* Process only entries in source that are NOT in ancestor. */ + if (a_entry) + continue; /* If NAME exists in TARGET, declare a conflict. */ if (t_entry) @@ -1816,7 +2121,7 @@ merge_changes(dag_node_t *ancestor_node, { dag_node_t *txn_root_node; svn_fs_t *fs = txn->fs; - const char *txn_id = txn->id; + const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn); SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool)); @@ -1891,6 +2196,7 @@ svn_fs_fs__commit_txn(const char **conflict_p, svn_error_t *err = SVN_NO_ERROR; svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool); svn_fs_t *fs = txn->fs; + fs_fs_data_t *ffd = fs->fsap_data; /* Limit memory usage when the repository has a high commit rate and needs to run the following while loop multiple times. The memory @@ -1973,7 +2279,15 @@ svn_fs_fs__commit_txn(const char **conflict_p, svn_fs_fs__reset_txn_caches(fs); svn_pool_destroy(iterpool); - return svn_error_trace(err); + + SVN_ERR(err); + + if (ffd->pack_after_commit) + { + SVN_ERR(svn_fs_fs__pack(fs, NULL, NULL, NULL, NULL, pool)); + } + + return SVN_NO_ERROR; } @@ -2068,10 +2382,36 @@ fs_dir_entries(apr_hash_t **table_p, apr_pool_t *pool) { dag_node_t *node; + apr_hash_t *hash = svn_hash__make(pool); + apr_array_header_t *table; + int i; /* Get the entries for this path in the caller's pool. */ SVN_ERR(get_dag(&node, root, path, pool)); - return svn_fs_fs__dag_dir_entries(table_p, node, pool); + SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool)); + + /* Convert directory array to hash. */ + for (i = 0; i < table->nelts; ++i) + { + svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *); + svn_hash_sets(hash, entry->name, entry); + } + + *table_p = hash; + return SVN_NO_ERROR; +} + +static svn_error_t * +fs_dir_optimal_order(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool, + scratch_pool); + + return SVN_NO_ERROR; } /* Raise an error if PATH contains a newline because FSFS cannot handle @@ -2100,13 +2440,13 @@ fs_make_dir(svn_fs_root_t *root, { parent_path_t *parent_path; dag_node_t *sub_dir; - const char *txn_id = root->txn; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(root); 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)); + TRUE, 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 @@ -2136,8 +2476,8 @@ fs_make_dir(svn_fs_root_t *root, /* 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); + svn_fs_path_change_add, FALSE, FALSE, FALSE, + svn_node_dir, SVN_INVALID_REVNUM, NULL, pool); } @@ -2149,15 +2489,16 @@ fs_delete_node(svn_fs_root_t *root, apr_pool_t *pool) { parent_path_t *parent_path; - const char *txn_id = root->txn; + const svn_fs_fs__id_part_t *txn_id; apr_int64_t mergeinfo_count = 0; svn_node_kind_t kind; if (! root->is_txn_root) return SVN_FS__NOT_TXN(root); + txn_id = root_txn_id(root); path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool)); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool)); kind = svn_fs_fs__dag_node_kind(parent_path->node); /* We can't remove the root of the filesystem. */ @@ -2193,7 +2534,7 @@ fs_delete_node(svn_fs_root_t *root, /* 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_fs_path_change_delete, FALSE, FALSE, FALSE, kind, SVN_INVALID_REVNUM, NULL, pool); } @@ -2224,7 +2565,7 @@ copy_helper(svn_fs_root_t *from_root, { dag_node_t *from_node; parent_path_t *to_parent_path; - const char *txn_id = to_root->txn; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root); svn_boolean_t same_p; /* Use an error check, not an assert, because even the caller cannot @@ -2236,11 +2577,17 @@ copy_helper(svn_fs_root_t *from_root, _("Cannot copy between two different filesystems ('%s' and '%s')"), from_root->fs->path, to_root->fs->path); + /* more things that we can't do ATM */ if (from_root->is_txn_root) return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Copy from mutable tree not currently supported")); + if (! to_root->is_txn_root) + return svn_error_create + (SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Copy immutable tree not supported")); + /* Get the NODE for FROM_PATH in FROM_ROOT.*/ SVN_ERR(get_dag(&from_node, from_root, from_path, pool)); @@ -2248,7 +2595,7 @@ copy_helper(svn_fs_root_t *from_root, 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)); + open_path_last_optional, TRUE, pool)); /* Check to see if path (or any child thereof) is locked; if so, check that we can use the existing lock(s). */ @@ -2307,7 +2654,7 @@ copy_helper(svn_fs_root_t *from_root, from_canonpath, txn_id, pool)); - if (kind == svn_fs_path_change_replace) + if (kind != svn_fs_path_change_add) SVN_ERR(dag_node_cache_invalidate(to_root, parent_path_path(to_parent_path, pool), pool)); @@ -2321,8 +2668,8 @@ copy_helper(svn_fs_root_t *from_root, /* Make a record of this modification in the changes table. */ 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), + svn_fs_fs__dag_get_id(new_node), kind, FALSE, + FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node), from_root->rev, from_canonpath, pool)); } else @@ -2397,46 +2744,12 @@ fs_copied_from(svn_revnum_t *rev_p, 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, pool)); - SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev, node)); - SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path, node)); - } - *rev_p = copyfrom_rev; - *path_p = copyfrom_path; + /* There is no cached entry, look it up the old-fashioned + way. */ + SVN_ERR(get_dag(&node, root, path, pool)); + SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node)); + SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node)); return SVN_NO_ERROR; } @@ -2454,13 +2767,13 @@ fs_make_file(svn_fs_root_t *root, { parent_path_t *parent_path; dag_node_t *child; - const char *txn_id = root->txn; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(root); 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)); + TRUE, pool)); /* If there's already a file by that name, complain. This also catches the case of trying to make a file named `/'. */ @@ -2489,8 +2802,8 @@ fs_make_file(svn_fs_root_t *root, /* 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); + svn_fs_path_change_add, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, pool); } @@ -2597,8 +2910,6 @@ typedef struct txdelta_baton_t 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 @@ -2612,20 +2923,6 @@ typedef struct txdelta_baton_t } 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) @@ -2637,48 +2934,11 @@ window_consumer(svn_txdelta_window_t *window, void *baton) 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. */ + /* Is the window NULL? If so, we're done. The stream has already been + closed by the interpreter. */ 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)); - } + SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum, + tb->pool)); return SVN_NO_ERROR; } @@ -2690,11 +2950,11 @@ 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; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root); /* 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)); + SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool)); /* Check (non-recursively) to see if path is locked; if so, check that we can use it. */ @@ -2730,15 +2990,9 @@ apply_textdelta(void *baton, apr_pool_t *pool) 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, + tb->target_stream, NULL, tb->path, tb->pool, @@ -2748,8 +3002,8 @@ apply_textdelta(void *baton, apr_pool_t *pool) /* 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); + svn_fs_path_change_modify, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, pool); } @@ -2855,11 +3109,11 @@ 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; + const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root); /* 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)); + SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool)); /* Check (non-recursively) to see if path is locked; if so, check that we can use it. */ @@ -2883,8 +3137,8 @@ apply_text(void *baton, apr_pool_t *pool) /* 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); + svn_fs_path_change_modify, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, pool); } @@ -2923,6 +3177,7 @@ fs_contents_changed(svn_boolean_t *changed_p, const char *path1, svn_fs_root_t *root2, const char *path2, + svn_boolean_t strict, apr_pool_t *pool) { dag_node_t *node1, *node2; @@ -2951,7 +3206,7 @@ fs_contents_changed(svn_boolean_t *changed_p, 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); + node1, node2, strict, pool); } @@ -2993,14 +3248,11 @@ fs_paths_changed(apr_hash_t **changed_paths_p, apr_pool_t *pool) { if (root->is_txn_root) - return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn, - pool); + return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, + root_txn_id(root), 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); - } + return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev, + pool); } @@ -3040,7 +3292,8 @@ 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) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_node_kind_t kind; @@ -3049,15 +3302,13 @@ fs_node_history(svn_fs_history_t **history_p, 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)); + SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_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); + *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL, + SVN_INVALID_REVNUM, result_pool); return SVN_NO_ERROR; } @@ -3115,14 +3366,13 @@ static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p, 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)); + SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool)); /* Find the youngest copyroot in the path of this node-rev, which will indicate the target of the innermost copy affecting the @@ -3136,11 +3386,11 @@ static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p, 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(©_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(©_dst_parent_path, copy_dst_root, path, - open_path_node_only, NULL, pool)); + open_path_node_only | open_path_allow_null, FALSE, pool)); + if (copy_dst_parent_path == NULL) + return SVN_NO_ERROR; + 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))) @@ -3236,7 +3486,7 @@ fs_node_origin_rev(svn_revnum_t *revision, { svn_fs_t *fs = root->fs; const svn_fs_id_t *given_noderev_id, *cached_origin_id; - const char *node_id, *dash; + const svn_fs_fs__id_part_t *node_id; path = svn_fs__canonicalize_abspath(path, pool); @@ -3244,27 +3494,13 @@ fs_node_origin_rev(svn_revnum_t *revision, 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) + /* Is it a brand new uncommitted node or a new-style node ID? + * (committed old-style nodes will have a 0 revision value; + * rev 0, number 0 is rev 0 root node). Note that != 0 includes + * SVN_INVALID_REVNUM for uncommitted nodes. */ + if (node_id->revision != 0 || node_id->number == 0) { - *revision = 0; + *revision = node_id->revision; return SVN_NO_ERROR; } @@ -3344,7 +3580,7 @@ fs_node_origin_rev(svn_revnum_t *revision, /* Wow, I don't want to have to do all that again. Let's cache the result. */ - if (node_id[0] != '_') + if (node_id->revision != SVN_INVALID_REVNUM) SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id, svn_fs_fs__dag_get_id(node), pool)); @@ -3355,26 +3591,17 @@ fs_node_origin_rev(svn_revnum_t *revision, } -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) +history_prev(svn_fs_history_t **prev_history, + svn_fs_history_t *history, + svn_boolean_t cross_copies, + apr_pool_t *result_pool, + apr_pool_t *scratch_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; @@ -3393,21 +3620,21 @@ history_prev(void *baton, apr_pool_t *pool) if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint)) { reported = FALSE; - if (! args->cross_copies) + if (! 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)); + SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_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)); + SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_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)); + SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool)); /* The Subversion filesystem is written in such a way that a given line of history may have at most one interesting history point @@ -3422,10 +3649,9 @@ history_prev(void *baton, apr_pool_t *pool) { /* ... 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), + *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE, NULL, - SVN_INVALID_REVNUM, retpool); + SVN_INVALID_REVNUM, result_pool); return SVN_NO_ERROR; } else @@ -3441,16 +3667,16 @@ history_prev(void *baton, apr_pool_t *pool) /* Replace NODE and friends with the information from its predecessor. */ - SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool)); + SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool)); commit_path = svn_fs_fs__dag_get_created_path(node); - SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool)); + SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool)); } } /* Find the youngest copyroot in the path of this node, including itself. */ SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs, - parent_path, pool)); + parent_path, scratch_pool)); /* Initialize some state variables. */ src_path = NULL; @@ -3464,8 +3690,8 @@ history_prev(void *baton, apr_pool_t *pool) svn_fs_root_t *copyroot_root; SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, - pool)); - SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool)); + scratch_pool)); + SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool)); copy_dst = svn_fs_fs__dag_get_created_path(node); /* If our current path was the very destination of the copy, @@ -3487,7 +3713,7 @@ history_prev(void *baton, apr_pool_t *pool) SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node)); dst_rev = copyroot_rev; - src_path = svn_fspath__join(copy_src, remainder_path, pool); + src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool); } } @@ -3504,15 +3730,13 @@ history_prev(void *baton, apr_pool_t *pool) if ((dst_rev == revision) && reported) retry = TRUE; - *prev_history = assemble_history(fs, apr_pstrdup(retpool, path), - dst_rev, ! retry, - src_path, src_rev, retpool); + *prev_history = assemble_history(fs, path, dst_rev, ! retry, + src_path, src_rev, result_pool); } else { - *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path), - commit_rev, TRUE, NULL, - SVN_INVALID_REVNUM, retpool); + *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE, + NULL, SVN_INVALID_REVNUM, result_pool); } return SVN_NO_ERROR; @@ -3527,7 +3751,8 @@ 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) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_fs_history_t *prev_history = NULL; fs_history_data_t *fhd = history->fsap_data; @@ -3541,23 +3766,23 @@ fs_history_prev(svn_fs_history_t **prev_history_p, { if (! fhd->is_interesting) prev_history = assemble_history(fs, "/", fhd->revision, - 1, NULL, SVN_INVALID_REVNUM, pool); + 1, NULL, SVN_INVALID_REVNUM, + result_pool); else if (fhd->revision > 0) prev_history = assemble_history(fs, "/", fhd->revision - 1, - 1, NULL, SVN_INVALID_REVNUM, pool); + 1, NULL, SVN_INVALID_REVNUM, + result_pool); } else { - struct history_prev_args args; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); 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)); + svn_pool_clear(iterpool); + SVN_ERR(history_prev(&prev_history, prev_history, cross_copies, + result_pool, iterpool)); if (! prev_history) break; @@ -3565,6 +3790,8 @@ fs_history_prev(svn_fs_history_t **prev_history_p, if (fhd->is_interesting) break; } + + svn_pool_destroy(iterpool); } *prev_history_p = prev_history; @@ -3594,9 +3821,8 @@ static history_vtable_t history_vtable = { /* 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. */ + of the parameters provided. Note that PATH and PATH_HINT get + normalized and duplicated in POOL. */ static svn_fs_history_t * assemble_history(svn_fs_t *fs, const char *path, @@ -3611,7 +3837,8 @@ assemble_history(svn_fs_t *fs, fhd->path = svn_fs__canonicalize_abspath(path, pool); fhd->revision = revision; fhd->is_interesting = is_interesting; - fhd->path_hint = path_hint; + fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool) + : NULL; fhd->rev_hint = rev_hint; fhd->fs = fs; @@ -3643,18 +3870,14 @@ crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *entries; - apr_hash_index_t *hi; + apr_array_header_t *entries; + int i; 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_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool)); + for (i = 0; i < entries->nelts; ++i) { - svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi); + svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); const char *kid_path; dag_node_t *kid_dag; svn_boolean_t has_mergeinfo, go_down; @@ -3758,7 +3981,7 @@ get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo, path = svn_fs__canonicalize_abspath(path, scratch_pool); - SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool)); + SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool)); if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent) return SVN_NO_ERROR; @@ -4017,20 +4240,23 @@ static root_vtable_t root_vtable = { svn_fs_fs__check_path, fs_node_history, svn_fs_fs__node_id, + fs_node_relation, svn_fs_fs__node_created_rev, fs_node_origin_rev, fs_node_created_path, fs_delete_node, + fs_copy, + fs_revision_link, fs_copied_from, fs_closest_copy, fs_node_prop, fs_node_proplist, + fs_node_has_props, fs_change_node_prop, fs_props_changed, fs_dir_entries, + fs_dir_optimal_order, fs_make_dir, - fs_copy, - fs_revision_link, fs_file_length, fs_file_checksum, fs_file_contents, @@ -4068,15 +4294,10 @@ make_revision_root(svn_fs_t *fs, 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; + root->fsap_data = root_dir; return root; } @@ -4088,21 +4309,20 @@ make_revision_root(svn_fs_t *fs, static svn_error_t * make_txn_root(svn_fs_root_t **root_p, svn_fs_t *fs, - const char *txn, + const svn_fs_fs__id_part_t *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)); + frd->txn_id = *txn; root->is_txn_root = TRUE; - root->txn = apr_pstrdup(root->pool, txn); + root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool); 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. @@ -4114,14 +4334,14 @@ make_txn_root(svn_fs_root_t **root_p, APR_HASH_KEY_STRING, 32, 20, FALSE, apr_pstrcat(pool, txn, ":TXN", - (char *)NULL), + SVN_VA_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)); + SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool)); root->fsap_data = frd; @@ -4132,7 +4352,7 @@ make_txn_root(svn_fs_root_t **root_p, /* Verify. */ -static APR_INLINE const char * +static const char * stringify_node(dag_node_t *node, apr_pool_t *pool) { @@ -4142,10 +4362,12 @@ stringify_node(dag_node_t *node, /* 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. */ + accuracy for nodes belonging to older revisions. To detect cycles, + provide all parent dag_node_t * in PARENT_NODES. */ static svn_error_t * verify_node(dag_node_t *node, svn_revnum_t rev, + apr_array_header_t *parent_nodes, apr_pool_t *pool) { svn_boolean_t has_mergeinfo; @@ -4155,6 +4377,18 @@ verify_node(dag_node_t *node, int pred_count; svn_node_kind_t kind; apr_pool_t *iterpool = svn_pool_create(pool); + int i; + + /* Detect (non-)DAG cycles. */ + for (i = 0; i < parent_nodes->nelts; ++i) + { + dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *); + if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent), + svn_fs_fs__dag_get_id(node))) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Node is its own direct or indirect parent '%s'", + stringify_node(node, iterpool)); + } /* Fetch some data. */ SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node)); @@ -4205,32 +4439,40 @@ verify_node(dag_node_t *node, } if (kind == svn_node_dir) { - apr_hash_t *entries; - apr_hash_index_t *hi; + apr_array_header_t *entries; apr_int64_t children_mergeinfo = 0; + APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node; 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)) + for (i = 0; i < entries->nelts; ++i) { - svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi); + svn_fs_dirent_t *dirent + = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); 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)); + if (svn_fs_fs__id_rev(dirent->id) == rev) + { + SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, + iterpool)); + SVN_ERR(verify_node(child, rev, parent_nodes, iterpool)); + SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, + child)); + } + else + { + /* access mergeinfo counter with minimal overhead */ + node_revision_t *noderev; + SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id, + iterpool, iterpool)); + child_mergeinfo = noderev->mergeinfo_count; + } - SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child)); children_mergeinfo += child_mergeinfo; } @@ -4243,6 +4485,10 @@ verify_node(dag_node_t *node, stringify_node(node, iterpool), mergeinfo_count, has_mergeinfo, children_mergeinfo); + + /* If we don't make it here, there was an error / corruption. + * In that case, nobody will need PARENT_NODES anymore. */ + apr_array_pop(parent_nodes); } svn_pool_destroy(iterpool); @@ -4255,6 +4501,7 @@ svn_fs_fs__verify_root(svn_fs_root_t *root, { svn_fs_t *fs = root->fs; dag_node_t *root_dir; + apr_array_header_t *parent_nodes; /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev (and elsewhere). This code makes more thorough checks than the @@ -4270,16 +4517,16 @@ svn_fs_fs__verify_root(svn_fs_root_t *root, 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)); + 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; + root_dir = root->fsap_data; } /* Recursively verify ROOT_DIR. */ - SVN_ERR(verify_node(root_dir, root->rev, pool)); + parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *)); + SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool)); /* Verify explicitly the predecessor of the root. */ { diff --git a/contrib/subversion/subversion/libsvn_fs_fs/tree.h b/contrib/subversion/subversion/libsvn_fs_fs/tree.h index 34fa0a23b..7ddfcd9eb 100644 --- a/contrib/subversion/subversion/libsvn_fs_fs/tree.h +++ b/contrib/subversion/subversion/libsvn_fs_fs/tree.h @@ -48,8 +48,7 @@ svn_error_t *svn_fs_fs__deltify(svn_fs_t *fs, svn_revnum_t rev, /* Commit the transaction TXN as a new revision. Return the new revision in *NEW_REV. If the transaction conflicts with other changes return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a string - that details the cause of the conflict. Perform temporary - allocations in POOL. */ + that details the cause of the conflict. */ 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); @@ -91,6 +90,20 @@ svn_error_t * svn_fs_fs__verify_root(svn_fs_root_t *root, apr_pool_t *pool); +svn_error_t * +svn_fs_fs__info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +svn_error_t * +svn_fs_fs__info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/libsvn_fs_fs/util.c b/contrib/subversion/subversion/libsvn_fs_fs/util.c new file mode 100644 index 000000000..faa1e3d31 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/util.c @@ -0,0 +1,694 @@ +/* util.c --- utility functions for FSFS repo access + * + * ==================================================================== + * 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 + +#include "svn_ctype.h" +#include "svn_dirent_uri.h" +#include "private/svn_string_private.h" + +#include "fs_fs.h" +#include "pack.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +svn_boolean_t +svn_fs_fs__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); +} + +svn_boolean_t +svn_fs_fs__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); +} + +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return (revision < ffd->min_unpacked_rev) + ? (revision - (revision % ffd->max_files_per_dir)) + : revision; +} + +const char * +svn_fs_fs__path_txn_current(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool); +} + +const char * +svn_fs_fs__path_txn_current_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool); +} + +const char * +svn_fs_fs__path_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool); +} + +const char * +svn_fs_fs__path_pack_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool); +} + +const char * +svn_fs_fs__path_revprop_generation(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool); +} + +const char * +svn_fs_fs__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(svn_fs_fs__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, SVN_VA_NULL); +} + +const char * +svn_fs_fs__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), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(! svn_fs_fs__is_packed_rev(fs, rev)); + + if (ffd->max_files_per_dir) + { + return svn_dirent_join(svn_fs_fs__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), SVN_VA_NULL); +} + +/* Set *PATH to the path of REV in FS with PACKED selecting whether the + (potential) pack file or single revision file name is returned. + Allocate *PATH in POOL. +*/ +static const char * +path_rev_absolute_internal(svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t packed, + apr_pool_t *pool) +{ + return packed + ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool) + : svn_fs_fs__path_rev(fs, rev, pool); +} + +const char * +svn_fs_fs__path_rev_absolute(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT + && svn_fs_fs__is_packed_rev(fs, rev); + + return path_rev_absolute_internal(fs, rev, is_packed, pool); +} + +const char * +svn_fs_fs__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), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__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), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__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(svn_fs_fs__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), SVN_VA_NULL); +} + +/* Return TO_ADD appended to the C string representation of TXN_ID. + * Allocate the result in POOL. + */ +static const char * +combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id, + const char *to_add, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool), + to_add, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); +} + +const char * +svn_fs_fs__path_txn_dir(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL); + return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_TXN, pool), + pool); +} + +const char* +svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_INDEX PATH_EXT_L2P_INDEX, pool); +} + +const char* +svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_INDEX PATH_EXT_P2L_INDEX, pool); +} + +const char * +svn_fs_fs__path_txn_item_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_TXN_ITEM_INDEX, pool); +} + +const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool); +} + +const char * +svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV, pool), + pool); + else + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_REV, pool); +} + + +const char * +svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs, + const svn_fs_fs__id_part_t *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(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK, + pool), + pool); + else + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_REV_LOCK, pool); +} + +const char * +svn_fs_fs__path_txn_node_rev(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data; + *strrchr(filename, '.') = '\0'; + + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id), + pool), + apr_psprintf(pool, PATH_PREFIX_NODE "%s", + filename), + pool); +} + +const char * +svn_fs_fs__path_txn_node_props(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), + PATH_EXT_PROPS, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_txn_node_children(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), + PATH_EXT_CHILDREN, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_node_origin(svn_fs_t *fs, + const svn_fs_fs__id_part_t *node_id, + apr_pool_t *pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__ui64tobase36(buffer, node_id->number); + + if (len > 1) + buffer[len - 1] = '\0'; + + return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR, + buffer, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool); +} + +svn_error_t * +svn_fs_fs__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; +} + +svn_error_t * +svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, + svn_fs_t *fs, + apr_pool_t *pool) +{ + char buf[80]; + apr_file_t *file; + apr_size_t len; + + SVN_ERR(svn_io_file_open(&file, + svn_fs_fs__path_min_unpacked_rev(fs, pool), + 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)); + + SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__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 svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool); +} + +svn_error_t * +svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs, + svn_revnum_t revnum, + apr_pool_t *scratch_pool) +{ + const char *final_path; + char buf[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__i64toa(buf, revnum); + buf[len] = '\n'; + + final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool); + + SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1, + final_path /* copy_perms */, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_current(svn_revnum_t *rev, + apr_uint64_t *next_node_id, + apr_uint64_t *next_copy_id, + svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_stringbuf_t *content; + + SVN_ERR(svn_fs_fs__read_content(&content, + svn_fs_fs__path_current(fs, pool), + pool)); + + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + /* When format 1 and 2 filesystems are upgraded, the 'current' file is + left intact. As a consequence, there is a window when a filesystem + has a new format, but this file still contains the IDs left from an + old format, i.e. looks like "359 j5 v\n". Do not be too strict here + and only expect a parseable revision number. */ + SVN_ERR(svn_revnum_parse(rev, content->data, NULL)); + + *next_node_id = 0; + *next_copy_id = 0; + } + else + { + const char *str; + + SVN_ERR(svn_revnum_parse(rev, content->data, &str)); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + + *next_node_id = svn__base36toui64(&str, str + 1); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + + *next_copy_id = svn__base36toui64(&str, str + 1); + if (*str != '\n') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__write_current(svn_fs_t *fs, + svn_revnum_t rev, + apr_uint64_t next_node_id, + apr_uint64_t next_copy_id, + apr_pool_t *pool) +{ + char *buf; + const char *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 + { + char node_id_str[SVN_INT64_BUFFER_SIZE]; + char copy_id_str[SVN_INT64_BUFFER_SIZE]; + svn__ui64tobase36(node_id_str, next_node_id); + svn__ui64tobase36(copy_id_str, next_copy_id); + + buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str); + } + + name = svn_fs_fs__path_current(fs, pool); + SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf), + name /* copy_perms_path */, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__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); +} + +svn_error_t * +svn_fs_fs__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; +} + +svn_error_t * +svn_fs_fs__read_content(svn_stringbuf_t **content, + const char *fname, + apr_pool_t *pool) +{ + int i; + *content = NULL; + + for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i) + SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL, + fname, i + 1 < SVN_FS_FS__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; +} + +svn_error_t * +svn_fs_fs__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; +} + +svn_error_t * +svn_fs_fs__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_boolean_t +svn_fs_fs__use_log_addressing(svn_fs_t *fs) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return ffd->use_log_addressing; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/util.h b/contrib/subversion/subversion/libsvn_fs_fs/util.h new file mode 100644 index 000000000..328dfbcff --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/util.h @@ -0,0 +1,408 @@ +/* util.h --- utility functions for FSFS repo access + * + * ==================================================================== + * 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_FS__UTIL_H +#define SVN_LIBSVN_FS__UTIL_H + +#include "svn_fs.h" +#include "id.h" + +/* 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 SVN_FS_FS__RECOVERABLE_RETRY_COUNT + * iterations (though, realistically, the second try will succeed). + */ + +#define SVN_FS_FS__RECOVERABLE_RETRY_COUNT 10 + +/* Return TRUE is REV is packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_fs__is_packed_rev(svn_fs_t *fs, + svn_revnum_t rev); + +/* Return TRUE is REV's props have been packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_fs__is_packed_revprop(svn_fs_t *fs, + svn_revnum_t rev); + +/* Return the first revision in the pack / rev file containing REVISION in + * filesystem FS. For non-packed revs, this will simply be REVISION. */ +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision); + +/* Return the full path of the rev shard directory that will contain + * revision REV in FS. Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_rev_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the full path of the non-packed rev file containing revision REV + * in FS. Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_rev(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the path of the pack-related file that for revision REV in FS. + * KIND specifies the file name base, e.g. "manifest" or "pack". + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_rev_packed(svn_fs_t *fs, + svn_revnum_t rev, + const char *kind, + apr_pool_t *pool); + +/* Return the full path of the "txn-current" file in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_current(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the full path of the "txn-current-lock" file in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_current_lock(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the full path of the global write lock file in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_lock(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the full path of the pack operation lock file in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_pack_lock(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the full path of the revprop generation file in FS. + * Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_revprop_generation(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the full path of the revision properties pack shard directory + * that will contain the packed properties of revision REV in FS. + * Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Set *PATH to the path of REV in FS, whether in a pack file or not. + Allocate *PATH in POOL. + + Note: If the caller does not have the write lock on FS, then the path is + not guaranteed to be correct or to remain correct after the function + returns, because the revision might become packed before or after this + call. If a file exists at that path, then it is correct; if not, then + the caller should call update_min_unpacked_rev() and re-try once. */ +const char * +svn_fs_fs__path_rev_absolute(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the full path of the revision properties shard directory that + * will contain the properties of revision REV in FS. + * Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_revprops_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the full path of the non-packed revision properties file that + * contains the props for revision REV in FS. Allocate the result in POOL. + */ +const char * +svn_fs_fs__path_revprops(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Return the path of the file storing the oldest non-packed revision in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the path of the 'transactions' directory in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the path of the directory containing the transaction TXN_ID in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_dir(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the 'txn-protorevs' directory in FS, even if that + * folder may not exist in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool); + +/* Return the path of the proto-revision file for transaction TXN_ID in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the proto-revision lock file for transaction TXN_ID + * in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the file containing the in-transaction node revision + * identified by ID in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_node_rev(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool); + +/* Return the path of the file containing the in-transaction properties of + * the node identified by ID in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_node_props(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool); + +/* Return the path of the file containing the directory entries of the + * in-transaction directory node identified by ID in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_node_children(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool); + +/* Return the path of the file containing the log-to-phys index for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in POOL. + */ +const char* +svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the file containing the phys-to-log index for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in POOL. + */ +const char* +svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the file containing item_index counter for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_item_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool); + +/* Return the path of the file containing the node origins cachs for + * the given NODE_ID in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_node_origin(svn_fs_t *fs, + const svn_fs_fs__id_part_t *node_id, + apr_pool_t *pool); + +/* Set *MIN_UNPACKED_REV to the integer value read from the file returned + * by #svn_fs_fs__path_min_unpacked_rev() for FS. + * Use POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, + svn_fs_t *fs, + apr_pool_t *pool); + +/* 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. */ +svn_error_t * +svn_fs_fs__check_file_buffer_numeric(const char *buf, + apr_off_t offset, + const char *path, + const char *title, + apr_pool_t *pool); + +/* Re-read the MIN_UNPACKED_REV member of FS from disk. + * Use POOL for temporary allocations. + */ +svn_error_t * +svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *pool); + +/* Atomically update the 'min-unpacked-rev' file in FS to hold the specifed + * REVNUM. Perform temporary allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs, + svn_revnum_t revnum, + apr_pool_t *scratch_pool); + +/* Set *REV, *NEXT_NODE_ID and *NEXT_COPY_ID to the values read from the + * 'current' file. For new FS formats, which only store the youngest + * revision, set the *NEXT_NODE_ID and *NEXT_COPY_ID to 0. Perform + * temporary allocations in POOL. + */ +svn_error_t * +svn_fs_fs__read_current(svn_revnum_t *rev, + apr_uint64_t *next_node_id, + apr_uint64_t *next_copy_id, + svn_fs_t *fs, + apr_pool_t *pool); + +/* 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 0 if the FS format does not use them.) + Perform temporary allocations in POOL. */ +svn_error_t * +svn_fs_fs__write_current(svn_fs_t *fs, + svn_revnum_t rev, + apr_uint64_t next_node_id, + apr_uint64_t next_copy_id, + apr_pool_t *pool); + +/* 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. + */ +svn_error_t * +svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content, + svn_boolean_t *missing, + const char *path, + svn_boolean_t last_attempt, + apr_pool_t *pool); + +/* Fetch the current offset of FILE into *OFFSET_P. */ +svn_error_t * +svn_fs_fs__get_file_offset(apr_off_t *offset_p, + apr_file_t *file, + apr_pool_t *pool); + +/* Read the file FNAME and store the contents in *BUF. + Allocations are performed in POOL. */ +svn_error_t * +svn_fs_fs__read_content(svn_stringbuf_t **content, + const char *fname, + apr_pool_t *pool); + +/* 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. + */ +svn_error_t * +svn_fs_fs__read_number_from_stream(apr_int64_t *result, + svn_boolean_t *hit_eof, + svn_stream_t *stream, + apr_pool_t *scratch_pool); + +/* 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. */ +svn_error_t * +svn_fs_fs__move_into_place(const char *old_filename, + const char *new_filename, + const char *perms_reference, + apr_pool_t *pool); + +/* Return TRUE, iff FS uses logical addressing. */ +svn_boolean_t +svn_fs_fs__use_log_addressing(svn_fs_t *fs); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_fs/verify.c b/contrib/subversion/subversion/libsvn_fs_fs/verify.c new file mode 100644 index 000000000..0fa314bfd --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/verify.c @@ -0,0 +1,883 @@ +/* verify.c --- verification of FSFS filesystems + * + * ==================================================================== + * 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_sorts.h" +#include "svn_checksum.h" +#include "svn_time.h" +#include "private/svn_subr_private.h" + +#include "verify.h" +#include "fs_fs.h" + +#include "cached_data.h" +#include "rep-cache.h" +#include "util.h" +#include "index.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + + +/** 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; + + /* cached hint for successive calls to svn_fs_fs__check_rep() */ + void *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) +{ + verify_walker_baton_t *walker_baton = baton; + void *previous_hint; + + /* 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->hint = NULL; + } + + /* access the repo data */ + previous_hint = walker_baton->hint; + SVN_ERR(svn_fs_fs__check_rep(rep, fs, &walker_baton->hint, + walker_baton->pool)); + + /* update resource usage counters */ + walker_baton->iteration_count++; + if (previous_hint != walker_baton->hint) + walker_baton->file_count++; + + return SVN_NO_ERROR; +} + +/* Verify the rep cache DB's consistency with our rev / pack data. + * The function signature is similar to svn_fs_fs__verify. + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_rep_cache(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) +{ + svn_boolean_t exists; + + /* 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->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; +} + +/* Verify that the MD5 checksum of the data between offsets START and END + * in FILE matches the EXPECTED checksum. If there is a mismatch use the + * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC + * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */ +static svn_error_t * +verify_index_checksum(apr_file_t *file, + const char *name, + apr_off_t start, + apr_off_t end, + svn_checksum_t *expected, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[SVN__STREAM_CHUNK_SIZE]; + apr_off_t size = end - start; + svn_checksum_t *actual; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + + /* Calculate the index checksum. */ + SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool)); + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL, + scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + SVN_ERR(svn_checksum_final(&actual, context, scratch_pool)); + + /* Verify that it matches the expected checksum. */ + if (!svn_checksum_match(expected, actual)) + { + const char *file_name; + + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool, + _("%s checksum mismatch in file %s"), + name, file_name)); + } + + return SVN_NO_ERROR; +} + +/* Verify the MD5 checksums of the index data in the rev / pack file + * containing revision START in FS. If given, invoke CANCEL_FUNC with + * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +verify_index_checksums(svn_fs_t *fs, + svn_revnum_t start, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_fs__revision_file_t *rev_file; + + /* Open the rev / pack file and read the footer */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, + scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + + /* Verify the index contents against the checksum from the footer. */ + SVN_ERR(verify_index_checksum(rev_file->file, "L2P index", + rev_file->l2p_offset, rev_file->p2l_offset, + rev_file->l2p_checksum, + cancel_func, cancel_baton, scratch_pool)); + SVN_ERR(verify_index_checksum(rev_file->file, "P2L index", + rev_file->p2l_offset, rev_file->footer_offset, + rev_file->p2l_checksum, + cancel_func, cancel_baton, scratch_pool)); + + /* Done. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Verify that for all log-to-phys index entries for revisions START to + * START + COUNT-1 in FS there is a consistent entry in the phys-to-log + * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular + * intervals. Use POOL for allocations. + */ +static svn_error_t * +compare_l2p_to_p2l_index(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + svn_revnum_t i; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_array_header_t *max_ids; + + /* common file access structure */ + svn_fs_fs__revision_file_t *rev_file; + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool, + iterpool)); + + /* determine the range of items to check for each revision */ + SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool, + iterpool)); + + /* check all items in all revisions if the given range */ + for (i = 0; i < max_ids->nelts; ++i) + { + apr_uint64_t k; + apr_uint64_t max_id = APR_ARRAY_IDX(max_ids, i, apr_uint64_t); + svn_revnum_t revision = start + i; + + for (k = 0; k < max_id; ++k) + { + apr_off_t offset; + svn_fs_fs__p2l_entry_t *p2l_entry; + svn_pool_clear(iterpool); + + /* get L2P entry. Ignore unused entries. */ + SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, revision, + NULL, k, iterpool)); + if (offset == -1) + continue; + + /* find the corresponding P2L entry */ + SVN_ERR(svn_fs_fs__p2l_entry_lookup(&p2l_entry, fs, rev_file, + revision, offset, iterpool, + iterpool)); + + if (p2l_entry == NULL) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index entry not found for " + "PHYS %s returned by " + "l2p index for LOG r%ld:i%ld"), + apr_off_t_toa(pool, offset), + revision, (long)k); + + if ( p2l_entry->item.number != k + || p2l_entry->item.revision != revision) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index info LOG r%ld:i%ld" + " does not match " + "l2p index for LOG r%ld:i%ld"), + p2l_entry->item.revision, + (long)p2l_entry->item.number, + revision, (long)k); + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Verify that for all phys-to-log index entries for revisions START to + * START + COUNT-1 in FS there is a consistent entry in the log-to-phys + * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular + * intervals. Use POOL for allocations. + * + * Please note that we can only check on pack / rev file granularity and + * must only be called for a single rev / pack file. + */ +static svn_error_t * +compare_p2l_to_l2p_index(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_off_t max_offset; + apr_off_t offset = 0; + + /* common file access structure */ + svn_fs_fs__revision_file_t *rev_file; + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool, + iterpool)); + + /* get the size of the rev / pack file as covered by the P2L index */ + SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs, rev_file, start, + pool)); + + /* for all offsets in the file, get the P2L index entries and check + them against the L2P index */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + svn_fs_fs__p2l_entry_t *last_entry; + int i; + + svn_pool_clear(iterpool); + + /* get all entries for the current block */ + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + if (entries->nelts == 0) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, + NULL, + _("p2l does not cover offset %s" + " for revision %ld"), + apr_off_t_toa(pool, offset), start); + + /* process all entries (and later continue with the next block) */ + last_entry + = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_fs__p2l_entry_t); + offset = last_entry->offset + last_entry->size; + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + + /* check all sub-items for consist entries in the L2P index */ + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + { + /* There is no L2P entry for unused rev file sections. + * And its P2L index data is hardly ever used. But we + * should still check whether someone tempered with it. */ + if ( entry->item.revision != SVN_INVALID_REVNUM + && ( entry->item.revision < start + || entry->item.revision >= start + count)) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("Empty P2L entry for PHYS %s " + "refers to revision %ld outside " + "the rev / pack file (%ld-%ld)"), + apr_off_t_toa(pool, entry->offset), + entry->item.revision, + start, start + count - 1); + } + else + { + apr_off_t l2p_offset; + SVN_ERR(svn_fs_fs__item_offset(&l2p_offset, fs, rev_file, + entry->item.revision, NULL, + entry->item.number, iterpool)); + + if (l2p_offset != entry->offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("l2p index entry PHYS %s" + "does not match p2l index value " + "LOG r%ld:i%ld for PHYS %s"), + apr_off_t_toa(pool, l2p_offset), + entry->item.revision, + (long)entry->item.number, + apr_off_t_toa(pool, entry->offset)); + } + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Items smaller than this can be read at once into a buffer and directly + * be checksummed. Larger items require stream processing. + * Must be a multiple of 8. */ +#define STREAM_THRESHOLD 4096 + +/* Verify that the next SIZE bytes read from FILE are NUL. + * SIZE must not exceed STREAM_THRESHOLD. Use POOL for allocations. + */ +static svn_error_t * +expect_buffer_nul(apr_file_t *file, + apr_off_t size, + apr_pool_t *pool) +{ + union + { + unsigned char buffer[STREAM_THRESHOLD]; + apr_uint64_t chunks[STREAM_THRESHOLD / sizeof(apr_uint64_t)]; + } data; + + apr_size_t i; + SVN_ERR_ASSERT(size <= STREAM_THRESHOLD); + + /* read the whole data block; error out on failure */ + data.chunks[(size - 1)/ sizeof(apr_uint64_t)] = 0; + SVN_ERR(svn_io_file_read_full2(file, data.buffer, size, NULL, NULL, pool)); + + /* chunky check */ + for (i = 0; i < size / sizeof(apr_uint64_t); ++i) + if (data.chunks[i] != 0) + break; + + /* byte-wise check upon mismatch or at the end of the block */ + for (i *= sizeof(apr_uint64_t); i < size; ++i) + if (data.buffer[i] != 0) + { + const char *file_name; + apr_off_t offset; + + SVN_ERR(svn_io_file_name_get(&file_name, file, pool)); + SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, pool)); + offset -= size - i; + + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Empty section in file %s contains " + "non-NUL data at offset %s"), + file_name, apr_off_t_toa(pool, offset)); + } + + return SVN_NO_ERROR; +} + +/* Verify that the next SIZE bytes read from FILE are NUL. + * Use POOL for allocations. + */ +static svn_error_t * +read_all_nul(apr_file_t *file, + apr_off_t size, + apr_pool_t *pool) +{ + for (; size >= STREAM_THRESHOLD; size -= STREAM_THRESHOLD) + SVN_ERR(expect_buffer_nul(file, STREAM_THRESHOLD, pool)); + + if (size) + SVN_ERR(expect_buffer_nul(file, size, pool)); + + return SVN_NO_ERROR; +} + +/* Compare the ACTUAL checksum with the one expected by ENTRY. + * Return an error in case of mismatch. Use the name of FILE + * in error message. Allocate data in POOL. + */ +static svn_error_t * +expected_checksum(apr_file_t *file, + svn_fs_fs__p2l_entry_t *entry, + apr_uint32_t actual, + apr_pool_t *pool) +{ + if (actual != entry->fnv1_checksum) + { + const char *file_name; + + SVN_ERR(svn_io_file_name_get(&file_name, file, pool)); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Checksum mismatch in item at offset %s of " + "length %s bytes in file %s"), + apr_off_t_toa(pool, entry->offset), + apr_off_t_toa(pool, entry->size), file_name); + } + + return SVN_NO_ERROR; +} + +/* Verify that the FNV checksum over the next ENTRY->SIZE bytes read + * from FILE will match ENTRY's expected checksum. SIZE must not + * exceed STREAM_THRESHOLD. Use POOL for allocations. + */ +static svn_error_t * +expected_buffered_checksum(apr_file_t *file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + unsigned char buffer[STREAM_THRESHOLD]; + SVN_ERR_ASSERT(entry->size <= STREAM_THRESHOLD); + + SVN_ERR(svn_io_file_read_full2(file, buffer, (apr_size_t)entry->size, + NULL, NULL, pool)); + SVN_ERR(expected_checksum(file, entry, + svn__fnv1a_32x4(buffer, (apr_size_t)entry->size), + pool)); + + return SVN_NO_ERROR; +} + +/* Verify that the FNV checksum over the next ENTRY->SIZE bytes read from + * FILE will match ENTRY's expected checksum. Use POOL for allocations. + */ +static svn_error_t * +expected_streamed_checksum(apr_file_t *file, + svn_fs_fs__p2l_entry_t *entry, + apr_pool_t *pool) +{ + unsigned char buffer[STREAM_THRESHOLD]; + svn_checksum_t *checksum; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, pool); + apr_off_t size = entry->size; + + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL, + pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + } + + SVN_ERR(svn_checksum_final(&checksum, context, pool)); + SVN_ERR(expected_checksum(file, entry, + ntohl(*(const apr_uint32_t *)checksum->digest), + pool)); + + return SVN_NO_ERROR; +} + +/* Verify that for all phys-to-log index entries for revisions START to + * START + COUNT-1 in FS match the actual pack / rev file contents. + * If given, invoke CANCEL_FUNC with CANCEL_BATON at regular intervals. + * Use POOL for allocations. + * + * Please note that we can only check on pack / rev file granularity and + * must only be called for a single rev / pack file. + */ +static svn_error_t * +compare_p2l_to_rev(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_off_t max_offset; + apr_off_t offset = 0; + svn_fs_fs__revision_file_t *rev_file; + + /* open the pack / rev file that is covered by the p2l index */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool, + iterpool)); + + /* check file size vs. range covered by index */ + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs, rev_file, start, + pool)); + + if (rev_file->l2p_offset != max_offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, + _("File size of %s for revision r%ld does " + "not match p2l index size of %s"), + apr_off_t_toa(pool, rev_file->l2p_offset), start, + apr_off_t_toa(pool, max_offset)); + + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, 0, + pool)); + + /* for all offsets in the file, get the P2L index entries and check + them against the L2P index */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + int i; + + svn_pool_clear(iterpool); + + /* get all entries for the current block */ + SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + + /* The above might have moved the file pointer. + * Ensure we actually start reading at OFFSET. */ + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, + NULL, offset, iterpool)); + + /* process all entries (and later continue with the next block) */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_fs__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); + + /* skip bits we previously checked */ + if (i == 0 && entry->offset < offset) + continue; + + /* skip zero-sized entries */ + if (entry->size == 0) + continue; + + /* p2l index must cover all rev / pack file offsets exactly once */ + if (entry->offset != offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index entry for revision r%ld" + " is non-contiguous between offsets " + " %s and %s"), + start, + apr_off_t_toa(pool, offset), + apr_off_t_toa(pool, entry->offset)); + + /* empty sections must contain NUL bytes only */ + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + { + /* skip filler entry at the end of the p2l index */ + if (entry->offset != max_offset) + SVN_ERR(read_all_nul(rev_file->file, entry->size, pool)); + } + else + { + if (entry->size < STREAM_THRESHOLD) + SVN_ERR(expected_buffered_checksum(rev_file->file, entry, + pool)); + else + SVN_ERR(expected_streamed_checksum(rev_file->file, entry, + pool)); + } + + /* advance offset */ + offset += entry->size; + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Verify that the revprops of the revisions START to END in FS can be + * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals. + * + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_revprops(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + svn_revnum_t revision; + apr_pool_t *iterpool = svn_pool_create(pool); + + for (revision = start; revision < end; ++revision) + { + svn_string_t *date; + apr_time_t timetemp; + + svn_pool_clear(iterpool); + + /* Access the svn:date revprop. + * This implies parsing all revprops for that revision. */ + SVN_ERR(svn_fs_fs__revision_prop(&date, fs, revision, + SVN_PROP_REVISION_DATE, iterpool)); + + /* The time stamp is the only revprop that, if given, needs to + * have a valid content. */ + if (date) + SVN_ERR(svn_time_from_cstring(&timetemp, date->data, iterpool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +static svn_revnum_t +pack_size(svn_fs_t *fs, svn_revnum_t rev) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1; +} + +/* Verify that on-disk representation has not been tempered with (in a way + * that leaves the repository in a corrupted state). This compares log-to- + * phys with phys-to-log indexes, verifies the low-level checksums and + * checks that all revprops are available. The function signature is + * similar to svn_fs_fs__verify. + * + * The values of START and END have already been auto-selected and + * verified. You may call this for format7 or higher repos. + */ +static svn_error_t * +verify_f7_metadata_consistency(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_revnum_t revision, next_revision; + apr_pool_t *iterpool = svn_pool_create(pool); + + for (revision = start; revision <= end; revision = next_revision) + { + svn_error_t *err = SVN_NO_ERROR; + + svn_revnum_t count = pack_size(fs, revision); + svn_revnum_t pack_start = svn_fs_fs__packed_base_rev(fs, revision); + svn_revnum_t pack_end = pack_start + count; + + svn_pool_clear(iterpool); + + if (notify_func && (pack_start % ffd->max_files_per_dir == 0)) + notify_func(pack_start, notify_baton, iterpool); + + /* Check for external corruption to the indexes. */ + err = verify_index_checksums(fs, pack_start, cancel_func, + cancel_baton, iterpool); + + /* two-way index check */ + if (!err) + err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + if (!err) + err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + + /* verify in-index checksums and types vs. actual rev / pack files */ + if (!err) + err = compare_p2l_to_rev(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + + /* ensure that revprops are available and accessible */ + if (!err) + err = verify_revprops(fs, pack_start, pack_end, + cancel_func, cancel_baton, iterpool); + + /* concurrent packing is one of the reasons why verification may fail. + Make sure, we operate on up-to-date information. */ + if (err) + { + svn_error_t *err2 + = svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, + fs, pool); + + /* Be careful to not leak ERR. */ + if (err2) + return svn_error_trace(svn_error_compose_create(err, err2)); + } + + /* retry the whole shard if it got packed in the meantime */ + if (err && count != pack_size(fs, revision)) + { + svn_error_clear(err); + + /* We could simply assign revision here but the code below is + more intuitive to maintainers. */ + next_revision = svn_fs_fs__packed_base_rev(fs, revision); + } + else + { + SVN_ERR(err); + next_revision = pack_end; + } + } + + svn_pool_destroy(iterpool); + + 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_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */ + + /* Input validation. */ + if (! SVN_IS_VALID_REVNUM(start)) + start = 0; + if (! SVN_IS_VALID_REVNUM(end)) + end = youngest; + SVN_ERR(svn_fs_fs__ensure_revision_exists(start, fs, pool)); + SVN_ERR(svn_fs_fs__ensure_revision_exists(end, fs, pool)); + + /* log/phys index consistency. We need to check them first to make + sure we can access the rev / pack files in format7. */ + if (svn_fs_fs__use_log_addressing(fs)) + SVN_ERR(verify_f7_metadata_consistency(fs, start, end, + notify_func, notify_baton, + cancel_func, cancel_baton, pool)); + + /* rep cache consistency */ + if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT) + SVN_ERR(verify_rep_cache(fs, start, end, notify_func, notify_baton, + cancel_func, cancel_baton, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_fs/verify.h b/contrib/subversion/subversion/libsvn_fs_fs/verify.h new file mode 100644 index 000000000..650a35b57 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_fs/verify.h @@ -0,0 +1,42 @@ +/* verify.h : verification interface of the native filesystem layer + * + * ==================================================================== + * 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_FS__VERIFY_H +#define SVN_LIBSVN_FS__VERIFY_H + +#include "fs.h" + +/* Verify metadata in fsfs filesystem FS. Limit the checks to revisions + * START to END where possible. Indicate progress via the optional + * NOTIFY_FUNC callback using NOTIFY_BATON. The optional CANCEL_FUNC + * will periodically be called with CANCEL_BATON to allow for preemption. + * Use POOL for temporary allocations. */ +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); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_util/fs-util.c b/contrib/subversion/subversion/libsvn_fs_util/fs-util.c index da57bc9a7..1e7ed4fde 100644 --- a/contrib/subversion/subversion/libsvn_fs_util/fs-util.c +++ b/contrib/subversion/subversion/libsvn_fs_util/fs-util.c @@ -26,16 +26,26 @@ #include #include +#include "svn_private_config.h" #include "svn_hash.h" #include "svn_fs.h" #include "svn_dirent_uri.h" #include "svn_path.h" -#include "svn_private_config.h" +#include "svn_version.h" #include "private/svn_fs_util.h" #include "private/svn_fspath.h" +#include "private/svn_subr_private.h" #include "../libsvn_fs/fs-loader.h" + +const svn_version_t * +svn_fs_util__version(void) +{ + SVN_VERSION_BODY; +} + + /* Return TRUE, if PATH of PATH_LEN > 0 chars starts with a '/' and does * not end with a '/' and does not contain duplicate '/'. */ @@ -196,6 +206,8 @@ svn_fs__path_change_create_internal(const svn_fs_id_t *node_rev_id, change = apr_pcalloc(pool, sizeof(*change)); change->node_rev_id = node_rev_id; change->change_kind = change_kind; + change->mergeinfo_mod = svn_tristate_unknown; + change->copyfrom_rev = SVN_INVALID_REVNUM; return change; } @@ -211,8 +223,8 @@ svn_fs__append_to_merged_froms(svn_mergeinfo_t *output, *output = apr_hash_make(pool); for (hi = apr_hash_first(pool, input); 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); + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_hash_sets(*output, svn_fspath__join(path, rel_path, pool), @@ -221,3 +233,102 @@ svn_fs__append_to_merged_froms(svn_mergeinfo_t *output, return SVN_NO_ERROR; } + +/* Set the version info in *VERSION to COMPAT_MAJOR and COMPAT_MINOR, if + the current value refers to a newer version than that. + */ +static void +add_compatility(svn_version_t *version, + int compat_major, + int compat_minor) +{ + if ( version->major > compat_major + || (version->major == compat_major && version->minor > compat_minor)) + { + version->major = compat_major; + version->minor = compat_minor; + } +} + +svn_error_t * +svn_fs__compatible_version(svn_version_t **compatible_version, + apr_hash_t *config, + apr_pool_t *pool) +{ + svn_version_t *version; + const char *compatible; + + /* set compatible version according to generic option. + Make sure, we are always compatible to the current SVN version + (or older). */ + compatible = svn_hash_gets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION); + if (compatible) + { + SVN_ERR(svn_version__parse_version_string(&version, + compatible, pool)); + add_compatility(version, + svn_subr_version()->major, + svn_subr_version()->minor); + } + else + { + version = apr_pmemdup(pool, svn_subr_version(), sizeof(*version)); + } + + /* specific options take precedence. + Let the lowest version compatibility requirement win */ + if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE)) + add_compatility(version, 1, 3); + else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE)) + add_compatility(version, 1, 4); + else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE)) + add_compatility(version, 1, 5); + else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE)) + add_compatility(version, 1, 7); + + /* we ignored the patch level and tag so far. + * Give them a defined value. */ + version->patch = 0; + version->tag = ""; + + /* done here */ + *compatible_version = version; + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_fs__prop_lists_equal(apr_hash_t *a, + apr_hash_t *b, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + + /* Quick checks and special cases. */ + if (a == b) + return TRUE; + + if (a == NULL) + return apr_hash_count(b) == 0; + if (b == NULL) + return apr_hash_count(a) == 0; + + if (apr_hash_count(a) != apr_hash_count(b)) + return FALSE; + + /* Compare prop by prop. */ + for (hi = apr_hash_first(pool, a); hi; hi = apr_hash_next(hi)) + { + const char *key; + apr_ssize_t klen; + svn_string_t *val_a, *val_b; + + apr_hash_this(hi, (const void **)&key, &klen, (void **)&val_a); + val_b = apr_hash_get(b, key, klen); + + if (!val_b || !svn_string_compare(val_a, val_b)) + return FALSE; + } + + /* No difference found. */ + return TRUE; +} diff --git a/contrib/subversion/subversion/libsvn_fs_util/libsvn_fs_util.pc.in b/contrib/subversion/subversion/libsvn_fs_util/libsvn_fs_util.pc.in new file mode 100644 index 000000000..05a9b4f34 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_util/libsvn_fs_util.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs_util +Description: Subversion Filesystem Utility Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_subr +Libs: -L${libdir} -lsvn_fs_util +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_fs_x/TODO b/contrib/subversion/subversion/libsvn_fs_x/TODO new file mode 100644 index 000000000..4daf45b5d --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/TODO @@ -0,0 +1,270 @@ + +TODO (see also DONE section below) +================================== + +Internal API cleanup +-------------------- + +During refactoring, some functions had to be declared in header files +to make them available to other fsfs code. We need to revisit those +function definitions to turn them into a proper API that may be useful +to other code (such as fsfs tools). + + +Checksum all metadata elements +------------------------------ + +All elements of an FS-X repository shall be guarded by checksums. That +includes indexes, noderevs etc. Larger data structures, such as index +files, should have checksummed sub-elements such that corrupted parts +may be identified and potentially repaired / circumvented in a meaningful +way. + +Those checksums may be quite simple such as Adler32 because that meta- +data can be cross-verified with other parts as well and acts only as a +fallback to narrow down the affected parts. + +'svnadmin verify' shall check consistency based on those checksums. + + +Port existing FSFS tools +------------------------ + +fsfs-stats, fsfsverify.py and possibly others should have equivalents +in the FS-X world. + + +Optimize data ordering during pack +---------------------------------- + +I/O optimized copy algorithms are yet to be implemented. The current +code is relatively slow as it performs quasi-random I/O on the +input stream. + + +TxDelta v2 +---------- + +Version 1 of txdelta turns out to be limited in its effectiveness for +larger files when data gets inserted or removed. For typical office +documents (zip files), deltification often becomes ineffective. + +Version 2 shall introduce the following changes: + +- increase the delta window from 100kB to 1MB +- use a sliding window instead of a fixed-sized one +- use a slightly more efficient instruction encoding + +When introducing it, we will make it an option at the txdelta interfaces +(e.g. a format number). The version will be indicated in the 'SVN\x1' / +'SVN\x2' stream header. While at it, (try to) fix the layering violations +where those prefixes are being read or written. + + +Large file storage +------------------ + +Even most source code repositories contain large, hard to compress, +hard to deltify binaries. Reconstructing their content becomes very I/O +intense and it "dilutes" the data in our pack files. The latter makes +e.g. caching, prefetching and packing less efficient. + +Once a representation exceeds a certain configured threshold (16M default), +the fulltext of that item will be stored in a separate file. This will +be marked in the representation_t by an extra flag and future reps will +not be deltified against it. From that location, the data can be forwarded +directly via SendFile and the fulltext caches will not be used for it. + +Note that by making the decision contingent upon the size of the deltified +and packed representation, all large data that benefit from these (i.e. +have smaller increments) will still be stored within the rev and pack files. +If a future representation is smaller than the threshold, it may be + +/* danielsh: so if we have a file which is 20MB over many revisions, it'll +be stored in fulltext every single time unless the configured threshold is +changed? Wondering if that's the best solution... */ + + +Sorted binary directory representations +--------------------------------------- + +Lookup of entries in a directory is a frequent operation when following +cached paths. The represents directories as arrays sorted by entry name +to allow for binary search during that lookup. However, all external +representation uses hashes and the conversion is expensive. + +FS-X shall store directory representations sorted by element names and +all use that array representation internally wherever appropriate. This +will minimize the conversion overhead for long directories, especially +during transaction building. + +Moreover, switch from the key/value representation to a slightly tighter +and easier to process binary representation (validity is already guaranteed +by checksums). + + +Star-Deltification +------------------ + +Current implementation is incomplete. TODO: actually support & use base +representations, optimize instruction table. + +Combine this with Txdelta 2 such that the corresponding windows from +all representations get stored in a common star-delta container. + + +Multiple pack stages +-------------------- + +FSFS only knows one packing level - the shard. For repositories with +a large number of revisions, it may be more efficient to start with small +packs (10-ish) and later pack them into larger and larger ones. + + +Open less files when opening a repository +----------------------------------------- + +Opening a repository reads numerous files in db/ (besides several more in +../conf): uuid, current, format, fs-type, fsfs.conf, min-unpacked-rev, ... + +Combine most of them into one or two files (eg uuid|format(|fs-type?), +current|min-unpacked-revprop). + + +Sharded transaction directories +------------------------------- + +Transaction directories contain 3 OS files per FS file modified in the +transaction. That doesn't scale well; find something better. + + +DONE +==== + +Turn into separate FS +--------------------- + +Make FS-X a separate file system alongside BDB and FSFS. Rip out all +FSFS compatibility code. + + +Logical addressing +------------------ + +To allow for moving data structures around within the repository, we must +replace the current absolute addressing using file offsets with a logical +one. All references will no take the form of (revision, index) pairs and +a replacement to the format 6 manifest files will map that to actual file +offsets. + +Having the need to map revision-local offsets to pack-file global offsets +today already gives us some localized address mapping code that simply +needs to be replaced. + + +Optimize data ordering during pack +---------------------------------- + +Replace today's simple concatenating shard packing process with a one +placing fragments (representations and noderevs) from various revisions +close to each other if they are likely needed to serve in the same request. + +We will optimize on a per-shard basis. The general strategy is + +* place all change lists at the beginning of the pack file + - strict revision order + - place newest ones first +* place all file properties reps next + - place newer reps first +* place all directory properties next + - place newer reps first +* place all root nodes and root directories + - ordered newest rev -> oldest rev + - place rep delta chains 'en block' + - place root node in front of rep, if that rep has not already + been placed as part of a rep delta chain +* place remaining content as follows: + - place node rev directly in front of their reps (where they have one) + - start with the latest root directory not placed, yet + - recurse to sub-folders first with, sorted by name + - per folder, place files in naming order + - place rep deltification chains in deltification order (new->old) +* no fragments should be left but if they are, put them at the end + + +Index pack files +---------------- + +In addition to the manifest we need for the (revision, index) -> offset +mapping, we also introduce an offset -> (revision, index, type) index +file. This will allow us to parse any data in a pack file without walking +the DAG top down. + + +Data prefetch +------------- + +This builds on the previous. The idea is that whenever a cache lookup +fails, we will not just read the single missing fragment but parse all +data within the APR file buffer and put that into the cache. + +For maximum efficiency, we will align the data blocks being read to +multiples of the block size and allow that buffer size to be configured +(where supported by APR). The default block size will be raised to 64kB. + + +Extend 'svnadmin verify' +------------------------ + +Format 7 provides many extra chances to verify contents plus contains +extra indexes that must be consistent with the pack / rev files. We +must extend the tests to cover all that. + + +Containers +---------- + +Extend the index format support containers, i.e. map a logical item index +to (file offset, sub-index) pairs. The whole container will be read and +cached and the specific item later accessed from the whole structure. + +Use these containers for reps, noderevs and changes. Provide specific +data container types for each of these item types and different item +types cannot be put into the same container. Containers are binaries, +i.e. there is no textual representations of their contents. + +This allows for significant space savings on disk due to deltification +amongst e.g. revprops. More importantly, it reduces the size of the +runtime data structures within the cache *and* reduces the number of +cache entries (the cache is can't handle items < 500 bytes very well). + + +Packed change lists +------------------- + +Change lists tend to be large, in some cases >20% of the repo. Due to the +new ordering of pack data, the change lists can be the largest part of +data to read for svn log. Use our standard compression method to save +70 .. 80% of the disk space. + +Packing will only be applied to binary representations of change lists +to keep the number of possible combinations low. + + +Star-Deltification +------------------ + +Most node contents are smaller than 500k, i.e. less than Txdelta 2 window. +Those contents shall be aggregated into star-delta containers upon pack. +This will save significant amounts of disk space, particularly in case +of heavy branching. Also, the data extraction is independent of the +number of deltas, i.e. delta chain length) within the same container. + + +Support for arbitrary chars in path names +----------------------------------------- + +FSFS's textual item representations breaks when path names contain +newlines. FS-X revisions shall escape all control chars (e.g. < 0x20) +in path names when using them in textual item representations. + diff --git a/contrib/subversion/subversion/libsvn_fs_x/cached_data.c b/contrib/subversion/subversion/libsvn_fs_x/cached_data.c new file mode 100644 index 000000000..2fdf56999 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/cached_data.c @@ -0,0 +1,3355 @@ +/* cached_data.c --- cached (read) access to FSX data + * + * ==================================================================== + * 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 "cached_data.h" + +#include + +#include "svn_hash.h" +#include "svn_ctype.h" +#include "svn_sorts.h" + +#include "private/svn_io_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "fs_x.h" +#include "low_level.h" +#include "util.h" +#include "pack.h" +#include "temp_serializer.h" +#include "index.h" +#include "changes.h" +#include "noderevs.h" +#include "reps.h" + +#include "../libsvn_fs/fs-loader.h" +#include "../libsvn_delta/delta.h" /* for SVN_DELTA_WINDOW_SIZE */ + +#include "svn_private_config.h" + +/* forward-declare. See implementation for the docstring */ +static svn_error_t * +block_read(void **result, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + svn_fs_x__revision_file_t *revision_file, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Defined this to enable access logging via dgb__log_access +#define SVN_FS_X__LOG_ACCESS +*/ + +/* When SVN_FS_X__LOG_ACCESS has been defined, write a line to console + * showing where ID is located in FS and use ITEM to show details on it's + * contents if not NULL. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +dgb__log_access(svn_fs_t *fs, + const svn_fs_x__id_t *id, + void *item, + apr_uint32_t item_type, + apr_pool_t *scratch_pool) +{ + /* no-op if this macro is not defined */ +#ifdef SVN_FS_X__LOG_ACCESS + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_off_t offset = -1; + apr_off_t end_offset = 0; + apr_uint32_t sub_item = 0; + svn_fs_x__p2l_entry_t *entry = NULL; + static const char *types[] = {"", "frep ", "drep ", "fprop", "dprop", + "node ", "chgs ", "rep ", "c:", "n:", "r:"}; + const char *description = ""; + const char *type = types[item_type]; + const char *pack = ""; + svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set); + + /* determine rev / pack file offset */ + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, id, scratch_pool)); + + /* constructing the pack file description */ + if (revision < ffd->min_unpacked_rev) + pack = apr_psprintf(scratch_pool, "%4ld|", + revision / ffd->max_files_per_dir); + + /* construct description if possible */ + if (item_type == SVN_FS_X__ITEM_TYPE_NODEREV && item != NULL) + { + svn_fs_x__noderev_t *node = item; + const char *data_rep + = node->data_rep + ? apr_psprintf(scratch_pool, " d=%ld/%" APR_UINT64_T_FMT, + svn_fs_x__get_revnum(node->data_rep->id.change_set), + node->data_rep->id.number) + : ""; + const char *prop_rep + = node->prop_rep + ? apr_psprintf(scratch_pool, " p=%ld/%" APR_UINT64_T_FMT, + svn_fs_x__get_revnum(node->prop_rep->id.change_set), + node->prop_rep->id.number) + : ""; + description = apr_psprintf(scratch_pool, "%s (pc=%d%s%s)", + node->created_path, + node->predecessor_count, + data_rep, + prop_rep); + } + else if (item_type == SVN_FS_X__ITEM_TYPE_ANY_REP) + { + svn_fs_x__rep_header_t *header = item; + if (header == NULL) + description = " (txdelta window)"; + else if (header->type == svn_fs_x__rep_self_delta) + description = " DELTA"; + else + description = apr_psprintf(scratch_pool, + " DELTA against %ld/%" APR_UINT64_T_FMT, + header->base_revision, + header->base_item_index); + } + else if (item_type == SVN_FS_X__ITEM_TYPE_CHANGES && item != NULL) + { + apr_array_header_t *changes = item; + switch (changes->nelts) + { + case 0: description = " no change"; + break; + case 1: description = " 1 change"; + break; + default: description = apr_psprintf(scratch_pool, " %d changes", + changes->nelts); + } + } + + /* reverse index lookup: get item description in ENTRY */ + SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, revision, offset, + scratch_pool)); + if (entry) + { + /* more details */ + end_offset = offset + entry->size; + type = types[entry->type]; + + /* merge the sub-item number with the container type */ + if ( entry->type == SVN_FS_X__ITEM_TYPE_CHANGES_CONT + || entry->type == SVN_FS_X__ITEM_TYPE_NODEREVS_CONT + || entry->type == SVN_FS_X__ITEM_TYPE_REPS_CONT) + type = apr_psprintf(scratch_pool, "%s%-3d", type, sub_item); + } + + /* line output */ + printf("%5s%4lx:%04lx -%4lx:%04lx %s %7ld %5"APR_UINT64_T_FMT" %s\n", + pack, (long)(offset / ffd->block_size), + (long)(offset % ffd->block_size), + (long)(end_offset / ffd->block_size), + (long)(end_offset % ffd->block_size), + type, revision, id->number, description); + +#endif + + return SVN_NO_ERROR; +} + +/* Convenience wrapper around svn_io_file_aligned_seek, taking filesystem + FS instead of a block size. */ +static svn_error_t * +aligned_seek(svn_fs_t *fs, + apr_file_t *file, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + return svn_error_trace(svn_io_file_aligned_seek(file, ffd->block_size, + buffer_start, offset, + scratch_pool)); +} + +/* Open the revision file for the item given by ID in filesystem FS and + store the newly opened file in FILE. Seek to the item's location before + returning. + + Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ +static svn_error_t * +open_and_seek_revision(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__revision_file_t *rev_file; + apr_off_t offset = -1; + apr_uint32_t sub_item = 0; + svn_revnum_t rev = svn_fs_x__get_revnum(id->change_set); + + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, rev, result_pool, + scratch_pool)); + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, id, + scratch_pool)); + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, scratch_pool)); + + *file = rev_file; + + return SVN_NO_ERROR; +} + +/* Open the representation REP for a node-revision in filesystem FS, seek + to its position and store the newly opened file in FILE. + + Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ +static svn_error_t * +open_and_seek_transaction(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_off_t offset; + apr_uint32_t sub_item = 0; + apr_int64_t txn_id = svn_fs_x__get_txn_id(rep->id.change_set); + + SVN_ERR(svn_fs_x__open_proto_rev_file(file, fs, txn_id, result_pool, + scratch_pool)); + + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, *file, &rep->id, + scratch_pool)); + SVN_ERR(aligned_seek(fs, (*file)->file, NULL, offset, scratch_pool)); + + 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. + + Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ +static svn_error_t * +open_and_seek_representation(svn_fs_x__revision_file_t **file_p, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if (svn_fs_x__is_revision(rep->id.change_set)) + return open_and_seek_revision(file_p, fs, &rep->id, result_pool, + scratch_pool); + else + return open_and_seek_transaction(file_p, fs, rep, result_pool, + scratch_pool); +} + + + +static svn_error_t * +err_dangling_id(svn_fs_t *fs, + const svn_fs_x__id_t *id) +{ + svn_string_t *id_str = svn_fs_x__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); +} + +/* 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_x__get_node_revision, which wraps this and adds another + error. */ +static svn_error_t * +get_node_revision_body(svn_fs_x__noderev_t **noderev_p, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_boolean_t is_cached = FALSE; + svn_fs_x__data_t *ffd = fs->fsap_data; + + if (svn_fs_x__is_txn(id->change_set)) + { + apr_file_t *file; + + /* This is a transaction node-rev. Its storage logic is very + different from that of rev / pack files. */ + err = svn_io_file_open(&file, + svn_fs_x__path_txn_node_rev(fs, id, + scratch_pool, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_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_x__read_noderev(noderev_p, + svn_stream_from_aprfile2(file, + FALSE, + scratch_pool), + result_pool, scratch_pool)); + } + else + { + svn_fs_x__revision_file_t *revision_file; + + /* noderevs in rev / pack files can be cached */ + svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set); + svn_fs_x__pair_cache_key_t key; + + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, revision, + scratch_pool, scratch_pool)); + + /* First, try a noderevs container cache lookup. */ + if ( svn_fs_x__is_packed_rev(fs, revision) + && ffd->noderevs_container_cache) + { + apr_off_t offset; + apr_uint32_t sub_item; + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, revision_file, + id, scratch_pool)); + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = offset; + + SVN_ERR(svn_cache__get_partial((void **)noderev_p, &is_cached, + ffd->noderevs_container_cache, &key, + svn_fs_x__noderevs_get_func, + &sub_item, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + key.revision = revision; + key.second = id->number; + + /* Not found or not applicable. Try a noderev cache lookup. + * If that succeeds, we are done here. */ + if (ffd->node_revision_cache) + { + SVN_ERR(svn_cache__get((void **) noderev_p, + &is_cached, + ffd->node_revision_cache, + &key, + result_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + /* block-read will parse the whole block and will also return + the one noderev that we need right now. */ + SVN_ERR(block_read((void **)noderev_p, fs, + id, + revision_file, + result_pool, + scratch_pool)); + SVN_ERR(svn_fs_x__close_revision_file(revision_file)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_node_revision(svn_fs_x__noderev_t **noderev_p, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = get_node_revision_body(noderev_p, fs, id, + result_pool, scratch_pool); + if (err && err->apr_err == SVN_ERR_FS_CORRUPT) + { + svn_string_t *id_string = svn_fs_x__id_unparse(id, scratch_pool); + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + "Corrupt node-revision '%s'", + id_string->data); + } + + SVN_ERR(dgb__log_access(fs, id, *noderev_p, + SVN_FS_X__ITEM_TYPE_NODEREV, scratch_pool)); + + return svn_error_trace(err); +} + + +svn_error_t * +svn_fs_x__get_mergeinfo_count(apr_int64_t *count, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + /* If we want a full acccess log, we need to provide full data and + cannot take shortcuts here. */ +#if !defined(SVN_FS_X__LOG_ACCESS) + + /* First, try a noderevs container cache lookup. */ + if (! svn_fs_x__is_txn(id->change_set)) + { + /* noderevs in rev / pack files can be cached */ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set); + + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision, + scratch_pool, scratch_pool)); + + if ( svn_fs_x__is_packed_rev(fs, revision) + && ffd->noderevs_container_cache) + { + svn_fs_x__pair_cache_key_t key; + apr_off_t offset; + apr_uint32_t sub_item; + svn_boolean_t is_cached; + + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, + id, scratch_pool)); + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = offset; + + SVN_ERR(svn_cache__get_partial((void **)count, &is_cached, + ffd->noderevs_container_cache, &key, + svn_fs_x__mergeinfo_count_get_func, + &sub_item, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + } +#endif + + /* fallback to the naive implementation handling all edge cases */ + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, scratch_pool, + scratch_pool)); + *count = noderev->mergeinfo_count; + + return SVN_NO_ERROR; +} + +/* Describes a lazily opened rev / pack file. Instances will be shared + between multiple instances of rep_state_t. */ +typedef struct shared_file_t +{ + /* The opened file. NULL while file is not open, yet. */ + svn_fs_x__revision_file_t *rfile; + + /* file system to open the file in */ + svn_fs_t *fs; + + /* a revision contained in the FILE. Since this file may be shared, + that value may be different from REP_STATE_T->REVISION. */ + svn_revnum_t revision; + + /* pool to use when creating the FILE. This guarantees that the file + remains open / valid beyond the respective local context that required + the file to be opened eventually. */ + apr_pool_t *pool; +} shared_file_t; + +/* Represents where in the current svndiff data block each + representation is. */ +typedef struct rep_state_t +{ + /* shared lazy-open rev/pack file structure */ + shared_file_t *sfile; + /* 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; + /* ID addressing the representation */ + svn_fs_x__id_t rep_id; + /* length of the header at the start of the rep. + 0 iff this is rep is stored in a container + (i.e. does not have a header) */ + apr_size_t header_size; + apr_off_t start; /* The starting offset for the raw + svndiff data minus header. + -1 if the offset is yet unknown. */ + /* sub-item index in case the rep is containered */ + apr_uint32_t sub_item; + apr_off_t current;/* The current offset relative to START. */ + apr_off_t size; /* The on-disk size of the representation. */ + int ver; /* If a delta, what svndiff version? + -1 for unknown delta version. */ + int chunk_index; /* number of the window to read */ +} rep_state_t; + +/* Simple wrapper around svn_fs_x__get_file_offset to simplify callers. */ +static svn_error_t * +get_file_offset(apr_off_t *offset, + rep_state_t *rs, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_fs_x__get_file_offset(offset, + rs->sfile->rfile->file, + scratch_pool)); +} + +/* Simple wrapper around svn_io_file_aligned_seek to simplify callers. */ +static svn_error_t * +rs_aligned_seek(rep_state_t *rs, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = rs->sfile->fs->fsap_data; + return svn_error_trace(svn_io_file_aligned_seek(rs->sfile->rfile->file, + ffd->block_size, + buffer_start, offset, + scratch_pool)); +} + +/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */ +static svn_error_t* +auto_open_shared_file(shared_file_t *file) +{ + if (file->rfile == NULL) + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&file->rfile, file->fs, + file->revision, file->pool, + file->pool)); + + return SVN_NO_ERROR; +} + +/* Set RS->START to the begin of the representation raw in RS->SFILE->RFILE, + if that hasn't been done yet. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t* +auto_set_start_offset(rep_state_t *rs, + apr_pool_t *scratch_pool) +{ + if (rs->start == -1) + { + SVN_ERR(svn_fs_x__item_offset(&rs->start, &rs->sub_item, + rs->sfile->fs, rs->sfile->rfile, + &rs->rep_id, scratch_pool)); + rs->start += rs->header_size; + } + + return SVN_NO_ERROR; +} + +/* Set RS->VER depending on what is found in the already open RS->FILE->FILE + if the diff version is still unknown. Use SCRATCH_POOL for temporary + allocations. + */ +static svn_error_t* +auto_read_diff_version(rep_state_t *rs, + apr_pool_t *scratch_pool) +{ + if (rs->ver == -1) + { + char buf[4]; + SVN_ERR(rs_aligned_seek(rs, NULL, rs->start, scratch_pool)); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, buf, + sizeof(buf), NULL, NULL, scratch_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->current = 4; + } + + return SVN_NO_ERROR; +} + +/* See create_rep_state, which wraps this and adds another error. */ +static svn_error_t * +create_rep_state_body(rep_state_t **rep_state, + svn_fs_x__rep_header_t **rep_header, + shared_file_t **shared_file, + svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + rep_state_t *rs = apr_pcalloc(result_pool, sizeof(*rs)); + svn_fs_x__rep_header_t *rh; + svn_boolean_t is_cached = FALSE; + svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + apr_uint64_t estimated_window_storage; + + /* 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 + * we can re-use the same, already open file object + */ + svn_boolean_t reuse_shared_file + = shared_file && *shared_file && (*shared_file)->rfile + && SVN_IS_VALID_REVNUM((*shared_file)->revision) + && (*shared_file)->revision < ffd->min_unpacked_rev + && revision < ffd->min_unpacked_rev + && ( ((*shared_file)->revision / ffd->max_files_per_dir) + == (revision / ffd->max_files_per_dir)); + + svn_fs_x__representation_cache_key_t key = { 0 }; + key.revision = revision; + key.is_packed = revision < ffd->min_unpacked_rev; + key.item_index = rep->id.number; + + /* continue constructing RS and RA */ + rs->size = rep->size; + rs->rep_id = rep->id; + rs->ver = -1; + rs->start = -1; + + /* Very long files stored as self-delta will produce a huge number of + delta windows. Don't cache them lest we don't thrash the cache. + Since we don't know the depth of the delta chain, let's assume, the + whole contents get rewritten 3 times. + */ + estimated_window_storage + = 4 * ( (rep->expanded_size ? rep->expanded_size : rep->size) + + SVN_DELTA_WINDOW_SIZE); + estimated_window_storage = MIN(estimated_window_storage, APR_SIZE_MAX); + + rs->window_cache = ffd->txdelta_window_cache + && svn_cache__is_cachable(ffd->txdelta_window_cache, + (apr_size_t)estimated_window_storage) + ? ffd->txdelta_window_cache + : NULL; + rs->combined_cache = ffd->combined_window_cache + && svn_cache__is_cachable(ffd->combined_window_cache, + (apr_size_t)estimated_window_storage) + ? ffd->combined_window_cache + : NULL; + + /* cache lookup, i.e. skip reading the rep header if possible */ + if (ffd->rep_header_cache && SVN_IS_VALID_REVNUM(revision)) + SVN_ERR(svn_cache__get((void **) &rh, &is_cached, + ffd->rep_header_cache, &key, result_pool)); + + /* initialize the (shared) FILE member in RS */ + if (reuse_shared_file) + { + rs->sfile = *shared_file; + } + else + { + shared_file_t *file = apr_pcalloc(result_pool, sizeof(*file)); + file->revision = revision; + file->pool = result_pool; + file->fs = fs; + rs->sfile = file; + + /* remember the current file, if suggested by the caller */ + if (shared_file) + *shared_file = file; + } + + /* read rep header, if necessary */ + if (!is_cached) + { + /* we will need the on-disk location for non-txn reps */ + apr_off_t offset; + svn_boolean_t in_container = TRUE; + + /* ensure file is open and navigate to the start of rep header */ + if (reuse_shared_file) + { + /* ... we can re-use the same, already open file object. + * This implies that we don't read from a txn. + */ + rs->sfile = *shared_file; + SVN_ERR(auto_open_shared_file(rs->sfile)); + } + else + { + /* otherwise, create a new file object. May or may not be + * an in-txn file. + */ + SVN_ERR(open_and_seek_representation(&rs->sfile->rfile, fs, rep, + result_pool, scratch_pool)); + } + + if (SVN_IS_VALID_REVNUM(revision)) + { + apr_uint32_t sub_item; + + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, + rs->sfile->rfile, &rep->id, + scratch_pool)); + + /* is rep stored in some star-deltified container? */ + if (sub_item == 0) + { + svn_fs_x__p2l_entry_t *entry; + SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, rs->sfile->rfile, + revision, offset, + scratch_pool, scratch_pool)); + in_container = entry->type == SVN_FS_X__ITEM_TYPE_REPS_CONT; + } + + if (in_container) + { + /* construct a container rep header */ + *rep_header = apr_pcalloc(result_pool, sizeof(**rep_header)); + (*rep_header)->type = svn_fs_x__rep_container; + + /* exit to caller */ + *rep_state = rs; + return SVN_NO_ERROR; + } + + SVN_ERR(rs_aligned_seek(rs, NULL, offset, scratch_pool)); + } + + SVN_ERR(svn_fs_x__read_rep_header(&rh, rs->sfile->rfile->stream, + result_pool, scratch_pool)); + SVN_ERR(get_file_offset(&rs->start, rs, result_pool)); + + /* populate the cache if appropriate */ + if (SVN_IS_VALID_REVNUM(revision)) + { + SVN_ERR(block_read(NULL, fs, &rs->rep_id, rs->sfile->rfile, + result_pool, scratch_pool)); + if (ffd->rep_header_cache) + SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, + scratch_pool)); + } + } + + /* finalize */ + SVN_ERR(dgb__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP, + scratch_pool)); + + rs->header_size = rh->header_size; + *rep_state = rs; + *rep_header = rh; + + rs->chunk_index = 0; + + /* skip "SVNx" diff marker */ + rs->current = 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 SHARED_FILE. (If SHARED_FILE is not NULL, in the first + call it should be a pointer to NULL.) The function will use this + variable to store the previous call results and tries to re-use it. + This may result in significant savings in I/O for packed files and + number of open file handles. + */ +static svn_error_t * +create_rep_state(rep_state_t **rep_state, + svn_fs_x__rep_header_t **rep_header, + shared_file_t **shared_file, + svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = create_rep_state_body(rep_state, rep_header, + shared_file, rep, fs, + result_pool, scratch_pool); + if (err && err->apr_err == SVN_ERR_FS_CORRUPT) + { + /* ### 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 FSX 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 + ? svn_fs_x__unparse_representation + (rep, TRUE, scratch_pool, + scratch_pool)->data + : "(null)"); + } + /* ### Call representation_string() ? */ + return svn_error_trace(err); +} + +svn_error_t * +svn_fs_x__check_rep(svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + apr_off_t offset; + apr_uint32_t sub_item; + svn_fs_x__p2l_entry_t *entry; + svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision, + scratch_pool, scratch_pool)); + + /* Does REP->ID refer to an actual item? Which one is it? */ + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, &rep->id, + scratch_pool)); + + /* What is the type of that item? */ + SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, rev_file, revision, offset, + scratch_pool, scratch_pool)); + + /* Verify that we've got an item that is actually a representation. */ + if ( entry == NULL + || ( entry->type != SVN_FS_X__ITEM_TYPE_FILE_REP + && entry->type != SVN_FS_X__ITEM_TYPE_DIR_REP + && entry->type != SVN_FS_X__ITEM_TYPE_FILE_PROPS + && entry->type != SVN_FS_X__ITEM_TYPE_DIR_PROPS + && entry->type != SVN_FS_X__ITEM_TYPE_REPS_CONT)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("No representation found at offset %s " + "for item %s in revision %ld"), + apr_off_t_toa(scratch_pool, offset), + apr_psprintf(scratch_pool, "%" APR_UINT64_T_FMT, + rep->id.number), + revision); + + return SVN_NO_ERROR; +} + +/* . + Do any allocations in POOL. */ +svn_error_t * +svn_fs_x__rep_chain_length(int *chain_length, + int *shard_count, + svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_revnum_t shard_size = ffd->max_files_per_dir; + svn_boolean_t is_delta = FALSE; + int count = 0; + int shards = 1; + svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + svn_revnum_t last_shard = revision / shard_size; + + /* Note that this iteration pool will be used in a non-standard way. + * To reuse open file handles between iterations (e.g. while within the + * same pack file), we only clear this pool once in a while instead of + * at the start of each iteration. */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Check whether the length of the deltification chain is acceptable. + * Otherwise, shared reps may form a non-skipping delta chain in + * extreme cases. */ + svn_fs_x__representation_t base_rep = *rep; + + /* re-use open files between iterations */ + shared_file_t *file_hint = NULL; + + svn_fs_x__rep_header_t *header; + + /* follow the delta chain towards the end but for at most + * MAX_CHAIN_LENGTH steps. */ + do + { + rep_state_t *rep_state; + revision = svn_fs_x__get_revnum(base_rep.id.change_set); + if (revision / shard_size != last_shard) + { + last_shard = revision / shard_size; + ++shards; + } + + SVN_ERR(create_rep_state_body(&rep_state, + &header, + &file_hint, + &base_rep, + fs, + iterpool, + iterpool)); + + base_rep.id.change_set + = svn_fs_x__change_set_by_rev(header->base_revision); + base_rep.id.number = header->base_item_index; + base_rep.size = header->base_length; + is_delta = header->type == svn_fs_x__rep_delta; + + /* Clear it the ITERPOOL once in a while. Doing it too frequently + * renders the FILE_HINT ineffective. Doing too infrequently, may + * leave us with too many open file handles. + * + * Note that this is mostly about efficiency, with larger values + * being more efficient, and any non-zero value is legal here. When + * reading deltified contents, we may keep 10s of rev files open at + * the same time and the system has to cope with that. Thus, the + * limit of 16 chosen below is in the same ballpark. + */ + ++count; + if (count % 16 == 0) + { + file_hint = NULL; + svn_pool_clear(iterpool); + } + } + while (is_delta && base_rep.id.change_set); + + *chain_length = count; + *shard_count = shards; + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +typedef struct rep_read_baton_t +{ + /* The FS from which we're reading. */ + svn_fs_t *fs; + + /* Representation to read. */ + svn_fs_x__representation_t rep; + + /* 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. */ + rep_state_t *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. */ + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; + + svn_filesize_t len; + svn_filesize_t off; + + /* The key for the fulltext cache for this rep, if there is a + fulltext cache. */ + svn_fs_x__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; + + /* If not NULL, attempt to read the data from this cache. + Once that lookup fails, reset it to NULL. */ + svn_cache__t *fulltext_cache; + + /* Bytes delivered from the FULLTEXT_CACHE so far. If the next + lookup fails, we need to skip that much data from the reconstructed + window stream before we continue normal operation. */ + svn_filesize_t fulltext_delivered; + + /* Used for temporary allocations during the read. */ + apr_pool_t *scratch_pool; + + /* Pool used to store file handles and other data that is persistant + for the entire stream read. */ + apr_pool_t *filehandle_pool; +} rep_read_baton_t; + +/* Set window key in *KEY to address the window described by RS. + For convenience, return the KEY. */ +static svn_fs_x__window_cache_key_t * +get_window_key(svn_fs_x__window_cache_key_t *key, + rep_state_t *rs) +{ + svn_revnum_t revision = svn_fs_x__get_revnum(rs->rep_id.change_set); + assert(revision <= APR_UINT32_MAX); + + key->revision = (apr_uint32_t)revision; + key->item_index = rs->rep_id.number; + key->chunk_index = rs->chunk_index; + + return key; +} + +/* Read the WINDOW_P number CHUNK_INDEX for the representation given in + * rep state RS from the current FSX 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 to CHUNK_INDEX. + */ + +/* Return data type for get_cached_window_sizes_func. + */ +typedef struct window_sizes_t +{ + /* length of the txdelta window in its on-disk format */ + svn_filesize_t packed_len; + + /* expanded (and combined) window length */ + svn_filesize_t target_len; +} window_sizes_t; + +/* Implements svn_cache__partial_getter_func_t extracting the packed + * and expanded window sizes from a cached window and return the size + * info as a window_sizes_t* in *OUT. + */ +static svn_error_t * +get_cached_window_sizes_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + const svn_fs_x__txdelta_cached_window_t *window = data; + const svn_txdelta_window_t *txdelta_window + = svn_temp_deserializer__ptr(window, (const void **)&window->window); + + window_sizes_t *result = apr_palloc(pool, sizeof(*result)); + result->packed_len = window->end_offset - window->start_offset; + result->target_len = txdelta_window->tview_len; + + *out = result; + + return SVN_NO_ERROR; +} + +/* Read the WINDOW_P number CHUNK_INDEX for the representation given in + * 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 will be made + * from RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + * + * If the information could be found, put RS to CHUNK_INDEX. + */ +static svn_error_t * +get_cached_window_sizes(window_sizes_t **sizes, + rep_state_t *rs, + svn_boolean_t *is_cached, + apr_pool_t *pool) +{ + if (! rs->window_cache) + { + /* txdelta window has not been enabled */ + *is_cached = FALSE; + } + else + { + svn_fs_x__window_cache_key_t key = { 0 }; + SVN_ERR(svn_cache__get_partial((void **)sizes, + is_cached, + rs->window_cache, + get_window_key(&key, rs), + get_cached_window_sizes_func, + NULL, + pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +get_cached_window(svn_txdelta_window_t **window_p, + rep_state_t *rs, + int chunk_index, + svn_boolean_t *is_cached, + apr_pool_t *result_pool, + apr_pool_t *scratch_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_x__txdelta_cached_window_t *cached_window; + svn_fs_x__window_cache_key_t key = { 0 }; + get_window_key(&key, rs); + key.chunk_index = chunk_index; + SVN_ERR(svn_cache__get((void **) &cached_window, + is_cached, + rs->window_cache, + &key, + result_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->current = cached_window->end_offset; + rs->chunk_index = chunk_index; + } + } + + return SVN_NO_ERROR; +} + +/* Store the WINDOW read for the rep state RS with the given START_OFFSET + * within the pack / rev file in the current FSX 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, + rep_state_t *rs, + apr_off_t start_offset, + apr_pool_t *scratch_pool) +{ + if (rs->window_cache) + { + /* store the window and the first offset _past_ it */ + svn_fs_x__txdelta_cached_window_t cached_window; + svn_fs_x__window_cache_key_t key = {0}; + + cached_window.window = window; + cached_window.start_offset = start_offset - rs->start; + cached_window.end_offset = rs->current; + + /* but key it with the start offset because that is the known state + * when we will look it up */ + SVN_ERR(svn_cache__set(rs->window_cache, + get_window_key(&key, rs), + &cached_window, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Read the WINDOW_P for the rep state RS from the current FSX 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 particular) will be made from POOL. + */ +static svn_error_t * +get_cached_combined_window(svn_stringbuf_t **window_p, + rep_state_t *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 */ + svn_fs_x__window_cache_key_t key = { 0 }; + return svn_cache__get((void **)window_p, + is_cached, + rs->combined_cache, + get_window_key(&key, rs), + pool); + } + + return SVN_NO_ERROR; +} + +/* Store the WINDOW read for the rep state RS in the current FSX 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, + rep_state_t *rs, + 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 */ + svn_fs_x__window_cache_key_t key = { 0 }; + return svn_cache__set(rs->combined_cache, + get_window_key(&key, rs), + 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 self-compressed rep. Set *SRC_STATE to + the container 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. + */ +static svn_error_t * +build_rep_list(apr_array_header_t **list, + svn_stringbuf_t **window_p, + rep_state_t **src_state, + svn_fs_t *fs, + svn_fs_x__representation_t *first_rep, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_t rep; + rep_state_t *rs = NULL; + svn_fs_x__rep_header_t *rep_header; + svn_boolean_t is_cached = FALSE; + shared_file_t *shared_file = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + *list = apr_array_make(result_pool, 1, sizeof(rep_state_t *)); + rep = *first_rep; + + /* for the top-level rep, we need the rep_args */ + SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, &rep, fs, + result_pool, iterpool)); + + while (1) + { + svn_pool_clear(iterpool); + + /* fetch state, if that has not been done already */ + if (!rs) + SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, + &rep, fs, result_pool, iterpool)); + + /* for txn reps and containered reps, there won't be a cached + * combined window */ + if (svn_fs_x__is_revision(rep.id.change_set) + && rep_header->type != svn_fs_x__rep_container) + SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached, + result_pool)); + + if (is_cached) + { + /* We already have a reconstructed window in our cache. + Write a pseudo rep_state with the full length. */ + rs->start = 0; + rs->current = 0; + rs->size = (*window_p)->len; + *src_state = rs; + break; + } + + if (rep_header->type == svn_fs_x__rep_container) + { + /* This is a container item, so just return the current rep_state. */ + *src_state = rs; + break; + } + + /* Push this rep onto the list. If it's self-compressed, we're done. */ + APR_ARRAY_PUSH(*list, rep_state_t *) = rs; + if (rep_header->type == svn_fs_x__rep_self_delta) + { + *src_state = NULL; + break; + } + + rep.id.change_set + = svn_fs_x__change_set_by_rev(rep_header->base_revision); + rep.id.number = rep_header->base_item_index; + rep.size = rep_header->base_length; + + rs = NULL; + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* 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. If rep is mutable, it must be + refer to file contents. + + Allocate the result in RESULT_POOL. This includes the pools within *RB_P. + */ +static svn_error_t * +rep_read_get_baton(rep_read_baton_t **rb_p, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + svn_fs_x__pair_cache_key_t fulltext_cache_key, + apr_pool_t *result_pool) +{ + rep_read_baton_t *b; + + b = apr_pcalloc(result_pool, sizeof(*b)); + b->fs = fs; + b->rep = *rep; + b->base_window = NULL; + b->chunk_index = 0; + b->buf = NULL; + b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, + result_pool); + b->checksum_finalized = FALSE; + memcpy(b->md5_digest, rep->md5_digest, sizeof(rep->md5_digest)); + b->len = rep->expanded_size; + b->off = 0; + b->fulltext_cache_key = fulltext_cache_key; + + /* Clearable sub-pools. Since they have to remain valid for as long as B + lives, we can't take them from some scratch pool. The caller of this + function will have no control over how those subpools will be used. */ + b->scratch_pool = svn_pool_create(result_pool); + b->filehandle_pool = svn_pool_create(result_pool); + b->fulltext_cache = NULL; + b->fulltext_delivered = 0; + 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, + rep_state_t *rs, apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t is_cached; + apr_off_t start_offset; + apr_off_t end_offset; + apr_pool_t *iterpool; + + SVN_ERR_ASSERT(rs->chunk_index <= this_chunk); + + SVN_ERR(dgb__log_access(rs->sfile->fs, &rs->rep_id, NULL, + SVN_FS_X__ITEM_TYPE_ANY_REP, scratch_pool)); + + /* Read the next window. But first, try to find it in the cache. */ + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* someone has to actually read the data from file. Open it */ + SVN_ERR(auto_open_shared_file(rs->sfile)); + + /* invoke the 'block-read' feature for non-txn data. + However, don't do that if we are in the middle of some representation, + because the block is unlikely to contain other data. */ + if ( rs->chunk_index == 0 + && svn_fs_x__is_revision(rs->rep_id.change_set) + && rs->window_cache) + { + SVN_ERR(block_read(NULL, rs->sfile->fs, &rs->rep_id, + rs->sfile->rfile, result_pool, scratch_pool)); + + /* reading the whole block probably also provided us with the + desired txdelta window */ + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + /* data is still not cached -> we need to read it. + Make sure we have all the necessary info. */ + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); + SVN_ERR(auto_read_diff_version(rs, scratch_pool)); + + /* RS->FILE may be shared between RS instances -> make sure we point + * to the right data. */ + start_offset = rs->start + rs->current; + SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, scratch_pool)); + + /* Skip windows to reach the current chunk if we aren't there yet. */ + iterpool = svn_pool_create(scratch_pool); + while (rs->chunk_index < this_chunk) + { + apr_file_t *file = rs->sfile->rfile->file; + svn_pool_clear(iterpool); + + SVN_ERR(svn_txdelta_skip_svndiff_window(file, rs->ver, iterpool)); + rs->chunk_index++; + SVN_ERR(svn_fs_x__get_file_offset(&start_offset, file, iterpool)); + + rs->current = start_offset - rs->start; + if (rs->current >= rs->size) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Reading one svndiff window read " + "beyond the end of the " + "representation")); + } + svn_pool_destroy(iterpool); + + /* Actually read the next window. */ + SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->sfile->rfile->stream, + rs->ver, result_pool)); + SVN_ERR(get_file_offset(&end_offset, rs, scratch_pool)); + rs->current = end_offset - rs->start; + if (rs->current > rs->size) + 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) */ + if (svn_fs_x__is_revision(rs->rep_id.change_set)) + SVN_ERR(set_cached_window(*nwin, rs, start_offset, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the whole representation RS and return it in *NWIN. */ +static svn_error_t * +read_container_window(svn_stringbuf_t **nwin, + rep_state_t *rs, + apr_size_t size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__rep_extractor_t *extractor = NULL; + svn_fs_t *fs = rs->sfile->fs; + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__pair_cache_key_t key; + svn_revnum_t revision = svn_fs_x__get_revnum(rs->rep_id.change_set); + + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = rs->start; + + /* already in cache? */ + if (ffd->reps_container_cache) + { + svn_boolean_t is_cached = FALSE; + svn_fs_x__reps_baton_t baton; + baton.fs = fs; + baton.idx = rs->sub_item; + + SVN_ERR(svn_cache__get_partial((void**)&extractor, &is_cached, + ffd->reps_container_cache, &key, + svn_fs_x__reps_get_func, &baton, + result_pool)); + } + + /* read from disk, if necessary */ + if (extractor == NULL) + { + SVN_ERR(auto_open_shared_file(rs->sfile)); + SVN_ERR(block_read((void **)&extractor, fs, &rs->rep_id, + rs->sfile->rfile, result_pool, scratch_pool)); + } + + SVN_ERR(svn_fs_x__extractor_drive(nwin, extractor, rs->current, size, + result_pool, scratch_pool)); + + /* Update RS. */ + rs->current += (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, + rep_read_baton_t *rb) +{ + apr_pool_t *pool, *new_pool, *window_pool; + int i; + apr_array_header_t *windows; + svn_stringbuf_t *source, *buf = rb->base_window; + rep_state_t *rs; + apr_pool_t *iterpool; + + /* 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->scratch_pool); + windows = apr_array_make(window_pool, 0, sizeof(svn_txdelta_window_t *)); + iterpool = svn_pool_create(rb->scratch_pool); + for (i = 0; i < rb->rs_list->nelts; ++i) + { + svn_txdelta_window_t *window; + + svn_pool_clear(iterpool); + + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); + SVN_ERR(read_delta_window(&window, rb->chunk_index, rs, window_pool, + iterpool)); + + 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->scratch_pool); + for (--i; i >= 0; --i) + { + svn_txdelta_window_t *window; + + svn_pool_clear(iterpool); + + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); + window = APR_ARRAY_IDX(windows, i, svn_txdelta_window_t *); + + /* Maybe, we've got a start representation in a container. 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. */ + source = buf; + if (source == NULL && rb->src_state != NULL) + SVN_ERR(read_container_window(&source, rb->src_state, + window->sview_len, pool, iterpool)); + + /* Combine this window with the current one. */ + new_pool = svn_pool_create(rb->scratch_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->current == rs->size) + && svn_fs_x__is_revision(rs->rep_id.change_set)) + SVN_ERR(set_cached_combined_window(buf, rs, new_pool)); + + rs->chunk_index++; + + /* Cycle pools so that we only need to hold three windows at a time. */ + svn_pool_destroy(pool); + pool = new_pool; + } + svn_pool_destroy(iterpool); + + 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(svn_fs_x__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) +{ + rep_read_baton_t *rb = baton; + + svn_pool_destroy(rb->scratch_pool); + svn_pool_destroy(rb->filehandle_pool); + + return SVN_NO_ERROR; +} + +/* Inialize the representation read state RS for the given REP_HEADER and + * p2l index ENTRY. If not NULL, assign FILE and STREAM to RS. + * Allocate all sub-structures of RS in RESULT_POOL. + */ +static svn_error_t * +init_rep_state(rep_state_t *rs, + svn_fs_x__rep_header_t *rep_header, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_pool_t *result_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + shared_file_t *shared_file = apr_pcalloc(result_pool, sizeof(*shared_file)); + + /* this function does not apply to representation containers */ + SVN_ERR_ASSERT(entry->type >= SVN_FS_X__ITEM_TYPE_FILE_REP + && entry->type <= SVN_FS_X__ITEM_TYPE_DIR_PROPS); + SVN_ERR_ASSERT(entry->item_count == 1); + + shared_file->rfile = rev_file; + shared_file->fs = fs; + shared_file->revision = svn_fs_x__get_revnum(entry->items[0].change_set); + shared_file->pool = result_pool; + + rs->sfile = shared_file; + rs->rep_id = entry->items[0]; + rs->header_size = rep_header->header_size; + rs->start = entry->offset + rs->header_size; + rs->current = 4; + rs->size = entry->size - rep_header->header_size - 7; + rs->ver = 1; + rs->chunk_index = 0; + rs->window_cache = ffd->txdelta_window_cache; + rs->combined_cache = ffd->combined_window_cache; + + return SVN_NO_ERROR; +} + +/* Walk through all windows in the representation addressed by RS in FS + * (excluding the delta bases) and put those not already cached into the + * window caches. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. As a side effect, return the total sum of all + * expanded window sizes in *FULLTEXT_LEN. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +cache_windows(svn_filesize_t *fulltext_len, + svn_fs_t *fs, + rep_state_t *rs, + apr_off_t max_offset, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + *fulltext_len = 0; + + while (rs->current < rs->size) + { + svn_boolean_t is_cached = FALSE; + window_sizes_t *window_sizes; + + svn_pool_clear(iterpool); + if (max_offset != -1 && rs->start + rs->current >= max_offset) + { + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; + } + + /* efficiently skip windows that are still being cached instead + * of fully decoding them */ + SVN_ERR(get_cached_window_sizes(&window_sizes, rs, &is_cached, + iterpool)); + if (is_cached) + { + *fulltext_len += window_sizes->target_len; + rs->current += window_sizes->packed_len; + } + else + { + svn_txdelta_window_t *window; + apr_off_t start_offset = rs->start + rs->current; + apr_off_t end_offset; + apr_off_t block_start; + + /* navigate to & read the current window */ + SVN_ERR(rs_aligned_seek(rs, &block_start, start_offset, iterpool)); + SVN_ERR(svn_txdelta_read_svndiff_window(&window, + rs->sfile->rfile->stream, + rs->ver, iterpool)); + + /* aggregate expanded window size */ + *fulltext_len += window->tview_len; + + /* determine on-disk window size */ + SVN_ERR(svn_fs_x__get_file_offset(&end_offset, + rs->sfile->rfile->file, + iterpool)); + rs->current = end_offset - rs->start; + if (rs->current > rs->size) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Reading one svndiff window read beyond " + "the end of the representation")); + + /* if the window has not been cached before, cache it now + * (if caching is used for them at all) */ + if (!is_cached) + SVN_ERR(set_cached_window(window, rs, start_offset, iterpool)); + } + + rs->chunk_index++; + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Try to get the representation header identified by KEY from FS's cache. + * If it has not been cached, read it from the current position in STREAM + * and put it into the cache (if caching has been enabled for rep headers). + * Return the result in *REP_HEADER. Use POOL for allocations. + */ +static svn_error_t * +read_rep_header(svn_fs_x__rep_header_t **rep_header, + svn_fs_t *fs, + svn_stream_t *stream, + svn_fs_x__representation_cache_key_t *key, + apr_pool_t *pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + + if (ffd->rep_header_cache) + { + SVN_ERR(svn_cache__get((void**)rep_header, &is_cached, + ffd->rep_header_cache, key, pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(svn_fs_x__read_rep_header(rep_header, stream, pool, pool)); + + if (ffd->rep_header_cache) + SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_representation_length(svn_filesize_t *packed_len, + svn_filesize_t *expanded_len, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_cache_key_t key = { 0 }; + rep_state_t rs = { 0 }; + svn_fs_x__rep_header_t *rep_header; + + /* this function does not apply to representation containers */ + SVN_ERR_ASSERT(entry->type >= SVN_FS_X__ITEM_TYPE_FILE_REP + && entry->type <= SVN_FS_X__ITEM_TYPE_DIR_PROPS); + SVN_ERR_ASSERT(entry->item_count == 1); + + /* get / read the representation header */ + key.revision = svn_fs_x__get_revnum(entry->items[0].change_set); + key.is_packed = svn_fs_x__is_packed_rev(fs, key.revision); + key.item_index = entry->items[0].number; + SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &key, + scratch_pool)); + + /* prepare representation reader state (rs) structure */ + SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, + scratch_pool)); + + /* RS->SFILE may be shared between RS instances -> make sure we point + * to the right data. */ + *packed_len = rs.size; + SVN_ERR(cache_windows(expanded_len, fs, &rs, -1, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Return the next *LEN bytes of the rep from our plain / delta windows + and store them in *BUF. */ +static svn_error_t * +get_contents_from_windows(rep_read_baton_t *rb, + char *buf, + apr_size_t *len) +{ + apr_size_t copy_len, remaining = *len; + char *cur = buf; + rep_state_t *rs; + + /* Special case for when there are no delta reps, only a + containered text. */ + if (rb->rs_list->nelts == 0 && rb->buf == NULL) + { + copy_len = remaining; + rs = rb->src_state; + + /* reps in containers don't have a header */ + if (rs->header_size == 0 && rb->base_window == NULL) + { + /* RS->SIZE is unreliable here because it is based upon + * the delta rep size _before_ putting the data into a + * a container. */ + SVN_ERR(read_container_window(&rb->base_window, rs, rb->len, + rb->scratch_pool, rb->scratch_pool)); + rs->current -= rb->base_window->len; + } + + 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->current; + 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); + } + + rs->current += 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->scratch_pool); + rb->buf = NULL; + } + } + else + { + svn_stringbuf_t *sbuf = NULL; + + rs = APR_ARRAY_IDX(rb->rs_list, 0, rep_state_t *); + if (rs->current == rs->size) + 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 type for get_fulltext_partial. */ +typedef struct fulltext_baton_t +{ + /* Target buffer to write to; of at least LEN bytes. */ + char *buffer; + + /* Offset within the respective fulltext at which we shall start to + copy data into BUFFER. */ + apr_size_t start; + + /* Number of bytes to copy. The actual amount may be less in case + the fulltext is short(er). */ + apr_size_t len; + + /* Number of bytes actually copied into BUFFER. */ + apr_size_t read; +} fulltext_baton_t; + +/* Implement svn_cache__partial_getter_func_t for fulltext caches. + * From the fulltext in DATA, we copy the range specified by the + * fulltext_baton_t* BATON into the buffer provided by that baton. + * OUT and RESULT_POOL are not used. + */ +static svn_error_t * +get_fulltext_partial(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + fulltext_baton_t *fulltext_baton = baton; + + /* We cached the fulltext with an NUL appended to it. */ + apr_size_t fulltext_len = data_len - 1; + + /* Clip the copy range to what the fulltext size allows. */ + apr_size_t start = MIN(fulltext_baton->start, fulltext_len); + fulltext_baton->read = MIN(fulltext_len - start, fulltext_baton->len); + + /* Copy the data to the output buffer and be done. */ + memcpy(fulltext_baton->buffer, (const char *)data + start, + fulltext_baton->read); + + return SVN_NO_ERROR; +} + +/* Find the fulltext specified in BATON in the fulltext cache given + * as well by BATON. If that succeeds, set *CACHED to TRUE and copy + * up to the next *LEN bytes into BUFFER. Set *LEN to the actual + * number of bytes copied. + */ +static svn_error_t * +get_contents_from_fulltext(svn_boolean_t *cached, + rep_read_baton_t *baton, + char *buffer, + apr_size_t *len) +{ + void *dummy; + fulltext_baton_t fulltext_baton; + + SVN_ERR_ASSERT((apr_size_t)baton->fulltext_delivered + == baton->fulltext_delivered); + fulltext_baton.buffer = buffer; + fulltext_baton.start = (apr_size_t)baton->fulltext_delivered; + fulltext_baton.len = *len; + fulltext_baton.read = 0; + + SVN_ERR(svn_cache__get_partial(&dummy, cached, baton->fulltext_cache, + &baton->fulltext_cache_key, + get_fulltext_partial, &fulltext_baton, + baton->scratch_pool)); + + if (*cached) + { + baton->fulltext_delivered += fulltext_baton.read; + *len = fulltext_baton.read; + } + + return SVN_NO_ERROR; +} + +/* Determine the optimal size of a string buf that shall receive a + * (full-) text of NEEDED bytes. + * + * The critical point is that those buffers may be very large and + * can cause memory fragmentation. We apply simple heuristics to + * make fragmentation less likely. + */ +static apr_size_t +optimimal_allocation_size(apr_size_t needed) +{ + /* For all allocations, assume some overhead that is shared between + * OS memory managemnt, APR memory management and svn_stringbuf_t. */ + const apr_size_t overhead = 0x400; + apr_size_t optimal; + + /* If an allocation size if safe for other ephemeral buffers, it should + * be safe for ours. */ + if (needed <= SVN__STREAM_CHUNK_SIZE) + return needed; + + /* Paranoia edge case: + * Skip our heuristics if they created arithmetical overflow. + * Beware to make this test work for NEEDED = APR_SIZE_MAX as well! */ + if (needed >= APR_SIZE_MAX / 2 - overhead) + return needed; + + /* As per definition SVN__STREAM_CHUNK_SIZE is a power of two. + * Since we know NEEDED to be larger than that, use it as the + * starting point. + * + * Heuristics: Allocate a power-of-two number of bytes that fit + * NEEDED plus some OVERHEAD. The APR allocator + * will round it up to the next full page size. + */ + optimal = SVN__STREAM_CHUNK_SIZE; + while (optimal - overhead < needed) + optimal *= 2; + + /* This is above or equal to NEEDED. */ + return optimal - overhead; +} + +/* After a fulltext cache lookup failure, we will continue to read from + * combined delta or plain windows. However, we must first make that data + * stream in BATON catch up tho the position LEN already delivered from the + * fulltext cache. Also, we need to store the reconstructed fulltext if we + * want to cache it at the end. + */ +static svn_error_t * +skip_contents(rep_read_baton_t *baton, + svn_filesize_t len) +{ + svn_error_t *err = SVN_NO_ERROR; + + /* Do we want to cache the reconstructed fulltext? */ + if (SVN_IS_VALID_REVNUM(baton->fulltext_cache_key.revision)) + { + char *buffer; + svn_filesize_t to_alloc = MAX(len, baton->len); + + /* This should only be happening if BATON->LEN and LEN are + * cacheable, implying they fit into memory. */ + SVN_ERR_ASSERT((apr_size_t)to_alloc == to_alloc); + + /* Allocate the fulltext buffer. */ + baton->current_fulltext = svn_stringbuf_create_ensure( + optimimal_allocation_size((apr_size_t)to_alloc), + baton->filehandle_pool); + + /* Read LEN bytes from the window stream and store the data + * in the fulltext buffer (will be filled by further reads later). */ + baton->current_fulltext->len = (apr_size_t)len; + baton->current_fulltext->data[(apr_size_t)len] = 0; + + buffer = baton->current_fulltext->data; + while (len > 0 && !err) + { + apr_size_t to_read = (apr_size_t)len; + err = get_contents_from_windows(baton, buffer, &to_read); + len -= to_read; + buffer += to_read; + } + } + else if (len > 0) + { + /* Simply drain LEN bytes from the window stream. */ + apr_pool_t *subpool = svn_pool_create(baton->scratch_pool); + char *buffer = apr_palloc(subpool, SVN__STREAM_CHUNK_SIZE); + + while (len > 0 && !err) + { + apr_size_t to_read = len > SVN__STREAM_CHUNK_SIZE + ? SVN__STREAM_CHUNK_SIZE + : (apr_size_t)len; + + err = get_contents_from_windows(baton, buffer, &to_read); + len -= to_read; + } + + svn_pool_destroy(subpool); + } + + return svn_error_trace(err); +} + +/* BATON is of type `rep_read_baton_t'; 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) +{ + rep_read_baton_t *rb = baton; + + /* Get data from the fulltext cache for as long as we can. */ + if (rb->fulltext_cache) + { + svn_boolean_t cached; + SVN_ERR(get_contents_from_fulltext(&cached, rb, buf, len)); + if (cached) + return SVN_NO_ERROR; + + /* Cache miss. From now on, we will never read from the fulltext + * cache for this representation anymore. */ + rb->fulltext_cache = NULL; + } + + /* No fulltext cache to help us. We must read from the window stream. */ + if (!rb->rs_list) + { + /* Window stream not initialized, yet. Do it now. */ + SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window, + &rb->src_state, rb->fs, &rb->rep, + rb->filehandle_pool, rb->scratch_pool)); + + /* In case we did read from the fulltext cache before, make the + * window stream catch up. Also, initialize the fulltext buffer + * if we want to cache the fulltext at the end. */ + SVN_ERR(skip_contents(rb, rb->fulltext_delivered)); + } + + /* Get the next block of data. */ + SVN_ERR(get_contents_from_windows(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; + svn_checksum_t expected; + expected.kind = svn_checksum_md5; + expected.digest = rb->md5_digest; + + rb->checksum_finalized = TRUE; + SVN_ERR(svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx, + rb->scratch_pool)); + if (!svn_checksum_match(md5_checksum, &expected)) + return svn_error_create(SVN_ERR_FS_CORRUPT, + svn_checksum_mismatch_err(&expected, md5_checksum, + rb->scratch_pool, + _("Checksum mismatch while reading representation")), + NULL); + } + } + + if (rb->off == rb->len && rb->current_fulltext) + { + svn_fs_x__data_t *ffd = rb->fs->fsap_data; + SVN_ERR(svn_cache__set(ffd->fulltext_cache, &rb->fulltext_cache_key, + rb->current_fulltext, rb->scratch_pool)); + rb->current_fulltext = NULL; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_contents(svn_stream_t **contents_p, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + svn_boolean_t cache_fulltext, + apr_pool_t *result_pool) +{ + if (! rep) + { + *contents_p = svn_stream_empty(result_pool); + } + else + { + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_filesize_t len = rep->expanded_size; + rep_read_baton_t *rb; + svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + + svn_fs_x__pair_cache_key_t fulltext_cache_key = { 0 }; + fulltext_cache_key.revision = revision; + fulltext_cache_key.second = rep->id.number; + + /* Initialize the reader baton. Some members may added lazily + * while reading from the stream */ + SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, + result_pool)); + + /* Make the stream attempt fulltext cache lookups if the fulltext + * is cacheable. If it is not, then also don't try to buffer and + * cache it. */ + if (ffd->fulltext_cache && cache_fulltext + && SVN_IS_VALID_REVNUM(revision) + && fulltext_size_is_cachable(ffd, len)) + { + rb->fulltext_cache = ffd->fulltext_cache; + } + else + { + /* This will also prevent the reconstructed fulltext from being + put into the cache. */ + rb->fulltext_cache_key.revision = SVN_INVALID_REVNUM; + } + + *contents_p = svn_stream_create(rb, result_pool); + svn_stream_set_read2(*contents_p, NULL /* only full read support */, + rep_read_contents); + svn_stream_set_close(*contents_p, rep_read_contents_close); + } + + return SVN_NO_ERROR; +} + + +/* Baton for cache_access_wrapper. Wraps the original parameters of + * svn_fs_x__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_x__try_process_file_contents(svn_boolean_t *success, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_t *rep = noderev->data_rep; + if (rep) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__pair_cache_key_t fulltext_cache_key = { 0 }; + + fulltext_cache_key.revision = svn_fs_x__get_revnum(rep->id.change_set); + fulltext_cache_key.second = rep->id.number; + if (ffd->fulltext_cache + && SVN_IS_VALID_REVNUM(fulltext_cache_key.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, + scratch_pool); + } + } + + *success = FALSE; + return SVN_NO_ERROR; +} + +/* Baton used when reading delta windows. */ +typedef struct delta_read_baton_t +{ + struct rep_state_t *rs; + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; +} delta_read_baton_t; + +/* 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) +{ + delta_read_baton_t *drb = baton; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + *window = NULL; + if (drb->rs->current < drb->rs->size) + { + SVN_ERR(read_delta_window(window, drb->rs->chunk_index, drb->rs, pool, + scratch_pool)); + drb->rs->chunk_index++; + } + + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +/* This implements the svn_txdelta_md5_digest_fn_t interface. */ +static const unsigned char * +delta_read_md5_digest(void *baton) +{ + delta_read_baton_t *drb = baton; + return drb->md5_digest; +} + +/* Return a txdelta stream for on-disk representation REP_STATE + * of TARGET. Allocate the result in RESULT_POOL. + */ +static svn_txdelta_stream_t * +get_storaged_delta_stream(rep_state_t *rep_state, + svn_fs_x__noderev_t *target, + apr_pool_t *result_pool) +{ + /* Create the delta read baton. */ + delta_read_baton_t *drb = apr_pcalloc(result_pool, sizeof(*drb)); + drb->rs = rep_state; + memcpy(drb->md5_digest, target->data_rep->md5_digest, + sizeof(drb->md5_digest)); + return svn_txdelta_stream_create(drb, delta_read_next_window, + delta_read_md5_digest, result_pool); +} + +svn_error_t * +svn_fs_x__get_file_delta_stream(svn_txdelta_stream_t **stream_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *source, + svn_fs_x__noderev_t *target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *source_stream, *target_stream; + rep_state_t *rep_state; + svn_fs_x__rep_header_t *rep_header; + svn_fs_x__data_t *ffd = fs->fsap_data; + + /* Try a shortcut: if the target is stored as a delta against the source, + then just use that delta. However, prefer using the fulltext cache + whenever that is available. */ + if (target->data_rep && (source || !ffd->fulltext_cache)) + { + /* Read target's base rep if any. */ + SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL, + target->data_rep, fs, result_pool, + scratch_pool)); + + /* 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) + { + /* 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_header->type == svn_fs_x__rep_delta + && rep_header->base_revision + == svn_fs_x__get_revnum(source->data_rep->id.change_set) + && rep_header->base_item_index == source->data_rep->id.number) + { + *stream_p = get_storaged_delta_stream(rep_state, target, + result_pool); + return SVN_NO_ERROR; + } + } + else if (!source) + { + /* We want a self-delta. There is a fair chance that TARGET got + added in this revision and is already stored in the requested + format. */ + if (rep_header->type == svn_fs_x__rep_self_delta) + { + *stream_p = get_storaged_delta_stream(rep_state, target, + result_pool); + return SVN_NO_ERROR; + } + } + + /* Don't keep file handles open for longer than necessary. */ + if (rep_state->sfile->rfile) + { + SVN_ERR(svn_fs_x__close_revision_file(rep_state->sfile->rfile)); + rep_state->sfile->rfile = NULL; + } + } + + /* Read both fulltexts and construct a delta. */ + if (source) + SVN_ERR(svn_fs_x__get_contents(&source_stream, fs, source->data_rep, + TRUE, result_pool)); + else + source_stream = svn_stream_empty(result_pool); + + SVN_ERR(svn_fs_x__get_contents(&target_stream, fs, target->data_rep, + TRUE, result_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, result_pool); + + return SVN_NO_ERROR; +} + +/* Return TRUE when all svn_fs_x__dirent_t* in ENTRIES are already sorted + by their respective name. */ +static svn_boolean_t +sorted(apr_array_header_t *entries) +{ + int i; + + const svn_fs_x__dirent_t * const *dirents = (const void *)entries->elts; + for (i = 0; i < entries->nelts-1; ++i) + if (strcmp(dirents[i]->name, dirents[i+1]->name) > 0) + return FALSE; + + return TRUE; +} + +/* Compare the names of the two dirents given in **A and **B. */ +static int +compare_dirents(const void *a, + const void *b) +{ + const svn_fs_x__dirent_t *lhs = *((const svn_fs_x__dirent_t * const *) a); + const svn_fs_x__dirent_t *rhs = *((const svn_fs_x__dirent_t * const *) b); + + return strcmp(lhs->name, rhs->name); +} + +/* Compare the name of the dirents given in **A with the C string in *B. */ +static int +compare_dirent_name(const void *a, + const void *b) +{ + const svn_fs_x__dirent_t *lhs = *((const svn_fs_x__dirent_t * const *) a); + const char *rhs = b; + + return strcmp(lhs->name, rhs); +} + +/* Into ENTRIES, read all directories entries from the key-value text in + * STREAM. If INCREMENTAL is TRUE, read until the end of the STREAM and + * update the data. ID is provided for nicer error messages. + */ +static svn_error_t * +read_dir_entries(apr_array_header_t *entries, + svn_stream_t *stream, + svn_boolean_t incremental, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *hash = incremental ? svn_hash__make(scratch_pool) : NULL; + const char *terminator = SVN_HASH_TERMINATOR; + + /* Read until the terminator (non-incremental) or the end of STREAM + (incremental mode). In the latter mode, we use a temporary HASH + to make updating and removing entries cheaper. */ + while (1) + { + svn_hash__entry_t entry; + svn_fs_x__dirent_t *dirent; + char *str; + + svn_pool_clear(iterpool); + SVN_ERR(svn_hash__read_entry(&entry, stream, terminator, + incremental, iterpool)); + + /* End of directory? */ + if (entry.key == NULL) + { + /* In incremental mode, we skip the terminator and read the + increments following it until the end of the stream. */ + if (incremental && terminator) + terminator = NULL; + else + break; + } + + /* Deleted entry? */ + if (entry.val == NULL) + { + /* We must be in incremental mode */ + assert(hash); + apr_hash_set(hash, entry.key, entry.keylen, NULL); + continue; + } + + /* Add a new directory entry. */ + dirent = apr_pcalloc(result_pool, sizeof(*dirent)); + dirent->name = apr_pstrmemdup(result_pool, entry.key, entry.keylen); + + str = svn_cstring_tokenize(" ", &entry.val); + if (str == NULL) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_x__id_unparse(id, scratch_pool)->data); + + if (strcmp(str, SVN_FS_X__KIND_FILE) == 0) + { + dirent->kind = svn_node_file; + } + else if (strcmp(str, SVN_FS_X__KIND_DIR) == 0) + { + dirent->kind = svn_node_dir; + } + else + { + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_x__id_unparse(id, scratch_pool)->data); + } + + str = svn_cstring_tokenize(" ", &entry.val); + if (str == NULL) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Directory entry corrupt in '%s'"), + svn_fs_x__id_unparse(id, scratch_pool)->data); + + SVN_ERR(svn_fs_x__id_parse(&dirent->id, str)); + + /* In incremental mode, update the hash; otherwise, write to the + * final array. */ + if (incremental) + apr_hash_set(hash, dirent->name, entry.keylen, dirent); + else + APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = dirent; + } + + /* Convert container to a sorted array. */ + if (incremental) + { + apr_hash_index_t *hi; + for (hi = apr_hash_first(iterpool, hash); hi; hi = apr_hash_next(hi)) + APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = apr_hash_this_val(hi); + } + + if (!sorted(entries)) + svn_sort__array(entries, compare_dirents); + + svn_pool_destroy(iterpool); + + 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_x__dirent_t values. */ +static svn_error_t * +get_dir_contents(apr_array_header_t **entries, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *contents; + const svn_fs_x__id_t *id = &noderev->noderev_id; + + *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_x__dirent_t *)); + if (noderev->data_rep + && ! svn_fs_x__is_revision(noderev->data_rep->id.change_set)) + { + const char *filename + = svn_fs_x__path_txn_node_children(fs, id, scratch_pool, + scratch_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, scratch_pool, + scratch_pool)); + SVN_ERR(read_dir_entries(*entries, contents, TRUE, id, + result_pool, scratch_pool)); + SVN_ERR(svn_stream_close(contents)); + } + else if (noderev->data_rep) + { + /* Undeltify content before parsing it. Otherwise, we could only + * parse it byte-by-byte. + */ + apr_size_t len = noderev->data_rep->expanded_size; + svn_stringbuf_t *text; + + /* The representation is immutable. Read it normally. */ + SVN_ERR(svn_fs_x__get_contents(&contents, fs, noderev->data_rep, + FALSE, scratch_pool)); + SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, scratch_pool)); + SVN_ERR(svn_stream_close(contents)); + + /* de-serialize hash */ + contents = svn_stream_from_stringbuf(text, scratch_pool); + SVN_ERR(read_dir_entries(*entries, contents, FALSE, id, + result_pool, scratch_pool)); + } + + return SVN_NO_ERROR; +} + + +/* Return the cache object in FS responsible to storing the directory the + * NODEREV plus the corresponding pre-allocated *KEY. + */ +static svn_cache__t * +locate_dir_cache(svn_fs_t *fs, + svn_fs_x__id_t *key, + svn_fs_x__noderev_t *noderev) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + if (svn_fs_x__is_txn(noderev->noderev_id.change_set)) + { + /* data in txns must be addressed by ID since the representation has + not been created, yet. */ + *key = noderev->noderev_id; + } + else + { + /* committed data can use simple rev,item pairs */ + if (noderev->data_rep) + { + *key = noderev->data_rep->id; + } + else + { + /* no data rep -> empty directory. + Use a key that does definitely not clash with non-NULL reps. */ + key->change_set = SVN_FS_X__INVALID_CHANGE_SET; + key->number = SVN_FS_X__ITEM_INDEX_UNUSED; + } + } + + return ffd->dir_cache; +} + +svn_error_t * +svn_fs_x__rep_contents_dir(apr_array_header_t **entries_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__id_t key; + + /* find the cache we may use */ + svn_cache__t *cache = locate_dir_cache(fs, &key, noderev); + if (cache) + { + svn_boolean_t found; + + SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, &key, + result_pool)); + if (found) + return SVN_NO_ERROR; + } + + /* Read in the directory contents. */ + SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool, + scratch_pool)); + + /* Update the cache, if we are to use one. */ + if (cache) + SVN_ERR(svn_cache__set(cache, &key, *entries_p, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_fs_x__dirent_t * +svn_fs_x__find_dir_entry(apr_array_header_t *entries, + const char *name, + int *hint) +{ + svn_fs_x__dirent_t **result + = svn_sort__array_lookup(entries, name, hint, compare_dirent_name); + return result ? *result : NULL; +} + +svn_error_t * +svn_fs_x__rep_contents_dir_entry(svn_fs_x__dirent_t **dirent, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + const char *name, + apr_size_t *hint, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t found = FALSE; + + /* find the cache we may use */ + svn_fs_x__id_t key; + svn_cache__t *cache = locate_dir_cache(fs, &key, noderev); + if (cache) + { + svn_fs_x__ede_baton_t baton; + baton.hint = *hint; + baton.name = name; + + /* Cache lookup. */ + SVN_ERR(svn_cache__get_partial((void **)dirent, + &found, + cache, + &key, + svn_fs_x__extract_dir_entry, + &baton, + result_pool)); + + /* Remember the new clue only if we found something at that spot. */ + if (found) + *hint = baton.hint; + } + + /* fetch data from disk if we did not find it in the cache */ + if (! found) + { + apr_array_header_t *entries; + svn_fs_x__dirent_t *entry; + svn_fs_x__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_x__rep_contents_dir(&entries, fs, noderev, + scratch_pool, scratch_pool)); + + /* find desired entry and return a copy in POOL, if found */ + entry = svn_fs_x__find_dir_entry(entries, name, NULL); + if (entry) + { + entry_copy = apr_pmemdup(result_pool, entry, sizeof(*entry_copy)); + entry_copy->name = apr_pstrdup(result_pool, entry->name); + } + + *dirent = entry_copy; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *proplist; + svn_stream_t *stream; + const svn_fs_x__id_t *noderev_id = &noderev->noderev_id; + + if (noderev->prop_rep + && !svn_fs_x__is_revision(noderev->prop_rep->id.change_set)) + { + const char *filename = svn_fs_x__path_txn_node_props(fs, noderev_id, + scratch_pool, + scratch_pool); + proplist = apr_hash_make(result_pool); + + SVN_ERR(svn_stream_open_readonly(&stream, filename, scratch_pool, + scratch_pool)); + SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, + result_pool)); + SVN_ERR(svn_stream_close(stream)); + } + else if (noderev->prop_rep) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__representation_t *rep = noderev->prop_rep; + svn_fs_x__pair_cache_key_t key = { 0 }; + + key.revision = svn_fs_x__get_revnum(rep->id.change_set); + key.second = rep->id.number; + if (ffd->properties_cache && SVN_IS_VALID_REVNUM(key.revision)) + { + svn_boolean_t is_cached; + SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached, + ffd->properties_cache, &key, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + proplist = apr_hash_make(result_pool); + SVN_ERR(svn_fs_x__get_contents(&stream, fs, noderev->prop_rep, FALSE, + scratch_pool)); + SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, + result_pool)); + SVN_ERR(svn_stream_close(stream)); + + if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->id.change_set)) + SVN_ERR(svn_cache__set(ffd->properties_cache, &key, proplist, + scratch_pool)); + } + else + { + /* return an empty prop list if the node doesn't have any props */ + proplist = apr_hash_make(result_pool); + } + + *proplist_p = proplist; + + return SVN_NO_ERROR; +} + + + +svn_error_t * +svn_fs_x__get_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + svn_fs_x__revision_file_t *revision_file; + svn_boolean_t found; + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + + svn_fs_x__id_t id; + id.change_set = svn_fs_x__change_set_by_rev(rev); + id.number = SVN_FS_X__ITEM_INDEX_CHANGES; + + /* Provide revision file. */ + + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, rev, + scratch_pool, scratch_pool)); + + /* try cache lookup first */ + + if (ffd->changes_container_cache && svn_fs_x__is_packed_rev(fs, rev)) + { + apr_off_t offset; + apr_uint32_t sub_item; + svn_fs_x__pair_cache_key_t key; + + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, revision_file, + &id, scratch_pool)); + key.revision = svn_fs_x__packed_base_rev(fs, rev); + key.second = offset; + + SVN_ERR(svn_cache__get_partial((void **)changes, &found, + ffd->changes_container_cache, &key, + svn_fs_x__changes_get_list_func, + &sub_item, result_pool)); + } + else if (ffd->changes_cache) + { + SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache, + &rev, result_pool)); + } + else + { + found = FALSE; + } + + if (!found) + { + /* 'block-read' will also provide us with the desired data */ + SVN_ERR(block_read((void **)changes, fs, &id, revision_file, + result_pool, scratch_pool)); + + SVN_ERR(svn_fs_x__close_revision_file(revision_file)); + } + + SVN_ERR(dgb__log_access(fs, &id, *changes, SVN_FS_X__ITEM_TYPE_CHANGES, + scratch_pool)); + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + +/* Fetch the representation data (header, txdelta / plain windows) + * addressed by ENTRY->ITEM in FS and cache it if caches are enabled. + * Read the data from the already open FILE and the wrapping + * STREAM object. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +block_read_contents(svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + svn_fs_x__pair_cache_key_t *key, + apr_off_t max_offset, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__representation_cache_key_t header_key = { 0 }; + rep_state_t rs = { 0 }; + svn_filesize_t fulltext_len; + svn_fs_x__rep_header_t *rep_header; + + if (!ffd->txdelta_window_cache || !ffd->combined_window_cache) + return SVN_NO_ERROR; + + header_key.revision = (apr_int32_t)key->revision; + header_key.is_packed = svn_fs_x__is_packed_rev(fs, header_key.revision); + header_key.item_index = key->second; + + SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key, + scratch_pool)); + SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, scratch_pool)); + SVN_ERR(cache_windows(&fulltext_len, fs, &rs, max_offset, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* For the given REV_FILE in FS, in *STREAM return a stream covering the + * item specified by ENTRY. Also, verify the item's content by low-level + * checksum. Allocate the result in POOL. + */ +static svn_error_t * +read_item(svn_stream_t **stream, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_pool_t *pool) +{ + apr_uint32_t digest; + svn_checksum_t *expected, *actual; + apr_uint32_t plain_digest; + + /* Read item into string buffer. */ + svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool); + text->len = entry->size; + text->data[text->len] = 0; + SVN_ERR(svn_io_file_read_full2(rev_file->file, text->data, text->len, + NULL, NULL, pool)); + + /* Return (construct, calculate) stream and checksum. */ + *stream = svn_stream_from_stringbuf(text, pool); + digest = svn__fnv1a_32x4(text->data, text->len); + + /* Checksums will match most of the time. */ + if (entry->fnv1_checksum == digest) + return SVN_NO_ERROR; + + /* Construct proper checksum objects from their digests to allow for + * nice error messages. */ + plain_digest = htonl(entry->fnv1_checksum); + expected = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + plain_digest = htonl(digest); + actual = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + + /* Construct the full error message with all the info we have. */ + return svn_checksum_mismatch_err(expected, actual, pool, + _("Low-level checksum mismatch while reading\n" + "%s bytes of meta data at offset %s "), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->size), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->offset)); +} + +/* Read all txdelta / plain windows following REP_HEADER in FS as described + * by ENTRY. Read the data from the already open FILE and the wrapping + * STREAM object. If MAX_OFFSET is not -1, don't read windows that start + * at or beyond that offset. Use SCRATCH_POOL for temporary allocations. + * If caching is not enabled, this is a no-op. + */ +static svn_error_t * +block_read_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_stream_t *stream; + svn_revnum_t revision = svn_fs_x__get_revnum(entry->items[0].change_set); + if (!must_read && !ffd->changes_cache) + return SVN_NO_ERROR; + + /* we don't support containers, yet */ + SVN_ERR_ASSERT(entry->item_count == 1); + + /* already in cache? */ + if (!must_read && ffd->changes_cache) + { + svn_boolean_t is_cached = FALSE; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache, &revision, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read changes from revision file */ + + SVN_ERR(svn_fs_x__read_changes(changes, stream, result_pool, scratch_pool)); + + /* cache for future reference */ + + if (ffd->changes_cache) + { + /* Guesstimate for the size of the in-cache representation. */ + apr_size_t estimated_size = (apr_size_t)250 * (*changes)->nelts; + + /* Don't even serialize data that probably won't fit into the + * cache. This often implies that either CHANGES is very + * large, memory is scarce or both. Having a huge temporary + * copy would not be a good thing in either case. */ + if (svn_cache__is_cachable(ffd->changes_cache, estimated_size)) + SVN_ERR(svn_cache__set(ffd->changes_cache, &revision, *changes, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +block_read_changes_container(apr_array_header_t **changes, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_uint32_t sub_item, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__changes_t *container; + svn_fs_x__pair_cache_key_t key; + svn_stream_t *stream; + svn_revnum_t revision = svn_fs_x__get_revnum(entry->items[0].change_set); + + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = entry->offset; + + /* already in cache? */ + if (!must_read && ffd->changes_container_cache) + { + svn_boolean_t is_cached = FALSE; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_container_cache, + &key, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read changes from revision file */ + + SVN_ERR(svn_fs_x__read_changes_container(&container, stream, scratch_pool, + scratch_pool)); + + /* extract requested data */ + + if (must_read) + SVN_ERR(svn_fs_x__changes_get_list(changes, container, sub_item, + result_pool)); + + if (ffd->changes_container_cache) + SVN_ERR(svn_cache__set(ffd->changes_container_cache, &key, container, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +block_read_noderev(svn_fs_x__noderev_t **noderev_p, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + svn_fs_x__pair_cache_key_t *key, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_stream_t *stream; + if (!must_read && !ffd->node_revision_cache) + return SVN_NO_ERROR; + + /* we don't support containers, yet */ + SVN_ERR_ASSERT(entry->item_count == 1); + + /* already in cache? */ + if (!must_read && ffd->node_revision_cache) + { + svn_boolean_t is_cached = FALSE; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->node_revision_cache, key, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read node rev from revision file */ + + SVN_ERR(svn_fs_x__read_noderev(noderev_p, stream, result_pool, + scratch_pool)); + if (ffd->node_revision_cache) + SVN_ERR(svn_cache__set(ffd->node_revision_cache, key, *noderev_p, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +block_read_noderevs_container(svn_fs_x__noderev_t **noderev_p, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_uint32_t sub_item, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__noderevs_t *container; + svn_stream_t *stream; + svn_fs_x__pair_cache_key_t key; + svn_revnum_t revision = svn_fs_x__get_revnum(entry->items[0].change_set); + + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = entry->offset; + + /* already in cache? */ + if (!must_read && ffd->noderevs_container_cache) + { + svn_boolean_t is_cached = FALSE; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->noderevs_container_cache, + &key, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read noderevs from revision file */ + SVN_ERR(svn_fs_x__read_noderevs_container(&container, stream, scratch_pool, + scratch_pool)); + + /* extract requested data */ + if (must_read) + SVN_ERR(svn_fs_x__noderevs_get(noderev_p, container, sub_item, + result_pool)); + + if (ffd->noderevs_container_cache) + SVN_ERR(svn_cache__set(ffd->noderevs_container_cache, &key, container, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +block_read_reps_container(svn_fs_x__rep_extractor_t **extractor, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_uint32_t sub_item, + svn_boolean_t must_read, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__reps_t *container; + svn_stream_t *stream; + svn_fs_x__pair_cache_key_t key; + svn_revnum_t revision = svn_fs_x__get_revnum(entry->items[0].change_set); + + key.revision = svn_fs_x__packed_base_rev(fs, revision); + key.second = entry->offset; + + /* already in cache? */ + if (!must_read && ffd->reps_container_cache) + { + svn_boolean_t is_cached = FALSE; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->reps_container_cache, + &key, scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + } + + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); + + /* read noderevs from revision file */ + SVN_ERR(svn_fs_x__read_reps_container(&container, stream, result_pool, + scratch_pool)); + + /* extract requested data */ + + if (must_read) + SVN_ERR(svn_fs_x__reps_get(extractor, fs, container, sub_item, + result_pool)); + + if (ffd->noderevs_container_cache) + SVN_ERR(svn_cache__set(ffd->reps_container_cache, &key, container, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +block_read(void **result, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + svn_fs_x__revision_file_t *revision_file, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_off_t offset, wanted_offset = 0; + apr_off_t block_start = 0; + apr_uint32_t wanted_sub_item = 0; + svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set); + apr_array_header_t *entries; + int run_count = 0; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* don't try this on transaction protorev files */ + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); + + /* index lookup: find the OFFSET of the item we *must* read plus (in the + * "do-while" block) the list of items in the same block. */ + SVN_ERR(svn_fs_x__item_offset(&wanted_offset, &wanted_sub_item, fs, + revision_file, id, iterpool)); + + offset = wanted_offset; + do + { + /* fetch list of items in the block surrounding OFFSET */ + SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset, + iterpool)); + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, revision_file, + revision, block_start, + ffd->block_size, scratch_pool, + scratch_pool)); + + /* read all items from the block */ + for (i = 0; i < entries->nelts; ++i) + { + svn_boolean_t is_result, is_wanted; + apr_pool_t *pool; + + svn_fs_x__p2l_entry_t* entry + = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t); + + /* skip empty sections */ + if (entry->type == SVN_FS_X__ITEM_TYPE_UNUSED) + continue; + + /* the item / container we were looking for? */ + is_wanted = entry->offset == wanted_offset + && entry->item_count >= wanted_sub_item + && svn_fs_x__id_eq(entry->items + wanted_sub_item, id); + is_result = result && is_wanted; + + /* select the pool that we want the item to be allocated in */ + pool = is_result ? result_pool : iterpool; + + /* handle all items that start within this block and are relatively + * small (i.e. < block size). Always read the item we need to return. + */ + if (is_result || ( entry->offset >= block_start + && entry->size < ffd->block_size)) + { + void *item = NULL; + svn_fs_x__pair_cache_key_t key = { 0 }; + key.revision = svn_fs_x__get_revnum(entry->items[0].change_set); + key.second = entry->items[0].number; + + SVN_ERR(svn_io_file_seek(revision_file->file, SEEK_SET, + &entry->offset, iterpool)); + switch (entry->type) + { + case SVN_FS_X__ITEM_TYPE_FILE_REP: + case SVN_FS_X__ITEM_TYPE_DIR_REP: + case SVN_FS_X__ITEM_TYPE_FILE_PROPS: + case SVN_FS_X__ITEM_TYPE_DIR_PROPS: + SVN_ERR(block_read_contents(fs, revision_file, + entry, &key, + is_wanted + ? -1 + : block_start + ffd->block_size, + iterpool)); + break; + + case SVN_FS_X__ITEM_TYPE_NODEREV: + if (ffd->node_revision_cache || is_result) + SVN_ERR(block_read_noderev((svn_fs_x__noderev_t **)&item, + fs, revision_file, + entry, &key, is_result, + pool, iterpool)); + break; + + case SVN_FS_X__ITEM_TYPE_CHANGES: + SVN_ERR(block_read_changes((apr_array_header_t **)&item, + fs, revision_file, + entry, is_result, + pool, iterpool)); + break; + + case SVN_FS_X__ITEM_TYPE_CHANGES_CONT: + SVN_ERR(block_read_changes_container + ((apr_array_header_t **)&item, + fs, revision_file, + entry, wanted_sub_item, + is_result, pool, iterpool)); + break; + + case SVN_FS_X__ITEM_TYPE_NODEREVS_CONT: + SVN_ERR(block_read_noderevs_container + ((svn_fs_x__noderev_t **)&item, + fs, revision_file, + entry, wanted_sub_item, + is_result, pool, iterpool)); + break; + + case SVN_FS_X__ITEM_TYPE_REPS_CONT: + SVN_ERR(block_read_reps_container + ((svn_fs_x__rep_extractor_t **)&item, + fs, revision_file, + entry, wanted_sub_item, + is_result, pool, iterpool)); + break; + + default: + break; + } + + if (is_result) + *result = item; + + /* if we crossed a block boundary, read the remainder of + * the last block as well */ + offset = entry->offset + entry->size; + if (offset > block_start + ffd->block_size) + ++run_count; + + svn_pool_clear(iterpool); + } + } + } + while(run_count++ == 1); /* can only be true once and only if a block + * boundary got crossed */ + + /* if the caller requested a result, we must have provided one by now */ + assert(!result || *result); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/cached_data.h b/contrib/subversion/subversion/libsvn_fs_x/cached_data.h new file mode 100644 index 000000000..079303eff --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/cached_data.h @@ -0,0 +1,180 @@ +/* cached_data.h --- cached (read) access to FSX data + * + * ==================================================================== + * 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_FS__CACHED_DATA_H +#define SVN_LIBSVN_FS__CACHED_DATA_H + +#include "svn_pools.h" +#include "svn_fs.h" + +#include "fs.h" +#include "index.h" + + + +/* Set *NODEREV_P to the node-revision for the node ID in FS. Do any + allocations in POOL. */ +svn_error_t * +svn_fs_x__get_node_revision(svn_fs_x__noderev_t **noderev_p, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set *COUNT to the value of the mergeinfo_count member of the node- + revision for the node ID in FS. Do temporary allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_x__get_mergeinfo_count(apr_int64_t *count, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *scratch_pool); + +/* Verify that representation REP in FS can be accessed. + Do any allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__check_rep(svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Follow the representation delta chain in FS starting with REP. The + number of reps (including REP) in the chain will be returned in + *CHAIN_LENGTH. *SHARD_COUNT will be set to the number of shards + accessed. Do any allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__rep_chain_length(int *chain_length, + int *shard_count, + svn_fs_x__representation_t *rep, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Set *CONTENTS to be a readable svn_stream_t that receives the text + representation REP as seen in filesystem FS. If CACHE_FULLTEXT is + not set, bypass fulltext cache lookup for this rep and don't put the + reconstructed fulltext into cache. + Allocate *CONTENT_P in RESULT_POOL. */ +svn_error_t * +svn_fs_x__get_contents(svn_stream_t **contents_p, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + svn_boolean_t cache_fulltext, + apr_pool_t *result_pool); + +/* Determine on-disk and expanded sizes of the representation identified + * by ENTRY in FS and return the result in PACKED_LEN and EXPANDED_LEN, + * respectively. FILE must point to the start of the representation and + * STREAM must be a stream defined on top of FILE. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__get_representation_length(svn_filesize_t *packed_len, + svn_filesize_t *expanded_len, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_pool_t *scratch_pool); + +/* Attempt to fetch the text representation of node-revision NODEREV as + seen in filesystem FS and pass it along with the BATON to the PROCESSOR. + Set *SUCCESS only of the data could be provided and the processing + had been called. + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__try_process_file_contents(svn_boolean_t *success, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *scratch_pool); + +/* Set *STREAM_P to a delta stream turning the contents of the file SOURCE + into the contents of the file TARGET, allocated in RESULT_POOL. + If SOURCE is NULL, an empty string will be used in its stead. + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__get_file_delta_stream(svn_txdelta_stream_t **stream_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *source, + svn_fs_x__noderev_t *target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set *ENTRIES to an apr_array_header_t of dirent structs that contain + the directory entries of node-revision NODEREV in filesystem FS. The + returned table is allocated in RESULT_POOL and entries are sorted + lexicographically. SCRATCH_POOL is used for temporary allocations. */ +svn_error_t * +svn_fs_x__rep_contents_dir(apr_array_header_t **entries_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return the directory entry from ENTRIES that matches NAME. If no such + entry exists, return NULL. If HINT is not NULL, set *HINT to the array + index of the entry returned. Successive calls in a linear scan scenario + will be faster called with the same HINT variable. */ +svn_fs_x__dirent_t * +svn_fs_x__find_dir_entry(apr_array_header_t *entries, + const char *name, + int *hint); + +/* Set *DIRENT to the entry identified by NAME in the directory given + by NODEREV in filesystem FS. If no such entry exits, *DIRENT will + be NULL. The value referenced by HINT can be used to speed up + consecutive calls when travering the directory in name order. + Any value is allowed, however APR_SIZE_MAX gives best performance + when there has been no previous lookup for the same directory. + + The returned object is allocated in RESULT_POOL; SCRATCH_POOL + used for temporary allocations. */ +svn_error_t * +svn_fs_x__rep_contents_dir_entry(svn_fs_x__dirent_t **dirent, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + const char *name, + apr_size_t *hint, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set *PROPLIST to be an apr_hash_t containing the property list of + node-revision NODEREV as seen in filesystem FS. Allocate the result + in RESULT_POOL and use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__get_proplist(apr_hash_t **proplist, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Fetch the list of change in revision REV in FS and return it in *CHANGES. + * Allocate the result in POOL. + */ +svn_error_t * +svn_fs_x__get_changes(apr_array_header_t **changes, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/caching.c b/contrib/subversion/subversion/libsvn_fs_x/caching.c new file mode 100644 index 000000000..17e80bddc --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/caching.c @@ -0,0 +1,725 @@ +/* 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_x.h" +#include "id.h" +#include "dag.h" +#include "tree.h" +#include "index.h" +#include "changes.h" +#include "noderevs.h" +#include "temp_serializer.h" +#include "reps.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 RESULT_POOL. + */ +static const char * +normalize_key_part(const char *original, + apr_pool_t *result_pool) +{ + apr_size_t i; + apr_size_t len = strlen(original); + svn_stringbuf_t *normalized = svn_stringbuf_create_ensure(len, + result_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; +} + +/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS and *CACHE_REVPROPS flags will be set + according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix + to use. + + Allocate CACHE_NAMESPACE in RESULT_POOL. */ +static svn_error_t * +read_config(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 *result_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, + ""), + result_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, + TRUE); + + /* 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. + * Option "2" is equivalent to "1". + */ + 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 = TRUE; + + return SVN_NO_ERROR; +} + + +/* 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, */ +typedef 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; +} dump_cache_baton_t; + +/* 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) +{ + 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); + + /* skip unused caches */ + if (! err && (info.gets > 0 || info.sets > 0)) + { + text_stats = svn_cache__format_info(&info, TRUE, 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; +} + +static apr_status_t +dump_global_cache_statistics(void *baton_void) +{ + apr_pool_t *pool = baton_void; + + svn_cache__info_t *info = svn_cache__membuffer_get_global_info(pool); + svn_string_t *text_stats = svn_cache__format_info(info, FALSE, pool); + apr_array_header_t *lines = svn_cstring_split(text_stats->data, "\n", + FALSE, pool); + + int i; + 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 + } + + return APR_SUCCESS; +} + +#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 FSX session. + */ + 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. Use the given PRIORITY class for the new cache. If it + * is 0, then use the default priority class. + * + * Unless NO_HANDLER is true, register an error handler that reports errors + * as warnings to the FS warning callback. + * + * Cache is allocated in RESULT_POOL, temporaries in SCRATCH_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, + apr_uint32_t priority, + svn_fs_t *fs, + svn_boolean_t no_handler, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_cache__error_handler_t error_handler = no_handler + ? NULL + : warn_and_fail_on_cache_errors; + if (priority == 0) + priority = SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY; + + if (memcache) + { + SVN_ERR(svn_cache__create_memcache(cache_p, memcache, + serializer, deserializer, klen, + prefix, result_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, priority, FALSE, result_pool, scratch_pool)); + } + else if (pages) + { + SVN_ERR(svn_cache__create_inprocess( + cache_p, serializer, deserializer, klen, pages, + items_per_page, FALSE, prefix, result_pool)); + } + else + { + *cache_p = NULL; + } + + SVN_ERR(init_callbacks(*cache_p, fs, error_handler, result_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__initialize_caches(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + const char *prefix = apr_pstrcat(scratch_pool, + "fsx:", fs->uuid, + "/", normalize_key_part(fs->path, + scratch_pool), + ":", + SVN_VA_NULL); + svn_membuffer_t *membuffer; + svn_boolean_t no_handler = ffd->fail_stop; + 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(&cache_namespace, + &cache_txdeltas, + &cache_fulltexts, + &cache_revprops, + fs, + scratch_pool)); + + prefix = apr_pstrcat(scratch_pool, "ns:", cache_namespace, ":", prefix, + SVN_VA_NULL); + + membuffer = svn_cache__get_global_membuffer_cache(); + + /* General rules for assigning cache priorities: + * + * - Data that can be reconstructed from other elements has low prio + * (e.g. fulltexts, directories etc.) + * - Index data required to find any of the other data has high prio + * (e.g. noderevs, L2P and P2L index pages) + * - everthing else should use default prio + */ + +#ifdef SVN_DEBUG_CACHE_DUMP_STATS + + /* schedule printing the global access statistics upon pool cleanup, + * i.e. end of FSX session. + */ + if (membuffer) + apr_pool_cleanup_register(fs->pool, + fs->pool, + dump_global_cache_statistics, + apr_pool_cleanup_null); +#endif + + /* 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_x__dag_serialize, + svn_fs_x__dag_deserialize, + APR_HASH_KEY_STRING, + apr_pstrcat(scratch_pool, prefix, "DAG", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + /* 1st level DAG node cache */ + ffd->dag_node_cache = svn_fs_x__create_dag_cache(fs->pool); + + /* Very rough estimate: 1K per directory. */ + SVN_ERR(create_cache(&(ffd->dir_cache), + NULL, + membuffer, + 1024, 8, + svn_fs_x__serialize_dir_entries, + svn_fs_x__deserialize_dir_entries, + sizeof(svn_fs_x__id_t), + apr_pstrcat(scratch_pool, prefix, "DIR", SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, + fs, + no_handler, + fs->pool, scratch_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_x__serialize_manifest, + svn_fs_x__deserialize_manifest, + sizeof(svn_revnum_t), + apr_pstrcat(scratch_pool, prefix, "PACK-MANIFEST", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + /* initialize node revision cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->node_revision_cache), + NULL, + membuffer, + 32, 32, /* ~200 byte / entry; 1k entries total */ + svn_fs_x__serialize_node_revision, + svn_fs_x__deserialize_node_revision, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "NODEREVS", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + /* initialize representation header cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->rep_header_cache), + NULL, + membuffer, + 1, 1000, /* ~8 bytes / entry; 1k entries total */ + svn_fs_x__serialize_rep_header, + svn_fs_x__deserialize_rep_header, + sizeof(svn_fs_x__representation_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "REPHEADER", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + /* initialize node change list cache, if caching has been enabled */ + SVN_ERR(create_cache(&(ffd->changes_cache), + NULL, + membuffer, + 1, 8, /* 1k / entry; 8 entries total, rarely used */ + svn_fs_x__serialize_changes, + svn_fs_x__deserialize_changes, + sizeof(svn_revnum_t), + apr_pstrcat(scratch_pool, prefix, "CHANGES", + SVN_VA_NULL), + 0, + fs, + no_handler, + fs->pool, scratch_pool)); + + /* if enabled, cache fulltext and other derived information */ + if (cache_fulltexts) + { + SVN_ERR(create_cache(&(ffd->fulltext_cache), + ffd->memcache, + membuffer, + 0, 0, /* Do not use inprocess cache */ + /* Values are svn_stringbuf_t */ + NULL, NULL, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "TEXT", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + SVN_ERR(create_cache(&(ffd->properties_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_x__serialize_properties, + svn_fs_x__deserialize_properties, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "PROP", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + SVN_ERR(create_cache(&(ffd->mergeinfo_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_x__serialize_mergeinfo, + svn_fs_x__deserialize_mergeinfo, + APR_HASH_KEY_STRING, + apr_pstrcat(scratch_pool, prefix, "MERGEINFO", + SVN_VA_NULL), + 0, + fs, + no_handler, + fs->pool, scratch_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(scratch_pool, prefix, "HAS_MERGEINFO", + SVN_VA_NULL), + 0, + fs, + no_handler, + fs->pool, scratch_pool)); + } + else + { + ffd->fulltext_cache = NULL; + ffd->properties_cache = NULL; + ffd->mergeinfo_cache = NULL; + ffd->mergeinfo_existence_cache = NULL; + } + + /* if enabled, cache revprops */ + if (cache_revprops) + { + SVN_ERR(create_cache(&(ffd->revprop_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_x__serialize_properties, + svn_fs_x__deserialize_properties, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "REVPROP", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY, + fs, + no_handler, + fs->pool, scratch_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_x__serialize_txdelta_window, + svn_fs_x__deserialize_txdelta_window, + sizeof(svn_fs_x__window_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "TXDELTA_WINDOW", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, + fs, + no_handler, + fs->pool, scratch_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, + sizeof(svn_fs_x__window_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "COMBINED_WINDOW", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_LOW_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + } + else + { + ffd->txdelta_window_cache = NULL; + ffd->combined_window_cache = NULL; + } + + SVN_ERR(create_cache(&(ffd->noderevs_container_cache), + NULL, + membuffer, + 16, 4, /* Important, largish objects */ + svn_fs_x__serialize_noderevs_container, + svn_fs_x__deserialize_noderevs_container, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "NODEREVSCNT", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + SVN_ERR(create_cache(&(ffd->changes_container_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_x__serialize_changes_container, + svn_fs_x__deserialize_changes_container, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "CHANGESCNT", + SVN_VA_NULL), + 0, + fs, + no_handler, + fs->pool, scratch_pool)); + SVN_ERR(create_cache(&(ffd->reps_container_cache), + NULL, + membuffer, + 0, 0, /* Do not use inprocess cache */ + svn_fs_x__serialize_reps_container, + svn_fs_x__deserialize_reps_container, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "REPSCNT", + SVN_VA_NULL), + 0, + fs, + no_handler, + fs->pool, scratch_pool)); + + SVN_ERR(create_cache(&(ffd->l2p_header_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_x__serialize_l2p_header, + svn_fs_x__deserialize_l2p_header, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "L2P_HEADER", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + SVN_ERR(create_cache(&(ffd->l2p_page_cache), + NULL, + membuffer, + 64, 16, /* entry size varies but we must cover + a reasonable number of revisions (1k) */ + svn_fs_x__serialize_l2p_page, + svn_fs_x__deserialize_l2p_page, + sizeof(svn_fs_x__page_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "L2P_PAGE", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + SVN_ERR(create_cache(&(ffd->p2l_header_cache), + NULL, + membuffer, + 4, 1, /* Large entries. Rarely used. */ + svn_fs_x__serialize_p2l_header, + svn_fs_x__deserialize_p2l_header, + sizeof(svn_fs_x__pair_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "P2L_HEADER", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + SVN_ERR(create_cache(&(ffd->p2l_page_cache), + NULL, + membuffer, + 4, 16, /* Variably sized entries. Rarely used. */ + svn_fs_x__serialize_p2l_page, + svn_fs_x__deserialize_p2l_page, + sizeof(svn_fs_x__page_cache_key_t), + apr_pstrcat(scratch_pool, prefix, "P2L_PAGE", + SVN_VA_NULL), + SVN_CACHE__MEMBUFFER_HIGH_PRIORITY, + fs, + no_handler, + fs->pool, scratch_pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/changes.c b/contrib/subversion/subversion/libsvn_fs_x/changes.c new file mode 100644 index 000000000..a7d5ee2fb --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/changes.c @@ -0,0 +1,536 @@ +/* changes.h --- FSX changed paths lists container + * + * ==================================================================== + * 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 "private/svn_packed_data.h" + +#include "changes.h" +#include "string_table.h" +#include "temp_serializer.h" + +/* These flags will be used with the FLAGS field in binary_change_t. + */ + +/* the change contains a text modification */ +#define CHANGE_TEXT_MOD 0x00001 + +/* the change contains a property modification */ +#define CHANGE_PROP_MOD 0x00002 + +/* the last part (rev_id) of node revision ID is a transaction ID */ +#define CHANGE_TXN_NODE 0x00004 + +/* (flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT extracts the node type */ +#define CHANGE_NODE_SHIFT 0x00003 +#define CHANGE_NODE_MASK 0x00018 + +/* node types according to svn_node_kind_t */ +#define CHANGE_NODE_NONE 0x00000 +#define CHANGE_NODE_FILE 0x00008 +#define CHANGE_NODE_DIR 0x00010 +#define CHANGE_NODE_UNKNOWN 0x00018 + +/* (flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT extracts the change type */ +#define CHANGE_KIND_SHIFT 0x00005 +#define CHANGE_KIND_MASK 0x000E0 + +/* node types according to svn_fs_path_change_kind_t */ +#define CHANGE_KIND_MODIFY 0x00000 +#define CHANGE_KIND_ADD 0x00020 +#define CHANGE_KIND_DELETE 0x00040 +#define CHANGE_KIND_REPLACE 0x00060 +#define CHANGE_KIND_RESET 0x00080 +#define CHANGE_KIND_MOVE 0x000A0 +#define CHANGE_KIND_MOVEREPLACE 0x000C0 + +/* Our internal representation of a change */ +typedef struct binary_change_t +{ + /* define the kind of change and what specific information is present */ + int flags; + + /* Path of the change. */ + apr_size_t path; + + /* copy-from information. + * Not present if COPYFROM_REV is SVN_INVALID_REVNUM. */ + svn_revnum_t copyfrom_rev; + apr_size_t copyfrom_path; + + /* Relevant parts of the node revision ID of the change. + * Empty, if REV_ID is not "used". */ + svn_fs_x__id_t noderev_id; + +} binary_change_t; + +/* The actual container object. Change lists are concatenated into CHANGES + * and and their begins and ends are stored in OFFSETS. + */ +struct svn_fs_x__changes_t +{ + /* The paths - either in 'builder' mode or finalized mode. + * The respective other pointer will be NULL. */ + string_table_builder_t *builder; + string_table_t *paths; + + /* All changes of all change lists concatenated. + * Array elements are binary_change_t.structs (not pointer!) */ + apr_array_header_t *changes; + + /* [Offsets[index] .. Offsets[index+1]) is the range in CHANGES that + * forms the contents of change list INDEX. */ + apr_array_header_t *offsets; +}; + +/* Create and return a new container object, allocated in RESULT_POOL with + * an initial capacity of INITIAL_COUNT changes. The PATH and BUILDER + * members must be initialized by the caller afterwards. + */ +static svn_fs_x__changes_t * +changes_create_body(apr_size_t initial_count, + apr_pool_t *result_pool) +{ + svn_fs_x__changes_t *changes = apr_pcalloc(result_pool, sizeof(*changes)); + + changes->changes = apr_array_make(result_pool, (int)initial_count, + sizeof(binary_change_t)); + changes->offsets = apr_array_make(result_pool, 16, sizeof(int)); + APR_ARRAY_PUSH(changes->offsets, int) = 0; + + return changes; +} + +svn_fs_x__changes_t * +svn_fs_x__changes_create(apr_size_t initial_count, + apr_pool_t *result_pool) +{ + svn_fs_x__changes_t *changes = changes_create_body(initial_count, + result_pool); + changes->builder = svn_fs_x__string_table_builder_create(result_pool); + + return changes; +} + +/* Add CHANGE to the latest change list in CHANGES. + */ +static svn_error_t * +append_change(svn_fs_x__changes_t *changes, + svn_fs_x__change_t *change) +{ + binary_change_t binary_change = { 0 }; + svn_boolean_t is_txn_id; + + /* CHANGE must be sufficiently complete */ + SVN_ERR_ASSERT(change); + SVN_ERR_ASSERT(change->path.data); + + /* Relevant parts of the revision ID of the change. */ + binary_change.noderev_id = change->noderev_id; + + /* define the kind of change and what specific information is present */ + is_txn_id = svn_fs_x__is_txn(binary_change.noderev_id.change_set); + binary_change.flags = (change->text_mod ? CHANGE_TEXT_MOD : 0) + | (change->prop_mod ? CHANGE_PROP_MOD : 0) + | (is_txn_id ? CHANGE_TXN_NODE : 0) + | ((int)change->change_kind << CHANGE_KIND_SHIFT) + | ((int)change->node_kind << CHANGE_NODE_SHIFT); + + /* Path of the change. */ + binary_change.path + = svn_fs_x__string_table_builder_add(changes->builder, + change->path.data, + change->path.len); + + /* copy-from information, if presence is indicated by FLAGS */ + if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) + { + binary_change.copyfrom_rev = change->copyfrom_rev; + binary_change.copyfrom_path + = svn_fs_x__string_table_builder_add(changes->builder, + change->copyfrom_path, + 0); + } + else + { + binary_change.copyfrom_rev = SVN_INVALID_REVNUM; + binary_change.copyfrom_path = 0; + } + + APR_ARRAY_PUSH(changes->changes, binary_change_t) = binary_change; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__changes_append_list(apr_size_t *list_index, + svn_fs_x__changes_t *changes, + apr_array_header_t *list) +{ + int i; + + /* CHANGES must be in 'builder' mode */ + SVN_ERR_ASSERT(changes->builder); + SVN_ERR_ASSERT(changes->paths == NULL); + + /* simply append the list and all changes */ + for (i = 0; i < list->nelts; ++i) + append_change(changes, APR_ARRAY_IDX(list, i, svn_fs_x__change_t *)); + + /* terminate the list by storing the next changes offset */ + APR_ARRAY_PUSH(changes->offsets, int) = changes->changes->nelts; + *list_index = (apr_size_t)(changes->offsets->nelts - 2); + + return SVN_NO_ERROR; +} + +apr_size_t +svn_fs_x__changes_estimate_size(const svn_fs_x__changes_t *changes) +{ + /* CHANGES must be in 'builder' mode */ + if (changes->builder == NULL) + return 0; + + /* string table code makes its own prediction, + * changes should be < 10 bytes each, + * some static overhead should be assumed */ + return svn_fs_x__string_table_builder_estimate_size(changes->builder) + + changes->changes->nelts * 10 + + 100; +} + +svn_error_t * +svn_fs_x__changes_get_list(apr_array_header_t **list, + const svn_fs_x__changes_t *changes, + apr_size_t idx, + apr_pool_t *pool) +{ + int first; + int last; + int i; + + /* CHANGES must be in 'finalized' mode */ + SVN_ERR_ASSERT(changes->builder == NULL); + SVN_ERR_ASSERT(changes->paths); + + /* validate index */ + if (idx + 1 >= (apr_size_t)changes->offsets->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Changes list index %%%s" + " exceeds container size %%d"), + APR_SIZE_T_FMT), + idx, changes->offsets->nelts - 1); + + /* range of changes to return */ + first = APR_ARRAY_IDX(changes->offsets, (int)idx, int); + last = APR_ARRAY_IDX(changes->offsets, (int)idx + 1, int); + + /* construct result */ + *list = apr_array_make(pool, last - first, sizeof(svn_fs_x__change_t*)); + for (i = first; i < last; ++i) + { + const binary_change_t *binary_change + = &APR_ARRAY_IDX(changes->changes, i, binary_change_t); + + /* convert BINARY_CHANGE into a standard FSX svn_fs_x__change_t */ + svn_fs_x__change_t *change = apr_pcalloc(pool, sizeof(*change)); + change->path.data = svn_fs_x__string_table_get(changes->paths, + binary_change->path, + &change->path.len, + pool); + + if (binary_change->noderev_id.change_set != SVN_FS_X__INVALID_CHANGE_SET) + change->noderev_id = binary_change->noderev_id; + + change->change_kind = (svn_fs_path_change_kind_t) + ((binary_change->flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT); + change->text_mod = (binary_change->flags & CHANGE_TEXT_MOD) != 0; + change->prop_mod = (binary_change->flags & CHANGE_PROP_MOD) != 0; + change->node_kind = (svn_node_kind_t) + ((binary_change->flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT); + + change->copyfrom_rev = binary_change->copyfrom_rev; + change->copyfrom_known = TRUE; + if (SVN_IS_VALID_REVNUM(binary_change->copyfrom_rev)) + change->copyfrom_path + = svn_fs_x__string_table_get(changes->paths, + binary_change->copyfrom_path, + NULL, + pool); + + /* add it to the result */ + APR_ARRAY_PUSH(*list, svn_fs_x__change_t*) = change; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__write_changes_container(svn_stream_t *stream, + const svn_fs_x__changes_t *changes, + apr_pool_t *scratch_pool) +{ + int i; + + string_table_t *paths = changes->paths + ? changes->paths + : svn_fs_x__string_table_create(changes->builder, + scratch_pool); + + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + /* one top-level stream for each array */ + svn_packed__int_stream_t *offsets_stream + = svn_packed__create_int_stream(root, TRUE, FALSE); + svn_packed__int_stream_t *changes_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + + /* structure the CHANGES_STREAM such we can extract much of the redundancy + * from the binary_change_t structs */ + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, TRUE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + svn_packed__create_int_substream(changes_stream, TRUE, TRUE); + svn_packed__create_int_substream(changes_stream, TRUE, FALSE); + + /* serialize offsets array */ + for (i = 0; i < changes->offsets->nelts; ++i) + svn_packed__add_uint(offsets_stream, + APR_ARRAY_IDX(changes->offsets, i, int)); + + /* serialize changes array */ + for (i = 0; i < changes->changes->nelts; ++i) + { + const binary_change_t *change + = &APR_ARRAY_IDX(changes->changes, i, binary_change_t); + + svn_packed__add_uint(changes_stream, change->flags); + svn_packed__add_uint(changes_stream, change->path); + + svn_packed__add_int(changes_stream, change->copyfrom_rev); + svn_packed__add_uint(changes_stream, change->copyfrom_path); + + svn_packed__add_int(changes_stream, change->noderev_id.change_set); + svn_packed__add_uint(changes_stream, change->noderev_id.number); + } + + /* write to disk */ + SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool)); + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_changes_container(svn_fs_x__changes_t **changes_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i; + apr_size_t count; + + svn_fs_x__changes_t *changes = apr_pcalloc(result_pool, sizeof(*changes)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *offsets_stream; + svn_packed__int_stream_t *changes_stream; + + /* read from disk */ + SVN_ERR(svn_fs_x__read_string_table(&changes->paths, stream, + result_pool, scratch_pool)); + + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + offsets_stream = svn_packed__first_int_stream(root); + changes_stream = svn_packed__next_int_stream(offsets_stream); + + /* read offsets array */ + count = svn_packed__int_count(offsets_stream); + changes->offsets = apr_array_make(result_pool, (int)count, sizeof(int)); + for (i = 0; i < count; ++i) + APR_ARRAY_PUSH(changes->offsets, int) + = (int)svn_packed__get_uint(offsets_stream); + + /* read changes array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(changes_stream)); + changes->changes + = apr_array_make(result_pool, (int)count, sizeof(binary_change_t)); + for (i = 0; i < count; ++i) + { + binary_change_t change; + + change.flags = (int)svn_packed__get_uint(changes_stream); + change.path = (apr_size_t)svn_packed__get_uint(changes_stream); + + change.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(changes_stream); + change.copyfrom_path = (apr_size_t)svn_packed__get_uint(changes_stream); + + change.noderev_id.change_set = svn_packed__get_int(changes_stream); + change.noderev_id.number = svn_packed__get_uint(changes_stream); + + APR_ARRAY_PUSH(changes->changes, binary_change_t) = change; + } + + *changes_p = changes; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_changes_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__changes_t *changes = in; + svn_stringbuf_t *serialized; + + /* make a guesstimate on the size of the serialized data. Erring on the + * low side will cause the serializer to re-alloc its buffer. */ + apr_size_t size + = changes->changes->elt_size * changes->changes->nelts + + changes->offsets->elt_size * changes->offsets->nelts + + 10 * changes->changes->elt_size + + 100; + + /* serialize array header and all its elements */ + svn_temp_serializer__context_t *context + = svn_temp_serializer__init(changes, sizeof(*changes), size, pool); + + /* serialize sub-structures */ + svn_fs_x__serialize_string_table(context, &changes->paths); + svn_fs_x__serialize_apr_array(context, &changes->changes); + svn_fs_x__serialize_apr_array(context, &changes->offsets); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_changes_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__changes_t *changes = (svn_fs_x__changes_t *)data; + + /* de-serialize sub-structures */ + svn_fs_x__deserialize_string_table(changes, &changes->paths); + svn_fs_x__deserialize_apr_array(changes, &changes->changes, pool); + svn_fs_x__deserialize_apr_array(changes, &changes->offsets, pool); + + /* done */ + *out = changes; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__changes_get_list_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + int first; + int last; + int i; + apr_array_header_t *list; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__changes_t *container = data; + + /* resolve all the sub-container pointers we need */ + const string_table_t *paths + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->paths); + const apr_array_header_t *serialized_offsets + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->offsets); + const apr_array_header_t *serialized_changes + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->changes); + const int *offsets + = svn_temp_deserializer__ptr(serialized_offsets, + (const void *const *)&serialized_offsets->elts); + const binary_change_t *changes + = svn_temp_deserializer__ptr(serialized_changes, + (const void *const *)&serialized_changes->elts); + + /* validate index */ + if (idx + 1 >= (apr_size_t)serialized_offsets->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("Changes list index %u exceeds container " + "size %d"), + (unsigned)idx, serialized_offsets->nelts - 1); + + /* range of changes to return */ + first = offsets[idx]; + last = offsets[idx+1]; + + /* construct result */ + list = apr_array_make(pool, last - first, sizeof(svn_fs_x__change_t*)); + + for (i = first; i < last; ++i) + { + const binary_change_t *binary_change = &changes[i]; + + /* convert BINARY_CHANGE into a standard FSX svn_fs_x__change_t */ + svn_fs_x__change_t *change = apr_pcalloc(pool, sizeof(*change)); + change->path.data + = svn_fs_x__string_table_get_func(paths, binary_change->path, + &change->path.len, pool); + + change->noderev_id = binary_change->noderev_id; + + change->change_kind = (svn_fs_path_change_kind_t) + ((binary_change->flags & CHANGE_KIND_MASK) >> CHANGE_KIND_SHIFT); + change->text_mod = (binary_change->flags & CHANGE_TEXT_MOD) != 0; + change->prop_mod = (binary_change->flags & CHANGE_PROP_MOD) != 0; + change->node_kind = (svn_node_kind_t) + ((binary_change->flags & CHANGE_NODE_MASK) >> CHANGE_NODE_SHIFT); + + change->copyfrom_rev = binary_change->copyfrom_rev; + change->copyfrom_known = TRUE; + if (SVN_IS_VALID_REVNUM(binary_change->copyfrom_rev)) + change->copyfrom_path + = svn_fs_x__string_table_get_func(paths, + binary_change->copyfrom_path, + NULL, + pool); + + /* add it to the result */ + APR_ARRAY_PUSH(list, svn_fs_x__change_t*) = change; + } + + *out = list; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/changes.h b/contrib/subversion/subversion/libsvn_fs_x/changes.h new file mode 100644 index 000000000..ccb264761 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/changes.h @@ -0,0 +1,132 @@ +/* changes.h --- FSX changed paths lists container + * + * ==================================================================== + * 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_FS__CHANGES_H +#define SVN_LIBSVN_FS__CHANGES_H + +#include "svn_io.h" +#include "fs.h" + +/* Entries in a revision's change list tend to be widely redundant (similar + * changes to similar paths). Even more so, change lists from a larger + * revision range also tend to overlap. + * + * In its serialized form, the svn_fs_x__changes_t container extracts most + * of that redundancy and the run-time representation is also much smaller + * than sum of the respective svn_fs_x__change_t* arrays. + * + * As with other containers, this one has two modes: 'construction', in + * which you may add data to it, and 'getter' in which there is only r/o + * access to the data. + */ + +/* An opaque collection of change lists (apr_array_header_t * of + * svn_fs_x__change_t *). + */ +typedef struct svn_fs_x__changes_t svn_fs_x__changes_t; + +/* Create and populate changes containers. */ + +/* Create and return a new changes container with an initial capacity of + * INITIAL_COUNT svn_fs_x__change_t objects. + * Allocate the result in RESULT_POOL. + */ +svn_fs_x__changes_t * +svn_fs_x__changes_create(apr_size_t initial_count, + apr_pool_t *result_pool); + +/* Start a new change list CHANGES (implicitly terminating the previous one) + * and return its index in *LIST_INDEX. Append all changes from LIST to + * that new change list. + */ +svn_error_t * +svn_fs_x__changes_append_list(apr_size_t *list_index, + svn_fs_x__changes_t *changes, + apr_array_header_t *list); + +/* Return a rough estimate in bytes for the serialized representation + * of CHANGES. + */ +apr_size_t +svn_fs_x__changes_estimate_size(const svn_fs_x__changes_t *changes); + +/* Read changes containers. */ + +/* From CHANGES, extract the change list with the given IDX. Allocate + * the result in POOL and return it in *LIST. + */ +svn_error_t * +svn_fs_x__changes_get_list(apr_array_header_t **list, + const svn_fs_x__changes_t *changes, + apr_size_t idx, + apr_pool_t *pool); + +/* I/O interface. */ + +/* Write a serialized representation of CHANGES to STREAM. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__write_changes_container(svn_stream_t *stream, + const svn_fs_x__changes_t *changes, + apr_pool_t *scratch_pool); + +/* Read a changes container from its serialized representation in STREAM. + * Allocate the result in RESULT_POOL and return it in *CHANGES_P. Use + * SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__read_changes_container(svn_fs_x__changes_t **changes_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Implements #svn_cache__serialize_func_t for svn_fs_x__changes_t objects. + */ +svn_error_t * +svn_fs_x__serialize_changes_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* Implements #svn_cache__deserialize_func_t for svn_fs_x__changes_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_changes_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* Implements svn_cache__partial_getter_func_t for svn_fs_x__changes_t, + * setting *OUT to the change list (apr_array_header_t *) selected by + * the apr_uint32_t index passed in as *BATON. This function is similar + * to svn_fs_x__changes_get_list but operates on the cache serialized + * representation of the container. + */ +svn_error_t * +svn_fs_x__changes_get_list_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/dag.c b/contrib/subversion/subversion/libsvn_fs_x/dag.c new file mode 100644 index 000000000..2f5bcb260 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/dag.c @@ -0,0 +1,1368 @@ +/* dag.c : DAG-like interface filesystem, private to libsvn_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 + +#include "svn_path.h" +#include "svn_error.h" +#include "svn_fs.h" +#include "svn_props.h" +#include "svn_pools.h" + +#include "dag.h" +#include "fs.h" +#include "fs_x.h" +#include "fs_id.h" +#include "cached_data.h" +#include "transaction.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "private/svn_fspath.h" +#include "svn_private_config.h" +#include "private/svn_temp_serializer.h" +#include "temp_serializer.h" + + +/* Initializing a filesystem. */ + +struct dag_node_t +{ + /* The filesystem this dag node came from. */ + svn_fs_t *fs; + + /* The node revision ID for this dag node. */ + svn_fs_x__id_t id; + + /* In the special case that this node is the root of a transaction + that has not yet been modified, the revision of this node is the + respective txn's base rev. Otherwise, this is SVN_INVALID_REVNUM + for txn nodes and the respective crev for committed nodes. + (Used in svn_fs_node_created_rev.) */ + svn_revnum_t revision; + + /* The node's type (file, dir, etc.) */ + svn_node_kind_t kind; + + /* The node's NODE-REVISION, or NULL if we haven't read it in yet. + This is allocated in this node's POOL. + + If you're willing to respect all the rules above, you can munge + this yourself, but you're probably better off just calling + `get_node_revision' and `set_node_revision', which take care of + things for you. */ + svn_fs_x__noderev_t *node_revision; + + /* The pool to allocate NODE_REVISION in. */ + apr_pool_t *node_pool; + + /* the path at which this node was created. */ + const char *created_path; + + /* Directory entry lookup hint to speed up consecutive calls to + svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes. + Any value is legal but should default to APR_SIZE_MAX. */ + apr_size_t hint; +}; + + + +/* Trivial helper/accessor functions. */ +svn_node_kind_t +svn_fs_x__dag_node_kind(dag_node_t *node) +{ + return node->kind; +} + +const svn_fs_x__id_t * +svn_fs_x__dag_get_id(const dag_node_t *node) +{ + return &node->id; +} + + +const char * +svn_fs_x__dag_get_created_path(dag_node_t *node) +{ + return node->created_path; +} + + +svn_fs_t * +svn_fs_x__dag_get_fs(dag_node_t *node) +{ + return node->fs; +} + +void +svn_fs_x__dag_set_fs(dag_node_t *node, + svn_fs_t *fs) +{ + node->fs = fs; +} + + +/* Dup NODEREV and all associated data into RESULT_POOL. + Leaves the id and is_fresh_txn_root fields as zero bytes. */ +static svn_fs_x__noderev_t * +copy_node_revision(svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool) +{ + svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev, + sizeof(*noderev)); + + if (noderev->copyfrom_path) + nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path); + + nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path); + nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool); + nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool); + + if (noderev->created_path) + nr->created_path = apr_pstrdup(result_pool, noderev->created_path); + + return nr; +} + + +/* Set *NODEREV_P to the cached node-revision for NODE. + If the node-revision was not already cached in NODE, read it in, + allocating the cache in NODE->NODE_POOL. + + If you plan to change the contents of NODE, be careful! We're + handing you a pointer directly to our cached node-revision, not + your own copy. If you change it as part of some operation, but + then some Berkeley DB function deadlocks or gets an error, you'll + need to back out your changes, or else the cache will reflect + changes that never got committed. It's probably best not to change + the structure at all. */ +static svn_error_t * +get_node_revision(svn_fs_x__noderev_t **noderev_p, + dag_node_t *node) +{ + /* If we've already got a copy, there's no need to read it in. */ + if (! node->node_revision) + { + svn_fs_x__noderev_t *noderev; + apr_pool_t *scratch_pool = svn_pool_create(node->node_pool); + + SVN_ERR(svn_fs_x__get_node_revision(&noderev, node->fs, &node->id, + node->node_pool, scratch_pool)); + node->node_revision = noderev; + svn_pool_destroy(scratch_pool); + } + + /* Now NODE->node_revision is set. */ + *noderev_p = node->node_revision; + return SVN_NO_ERROR; +} + +/* Return the node revision ID of NODE. The value returned is shared + with NODE, and will be deallocated when NODE is. */ +svn_error_t * +svn_fs_x__dag_get_node_id(svn_fs_x__id_t *node_id, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + SVN_ERR(get_node_revision(&noderev, node)); + + *node_id = noderev->node_id; + return SVN_NO_ERROR; +} + +/* Return the node revision ID of NODE. The value returned is shared + with NODE, and will be deallocated when NODE is. */ +svn_error_t * +svn_fs_x__dag_get_copy_id(svn_fs_x__id_t *copy_id, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + SVN_ERR(get_node_revision(&noderev, node)); + + *copy_id = noderev->copy_id; + return SVN_NO_ERROR; +} + +/* Return the node ID of NODE. The value returned is shared with NODE, + and will be deallocated when NODE is. */ +svn_error_t * +svn_fs_x__dag_related_node(svn_boolean_t *same, + dag_node_t *lhs, + dag_node_t *rhs) +{ + svn_fs_x__id_t lhs_node, rhs_node; + + SVN_ERR(svn_fs_x__dag_get_node_id(&lhs_node, lhs)); + SVN_ERR(svn_fs_x__dag_get_node_id(&rhs_node, rhs)); + *same = svn_fs_x__id_eq(&lhs_node, &rhs_node); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_same_line_of_history(svn_boolean_t *same, + dag_node_t *lhs, + dag_node_t *rhs) +{ + svn_fs_x__noderev_t *lhs_noderev, *rhs_noderev; + + SVN_ERR(get_node_revision(&lhs_noderev, lhs)); + SVN_ERR(get_node_revision(&rhs_noderev, rhs)); + + *same = svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id) + && svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id); + + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_fs_x__dag_check_mutable(const dag_node_t *node) +{ + return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set); +} + + +svn_error_t * +svn_fs_x__dag_get_node(dag_node_t **node, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + dag_node_t *new_node; + svn_fs_x__noderev_t *noderev; + + /* Construct the node. */ + new_node = apr_pcalloc(result_pool, sizeof(*new_node)); + new_node->fs = fs; + new_node->id = *id; + new_node->hint = APR_SIZE_MAX; + + /* Grab the contents so we can inspect the node's kind and created path. */ + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, + result_pool, scratch_pool)); + new_node->node_pool = result_pool; + new_node->node_revision = noderev; + + /* Initialize the KIND and CREATED_PATH attributes */ + new_node->kind = noderev->kind; + new_node->created_path = noderev->created_path; + + /* Support our quirky svn_fs_node_created_rev API. + Untouched txn roots report the base rev as theirs. */ + new_node->revision + = ( svn_fs_x__is_fresh_txn_root(noderev) + ? svn_fs_x__get_revnum(noderev->predecessor_id.change_set) + : svn_fs_x__get_revnum(id->change_set)); + + /* Return a fresh new node */ + *node = new_node; + return SVN_NO_ERROR; +} + + +svn_revnum_t +svn_fs_x__dag_get_revision(const dag_node_t *node) +{ + return node->revision; +} + + +svn_error_t * +svn_fs_x__dag_get_predecessor_id(svn_fs_x__id_t *id_p, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + *id_p = noderev->predecessor_id; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__dag_get_predecessor_count(int *count, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + *count = noderev->predecessor_count; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + *count = noderev->mergeinfo_count; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + *has_mergeinfo = noderev->has_mergeinfo; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + if (node->kind != svn_node_dir) + { + *do_they = FALSE; + return SVN_NO_ERROR; + } + + SVN_ERR(get_node_revision(&noderev, node)); + if (noderev->mergeinfo_count > 1) + *do_they = TRUE; + else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo) + *do_they = TRUE; + else + *do_they = FALSE; + return SVN_NO_ERROR; +} + + +/*** Directory node functions ***/ + +/* Some of these are helpers for functions outside this section. */ + +/* Set *ID_P to the noderev-id for entry NAME in PARENT. If no such + entry, set *ID_P to NULL but do not error. */ +static svn_error_t * +dir_entry_id_from_node(svn_fs_x__id_t *id_p, + dag_node_t *parent, + const char *name, + apr_pool_t *scratch_pool) +{ + svn_fs_x__dirent_t *dirent; + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, parent)); + if (noderev->kind != svn_node_dir) + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get entries of non-directory")); + + /* Make sure that NAME is a single path component. */ + if (! svn_path_is_single_path_component(name)) + return svn_error_createf + (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, + "Attempted to open node with an illegal name '%s'", name); + + /* Get a dirent hash for this directory. */ + SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev, + name, &parent->hint, + scratch_pool, scratch_pool)); + if (dirent) + *id_p = dirent->id; + else + svn_fs_x__id_reset(id_p); + + return SVN_NO_ERROR; +} + + +/* Add or set in PARENT a directory entry NAME pointing to ID. + Temporary allocations are done in SCRATCH_POOL. + + Assumptions: + - PARENT is a mutable directory. + - ID does not refer to an ancestor of parent + - NAME is a single path component +*/ +static svn_error_t * +set_entry(dag_node_t *parent, + const char *name, + const svn_fs_x__id_t *id, + svn_node_kind_t kind, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *parent_noderev; + + /* Get the parent's node-revision. */ + SVN_ERR(get_node_revision(&parent_noderev, parent)); + + /* Set the new entry. */ + return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id, + kind, parent->node_pool, scratch_pool); +} + + +/* Make a new entry named NAME in PARENT. If IS_DIR is true, then the + node revision the new entry points to will be a directory, else it + will be a file. The new node will be allocated in RESULT_POOL. PARENT + must be mutable, and must not have an entry named NAME. + + Use SCRATCH_POOL for all temporary allocations. + */ +static svn_error_t * +make_entry(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + svn_boolean_t is_dir, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t new_noderev, *parent_noderev; + + /* Make sure that NAME is a single path component. */ + if (! svn_path_is_single_path_component(name)) + return svn_error_createf + (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, + _("Attempted to create a node with an illegal name '%s'"), name); + + /* Make sure that parent is a directory */ + if (parent->kind != svn_node_dir) + return svn_error_create + (SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Attempted to create entry in non-directory parent")); + + /* Check that the parent is mutable. */ + if (! svn_fs_x__dag_check_mutable(parent)) + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + _("Attempted to clone child of non-mutable node")); + + /* Create the new node's NODE-REVISION */ + memset(&new_noderev, 0, sizeof(new_noderev)); + new_noderev.kind = is_dir ? svn_node_dir : svn_node_file; + new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool); + + SVN_ERR(get_node_revision(&parent_noderev, parent)); + new_noderev.copyroot_path = apr_pstrdup(result_pool, + parent_noderev->copyroot_path); + new_noderev.copyroot_rev = parent_noderev->copyroot_rev; + new_noderev.copyfrom_rev = SVN_INVALID_REVNUM; + new_noderev.copyfrom_path = NULL; + svn_fs_x__id_reset(&new_noderev.predecessor_id); + + SVN_ERR(svn_fs_x__create_node + (svn_fs_x__dag_get_fs(parent), &new_noderev, + &parent_noderev->copy_id, txn_id, scratch_pool)); + + /* Create a new dag_node_t for our new node */ + SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent), + &new_noderev.noderev_id, result_pool, + scratch_pool)); + + /* We can safely call set_entry because we already know that + PARENT is mutable, and we just created CHILD, so we know it has + no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */ + return set_entry(parent, name, &new_noderev.noderev_id, + new_noderev.kind, txn_id, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_dir_entries(apr_array_header_t **entries, + dag_node_t *node, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + + if (noderev->kind != svn_node_dir) + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get entries of non-directory")); + + return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, result_pool, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_set_entry(dag_node_t *node, + const char *entry_name, + const svn_fs_x__id_t *id, + svn_node_kind_t kind, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + /* Check it's a directory. */ + if (node->kind != svn_node_dir) + return svn_error_create + (SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Attempted to set entry in non-directory node")); + + /* Check it's mutable. */ + if (! svn_fs_x__dag_check_mutable(node)) + return svn_error_create + (SVN_ERR_FS_NOT_MUTABLE, NULL, + _("Attempted to set entry in immutable node")); + + return set_entry(node, entry_name, id, kind, txn_id, scratch_pool); +} + + + +/*** Proplists. ***/ + +svn_error_t * +svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p, + dag_node_t *node, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + apr_hash_t *proplist = NULL; + + SVN_ERR(get_node_revision(&noderev, node)); + + SVN_ERR(svn_fs_x__get_proplist(&proplist, node->fs, noderev, result_pool, + scratch_pool)); + + *proplist_p = proplist; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__dag_set_proplist(dag_node_t *node, + apr_hash_t *proplist, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + /* Sanity check: this node better be mutable! */ + if (! svn_fs_x__dag_check_mutable(node)) + { + svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool); + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Can't set proplist on *immutable* node-revision %s", + idstr->data); + } + + /* Go get a fresh NODE-REVISION for this node. */ + SVN_ERR(get_node_revision(&noderev, node)); + + /* Set the new proplist. */ + return svn_fs_x__set_proplist(node->fs, noderev, proplist, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node, + apr_int64_t increment, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + /* Sanity check: this node better be mutable! */ + if (! svn_fs_x__dag_check_mutable(node)) + { + svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool); + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Can't increment mergeinfo count on *immutable* node-revision %s", + idstr->data); + } + + if (increment == 0) + return SVN_NO_ERROR; + + /* Go get a fresh NODE-REVISION for this node. */ + SVN_ERR(get_node_revision(&noderev, node)); + + noderev->mergeinfo_count += increment; + if (noderev->mergeinfo_count < 0) + { + svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool); + return svn_error_createf + (SVN_ERR_FS_CORRUPT, NULL, + apr_psprintf(scratch_pool, + _("Can't increment mergeinfo count on node-revision %%s " + "to negative value %%%s"), + APR_INT64_T_FMT), + idstr->data, noderev->mergeinfo_count); + } + if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file) + { + svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool); + return svn_error_createf + (SVN_ERR_FS_CORRUPT, NULL, + apr_psprintf(scratch_pool, + _("Can't increment mergeinfo count on *file* " + "node-revision %%s to %%%s (> 1)"), + APR_INT64_T_FMT), + idstr->data, noderev->mergeinfo_count); + } + + /* Flush it out. */ + return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool); +} + +svn_error_t * +svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node, + svn_boolean_t has_mergeinfo, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + /* Sanity check: this node better be mutable! */ + if (! svn_fs_x__dag_check_mutable(node)) + { + svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool); + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Can't set mergeinfo flag on *immutable* node-revision %s", + idstr->data); + } + + /* Go get a fresh NODE-REVISION for this node. */ + SVN_ERR(get_node_revision(&noderev, node)); + + noderev->has_mergeinfo = has_mergeinfo; + + /* Flush it out. */ + return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool); +} + + +/*** Roots. ***/ + +svn_error_t * +svn_fs_x__dag_revision_root(dag_node_t **node_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__id_t root_id; + + svn_fs_x__init_rev_root(&root_id, rev); + return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_txn_root(dag_node_t **node_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__id_t root_id; + + svn_fs_x__init_txn_root(&root_id, txn_id); + return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_clone_child(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + svn_boolean_t is_parent_copyroot, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + dag_node_t *cur_entry; /* parent's current entry named NAME */ + const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */ + svn_fs_t *fs = svn_fs_x__dag_get_fs(parent); + + /* First check that the parent is mutable. */ + if (! svn_fs_x__dag_check_mutable(parent)) + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Attempted to clone child of non-mutable node"); + + /* Make sure that NAME is a single path component. */ + if (! svn_path_is_single_path_component(name)) + return svn_error_createf + (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, + "Attempted to make a child clone with an illegal name '%s'", name); + + /* Find the node named NAME in PARENT's entries list if it exists. */ + SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool, + scratch_pool)); + if (! cur_entry) + return svn_error_createf + (SVN_ERR_FS_NOT_FOUND, NULL, + "Attempted to open non-existent child node '%s'", name); + + /* Check for mutability in the node we found. If it's mutable, we + don't need to clone it. */ + if (svn_fs_x__dag_check_mutable(cur_entry)) + { + /* This has already been cloned */ + new_node_id = svn_fs_x__dag_get_id(cur_entry); + } + else + { + svn_fs_x__noderev_t *noderev, *parent_noderev; + + /* Go get a fresh NODE-REVISION for current child node. */ + SVN_ERR(get_node_revision(&noderev, cur_entry)); + + if (is_parent_copyroot) + { + SVN_ERR(get_node_revision(&parent_noderev, parent)); + noderev->copyroot_rev = parent_noderev->copyroot_rev; + noderev->copyroot_path = apr_pstrdup(scratch_pool, + parent_noderev->copyroot_path); + } + + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + + noderev->predecessor_id = noderev->noderev_id; + noderev->predecessor_count++; + noderev->created_path = svn_fspath__join(parent_path, name, + scratch_pool); + + if (copy_id == NULL) + copy_id = &noderev->copy_id; + + SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id, + scratch_pool)); + new_node_id = &noderev->noderev_id; + + /* Replace the ID in the parent's ENTRY list with the ID which + refers to the mutable clone of this child. */ + SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id, + scratch_pool)); + } + + /* Initialize the youngster. */ + return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool, + scratch_pool); +} + + +/* Delete all mutable node revisions reachable from node ID, including + ID itself, from FS's `nodes' table. Also delete any mutable + representations and strings associated with that node revision. + ID may refer to a file or directory, which may be mutable or immutable. + + Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +delete_if_mutable(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *scratch_pool) +{ + dag_node_t *node; + + /* Get the node. */ + SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool)); + + /* If immutable, do nothing and return immediately. */ + if (! svn_fs_x__dag_check_mutable(node)) + return SVN_NO_ERROR; + + /* Else it's mutable. Recurse on directories... */ + if (node->kind == svn_node_dir) + { + apr_array_header_t *entries; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Loop over directory entries */ + SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool, + iterpool)); + for (i = 0; i < entries->nelts; ++i) + { + const svn_fs_x__id_t *noderev_id + = &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id; + + svn_pool_clear(iterpool); + SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool)); + } + + svn_pool_destroy(iterpool); + } + + /* ... then delete the node itself, after deleting any mutable + representations and strings it points to. */ + return svn_fs_x__delete_node_revision(fs, id, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_delete(dag_node_t *parent, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *parent_noderev; + svn_fs_t *fs = parent->fs; + svn_fs_x__dirent_t *dirent; + apr_pool_t *subpool; + + /* Make sure parent is a directory. */ + if (parent->kind != svn_node_dir) + return svn_error_createf + (SVN_ERR_FS_NOT_DIRECTORY, NULL, + "Attempted to delete entry '%s' from *non*-directory node", name); + + /* Make sure parent is mutable. */ + if (! svn_fs_x__dag_check_mutable(parent)) + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Attempted to delete entry '%s' from immutable directory node", name); + + /* Make sure that NAME is a single path component. */ + if (! svn_path_is_single_path_component(name)) + return svn_error_createf + (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, + "Attempted to delete a node with an illegal name '%s'", name); + + /* Get a fresh NODE-REVISION for the parent node. */ + SVN_ERR(get_node_revision(&parent_noderev, parent)); + + subpool = svn_pool_create(scratch_pool); + + /* Search this directory for a dirent with that NAME. */ + SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev, + name, &parent->hint, + subpool, subpool)); + + /* If we never found ID in ENTRIES (perhaps because there are no + ENTRIES, perhaps because ID just isn't in the existing ENTRIES + ... it doesn't matter), return an error. */ + if (! dirent) + return svn_error_createf + (SVN_ERR_FS_NO_SUCH_ENTRY, NULL, + "Delete failed--directory has no entry '%s'", name); + + /* If mutable, remove it and any mutable children from db. */ + SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, scratch_pool)); + svn_pool_destroy(subpool); + + /* Remove this entry from its parent's entries list. */ + return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, + NULL, svn_node_unknown, parent->node_pool, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_make_file(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* Call our little helper function */ + return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, + result_pool, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_make_dir(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* Call our little helper function */ + return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, + result_pool, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_get_contents(svn_stream_t **contents_p, + dag_node_t *file, + apr_pool_t *result_pool) +{ + svn_fs_x__noderev_t *noderev; + svn_stream_t *contents; + + /* Make sure our node is a file. */ + if (file->kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_NOT_FILE, NULL, + "Attempted to get textual contents of a *non*-file node"); + + /* Go get a fresh node-revision for FILE. */ + SVN_ERR(get_node_revision(&noderev, file)); + + /* Get a stream to the contents. */ + SVN_ERR(svn_fs_x__get_contents(&contents, file->fs, + noderev->data_rep, TRUE, result_pool)); + + *contents_p = contents; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p, + dag_node_t *source, + dag_node_t *target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *src_noderev; + svn_fs_x__noderev_t *tgt_noderev; + + /* Make sure our nodes are files. */ + if ((source && source->kind != svn_node_file) + || target->kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_NOT_FILE, NULL, + "Attempted to get textual contents of a *non*-file node"); + + /* Go get fresh node-revisions for the nodes. */ + if (source) + SVN_ERR(get_node_revision(&src_noderev, source)); + else + src_noderev = NULL; + SVN_ERR(get_node_revision(&tgt_noderev, target)); + + /* Get the delta stream. */ + return svn_fs_x__get_file_delta_stream(stream_p, target->fs, + src_noderev, tgt_noderev, + result_pool, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success, + dag_node_t *node, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + + /* Go get fresh node-revisions for the nodes. */ + SVN_ERR(get_node_revision(&noderev, node)); + + return svn_fs_x__try_process_file_contents(success, node->fs, + noderev, + processor, baton, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_file_length(svn_filesize_t *length, + dag_node_t *file) +{ + svn_fs_x__noderev_t *noderev; + + /* Make sure our node is a file. */ + if (file->kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_NOT_FILE, NULL, + "Attempted to get length of a *non*-file node"); + + /* Go get a fresh node-revision for FILE, and . */ + SVN_ERR(get_node_revision(&noderev, file)); + + return svn_fs_x__file_length(length, noderev); +} + + +svn_error_t * +svn_fs_x__dag_file_checksum(svn_checksum_t **checksum, + dag_node_t *file, + svn_checksum_kind_t kind, + apr_pool_t *result_pool) +{ + svn_fs_x__noderev_t *noderev; + + if (file->kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_NOT_FILE, NULL, + "Attempted to get checksum of a *non*-file node"); + + SVN_ERR(get_node_revision(&noderev, file)); + + return svn_fs_x__file_checksum(checksum, noderev, kind, result_pool); +} + + +svn_error_t * +svn_fs_x__dag_get_edit_stream(svn_stream_t **contents, + dag_node_t *file, + apr_pool_t *result_pool) +{ + svn_fs_x__noderev_t *noderev; + svn_stream_t *ws; + + /* Make sure our node is a file. */ + if (file->kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_NOT_FILE, NULL, + "Attempted to set textual contents of a *non*-file node"); + + /* Make sure our node is mutable. */ + if (! svn_fs_x__dag_check_mutable(file)) + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + "Attempted to set textual contents of an immutable node"); + + /* Get the node revision. */ + SVN_ERR(get_node_revision(&noderev, file)); + + SVN_ERR(svn_fs_x__set_contents(&ws, file->fs, noderev, result_pool)); + + *contents = ws; + + return SVN_NO_ERROR; +} + + + +svn_error_t * +svn_fs_x__dag_finalize_edits(dag_node_t *file, + const svn_checksum_t *checksum, + apr_pool_t *scratch_pool) +{ + if (checksum) + { + svn_checksum_t *file_checksum; + + SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file, + checksum->kind, scratch_pool)); + if (!svn_checksum_match(checksum, file_checksum)) + return svn_checksum_mismatch_err(checksum, file_checksum, + scratch_pool, + _("Checksum mismatch for '%s'"), + file->created_path); + } + + return SVN_NO_ERROR; +} + + +dag_node_t * +svn_fs_x__dag_dup(const dag_node_t *node, + apr_pool_t *result_pool) +{ + /* Allocate our new node. */ + dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node)); + + /* Only copy cached svn_fs_x__noderev_t for immutable nodes. */ + if (node->node_revision && !svn_fs_x__dag_check_mutable(node)) + { + new_node->node_revision = copy_node_revision(node->node_revision, + result_pool); + new_node->created_path = new_node->node_revision->created_path; + } + else + { + new_node->node_revision = NULL; + new_node->created_path = apr_pstrdup(result_pool, node->created_path); + } + + new_node->node_pool = result_pool; + + return new_node; +} + +dag_node_t * +svn_fs_x__dag_copy_into_pool(dag_node_t *node, + apr_pool_t *result_pool) +{ + return (node->node_pool == result_pool + ? node + : svn_fs_x__dag_dup(node, result_pool)); +} + +svn_error_t * +svn_fs_x__dag_serialize(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + dag_node_t *node = in; + svn_stringbuf_t *serialized; + + /* create an serialization context and serialize the dag node as root */ + svn_temp_serializer__context_t *context = + svn_temp_serializer__init(node, + sizeof(*node), + 1024 - SVN_TEMP_SERIALIZER__OVERHEAD, + pool); + + /* for mutable nodes, we will _never_ cache the noderev */ + if (node->node_revision && !svn_fs_x__dag_check_mutable(node)) + { + svn_fs_x__noderev_serialize(context, &node->node_revision); + } + else + { + svn_temp_serializer__set_null(context, + (const void * const *)&node->node_revision); + svn_temp_serializer__add_string(context, &node->created_path); + } + + /* The deserializer will use its own pool. */ + svn_temp_serializer__set_null(context, + (const void * const *)&node->node_pool); + + /* return serialized data */ + serialized = svn_temp_serializer__get(context); + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_deserialize(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + dag_node_t *node = (dag_node_t *)data; + if (data_len == 0) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Empty noderev in cache")); + + /* Copy the _full_ buffer as it also contains the sub-structures. */ + node->fs = NULL; + + /* fixup all references to sub-structures */ + svn_fs_x__noderev_deserialize(node, &node->node_revision, pool); + node->node_pool = pool; + + if (node->node_revision) + node->created_path = node->node_revision->created_path; + else + svn_temp_deserializer__resolve(node, (void**)&node->created_path); + + /* return result */ + *out = node; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_open(dag_node_t **child_p, + dag_node_t *parent, + const char *name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__id_t node_id; + + /* Ensure that NAME exists in PARENT's entry list. */ + SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, scratch_pool)); + if (! svn_fs_x__id_used(&node_id)) + { + *child_p = NULL; + return SVN_NO_ERROR; + } + + /* Now get the node that was requested. */ + return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent), + &node_id, result_pool, scratch_pool); +} + + +svn_error_t * +svn_fs_x__dag_copy(dag_node_t *to_node, + const char *entry, + dag_node_t *from_node, + svn_boolean_t preserve_history, + svn_revnum_t from_rev, + const char *from_path, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + const svn_fs_x__id_t *id; + + if (preserve_history) + { + svn_fs_x__noderev_t *from_noderev, *to_noderev; + svn_fs_x__id_t copy_id; + svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node); + + /* Make a copy of the original node revision. */ + SVN_ERR(get_node_revision(&from_noderev, from_node)); + to_noderev = copy_node_revision(from_noderev, scratch_pool); + + /* Reserve a copy ID for this new copy. */ + SVN_ERR(svn_fs_x__reserve_copy_id(©_id, fs, txn_id, scratch_pool)); + + /* Create a successor with its predecessor pointing at the copy + source. */ + to_noderev->predecessor_id = to_noderev->noderev_id; + to_noderev->predecessor_count++; + to_noderev->created_path = + svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry, + scratch_pool); + to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path); + to_noderev->copyfrom_rev = from_rev; + + /* Set the copyroot equal to our own id. */ + to_noderev->copyroot_path = NULL; + + SVN_ERR(svn_fs_x__create_successor(fs, to_noderev, + ©_id, txn_id, scratch_pool)); + id = &to_noderev->noderev_id; + } + else /* don't preserve history */ + { + id = svn_fs_x__dag_get_id(from_node); + } + + /* Set the entry in to_node to the new id. */ + return svn_fs_x__dag_set_entry(to_node, entry, id, from_node->kind, + txn_id, scratch_pool); +} + + + +/*** Comparison. ***/ + +svn_error_t * +svn_fs_x__dag_things_different(svn_boolean_t *props_changed, + svn_boolean_t *contents_changed, + dag_node_t *node1, + dag_node_t *node2, + svn_boolean_t strict, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev1, *noderev2; + svn_fs_t *fs; + svn_boolean_t same; + + /* If we have no place to store our results, don't bother doing + anything. */ + if (! props_changed && ! contents_changed) + return SVN_NO_ERROR; + + fs = svn_fs_x__dag_get_fs(node1); + + /* The node revision skels for these two nodes. */ + SVN_ERR(get_node_revision(&noderev1, node1)); + SVN_ERR(get_node_revision(&noderev2, node2)); + + /* Compare property keys. */ + if (props_changed != NULL) + { + SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2, + strict, scratch_pool)); + *props_changed = !same; + } + + /* Compare contents keys. */ + if (contents_changed != NULL) + *contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep, + noderev2->data_rep); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_get_copyroot(svn_revnum_t *rev, + const char **path, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + /* Go get a fresh node-revision for NODE. */ + SVN_ERR(get_node_revision(&noderev, node)); + + *rev = noderev->copyroot_rev; + *path = noderev->copyroot_path; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + /* Go get a fresh node-revision for NODE. */ + SVN_ERR(get_node_revision(&noderev, node)); + + *rev = noderev->copyfrom_rev; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_get_copyfrom_path(const char **path, + dag_node_t *node) +{ + svn_fs_x__noderev_t *noderev; + + /* Go get a fresh node-revision for NODE. */ + SVN_ERR(get_node_revision(&noderev, node)); + + *path = noderev->copyfrom_path; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__dag_update_ancestry(dag_node_t *target, + dag_node_t *source, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *source_noderev, *target_noderev; + + if (! svn_fs_x__dag_check_mutable(target)) + return svn_error_createf + (SVN_ERR_FS_NOT_MUTABLE, NULL, + _("Attempted to update ancestry of non-mutable node")); + + SVN_ERR(get_node_revision(&source_noderev, source)); + SVN_ERR(get_node_revision(&target_noderev, target)); + + target_noderev->predecessor_id = source_noderev->noderev_id; + target_noderev->predecessor_count = source_noderev->predecessor_count; + target_noderev->predecessor_count++; + + return svn_fs_x__put_node_revision(target->fs, target_noderev, + scratch_pool); +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/dag.h b/contrib/subversion/subversion/libsvn_fs_x/dag.h new file mode 100644 index 000000000..6d5e85baf --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/dag.h @@ -0,0 +1,580 @@ +/* dag.h : DAG-like interface filesystem, private to libsvn_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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS_DAG_H +#define SVN_LIBSVN_FS_DAG_H + +#include "svn_fs.h" +#include "svn_delta.h" +#include "private/svn_cache.h" + +#include "fs.h" +#include "id.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* The interface in this file provides all the essential filesystem + operations, but exposes the filesystem's DAG structure. This makes + it simpler to implement than the public interface, since a client + of this interface has to understand and cope with shared structure + directly as it appears in the database. However, it's still a + self-consistent set of invariants to maintain, making it + (hopefully) a useful interface boundary. + + In other words: + + - The dag_node_t interface exposes the internal DAG structure of + the filesystem, while the svn_fs.h interface does any cloning + necessary to make the filesystem look like a tree. + + - The dag_node_t interface exposes the existence of copy nodes, + whereas the svn_fs.h handles them transparently. + + - dag_node_t's must be explicitly cloned, whereas the svn_fs.h + operations make clones implicitly. + + - Callers of the dag_node_t interface use Berkeley DB transactions + to ensure consistency between operations, while callers of the + svn_fs.h interface use Subversion transactions. */ + + +/* Generic DAG node stuff. */ + +typedef struct dag_node_t dag_node_t; + +/* Fill *NODE with a dag_node_t representing node revision ID in FS, + allocating in RESULT_POOL. Use SCRATCH_POOL for temporaries. */ +svn_error_t * +svn_fs_x__dag_get_node(dag_node_t **node, + svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Return a new dag_node_t object referring to the same node as NODE, + allocated in RESULT_POOL. If you're trying to build a structure in a + pool that wants to refer to dag nodes that may have been allocated + elsewhere, you can call this function and avoid inter-pool pointers. */ +dag_node_t * +svn_fs_x__dag_dup(const dag_node_t *node, + apr_pool_t *result_pool); + +/* If NODE has been allocated in POOL, return NODE. Otherwise, return + a copy created in RESULT_POOL with svn_fs_fs__dag_dup. */ +dag_node_t * +svn_fs_x__dag_copy_into_pool(dag_node_t *node, + apr_pool_t *result_pool); + +/* Serialize a DAG node, except don't try to preserve the 'fs' member. + Implements svn_cache__serialize_func_t */ +svn_error_t * +svn_fs_x__dag_serialize(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* Deserialize a DAG node, leaving the 'fs' member as NULL. + Implements svn_cache__deserialize_func_t */ +svn_error_t * +svn_fs_x__dag_deserialize(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* Return the filesystem containing NODE. */ +svn_fs_t * +svn_fs_x__dag_get_fs(dag_node_t *node); + +/* Changes the filesystem containing NODE to FS. (Used when pulling + nodes out of a shared cache, say.) */ +void +svn_fs_x__dag_set_fs(dag_node_t *node, + svn_fs_t *fs); + + +/* Return NODE's revision number. If NODE has never been committed as + part of a revision, set *REV to SVN_INVALID_REVNUM. */ +svn_revnum_t +svn_fs_x__dag_get_revision(const dag_node_t *node); + + +/* Return the node revision ID of NODE. The value returned is shared + with NODE, and will be deallocated when NODE is. */ +const svn_fs_x__id_t * +svn_fs_x__dag_get_id(const dag_node_t *node); + +/* Return the node ID of NODE. The value returned is shared with NODE, + and will be deallocated when NODE is. */ +svn_error_t * +svn_fs_x__dag_get_node_id(svn_fs_x__id_t *node_id, + dag_node_t *node); + +/* Return the copy ID of NODE. The value returned is shared with NODE, + and will be deallocated when NODE is. */ +svn_error_t * +svn_fs_x__dag_get_copy_id(svn_fs_x__id_t *copy_id, + dag_node_t *node); + +/* Set *SAME to TRUE, if nodes LHS and RHS have the same node ID. */ +svn_error_t * +svn_fs_x__dag_related_node(svn_boolean_t *same, + dag_node_t *lhs, + dag_node_t *rhs); + +/* Set *SAME to TRUE, if nodes LHS and RHS have the same node and copy IDs. + */ +svn_error_t * +svn_fs_x__dag_same_line_of_history(svn_boolean_t *same, + dag_node_t *lhs, + dag_node_t *rhs); + +/* Return the created path of NODE. The value returned is shared + with NODE, and will be deallocated when NODE is. */ +const char * +svn_fs_x__dag_get_created_path(dag_node_t *node); + + +/* Set *ID_P to the node revision ID of NODE's immediate predecessor. + */ +svn_error_t * +svn_fs_x__dag_get_predecessor_id(svn_fs_x__id_t *id_p, + dag_node_t *node); + + +/* Set *COUNT to the number of predecessors NODE has (recursively). + */ +/* ### This function is currently only used by 'verify'. */ +svn_error_t * +svn_fs_x__dag_get_predecessor_count(int *count, + dag_node_t *node); + +/* Set *COUNT to the number of node under NODE (inclusive) with + svn:mergeinfo properties. + */ +svn_error_t * +svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count, + dag_node_t *node); + +/* Set *DO_THEY to a flag indicating whether or not NODE is a + directory with at least one descendant (not including itself) with + svn:mergeinfo. + */ +svn_error_t * +svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they, + dag_node_t *node); + +/* Set *HAS_MERGEINFO to a flag indicating whether or not NODE itself + has svn:mergeinfo set on it. + */ +svn_error_t * +svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo, + dag_node_t *node); + +/* Return non-zero IFF NODE is currently mutable. */ +svn_boolean_t +svn_fs_x__dag_check_mutable(const dag_node_t *node); + +/* Return the node kind of NODE. */ +svn_node_kind_t +svn_fs_x__dag_node_kind(dag_node_t *node); + +/* Set *PROPLIST_P to a PROPLIST hash representing the entire property + list of NODE, allocating from POOL. The hash has const char * + names (the property names) and svn_string_t * values (the property + values). + + If properties do not exist on NODE, *PROPLIST_P will be set to + NULL. + + Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporaries. + */ +svn_error_t * +svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p, + dag_node_t *node, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set the property list of NODE to PROPLIST, allocating from POOL. + The node being changed must be mutable. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_set_proplist(dag_node_t *node, + apr_hash_t *proplist, + apr_pool_t *scratch_pool); + +/* Increment the mergeinfo_count field on NODE by INCREMENT. The node + being changed must be mutable. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node, + apr_int64_t increment, + apr_pool_t *scratch_pool); + +/* Set the has-mergeinfo flag on NODE to HAS_MERGEINFO. The node + being changed must be mutable. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node, + svn_boolean_t has_mergeinfo, + apr_pool_t *scratch_pool); + + + +/* Revision and transaction roots. */ + + +/* Open the root of revision REV of filesystem FS, allocating from + RESULT_POOL. Set *NODE_P to the new node. Use SCRATCH_POOL for + temporary allocations.*/ +svn_error_t * +svn_fs_x__dag_revision_root(dag_node_t **node_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Set *NODE_P to the root of transaction TXN_ID in FS, allocating + from RESULT_POOL. Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__dag_txn_root(dag_node_t **node_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Directories. */ + + +/* Open the node named NAME in the directory PARENT. Set *CHILD_P to + the new node, allocated in RESULT_POOL. NAME must be a single path + component; it cannot be a slash-separated directory path. If NAME does + not exist within PARENT, set *CHILD_P to NULL. + */ +svn_error_t * +svn_fs_x__dag_open(dag_node_t **child_p, + dag_node_t *parent, + const char *name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Set *ENTRIES_P to an array of NODE's entries, sorted by entry names, + and the values are svn_fs_x__dirent_t. The returned table (and elements) + is allocated in RESULT_POOL, temporaries in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__dag_dir_entries(apr_array_header_t **entries_p, + dag_node_t *node, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set ENTRY_NAME in NODE to point to ID (with kind KIND), allocating + from POOL. NODE must be a mutable directory. ID can refer to a + mutable or immutable node. If ENTRY_NAME does not exist, it will + be created. TXN_ID is the Subversion transaction under which this + occurs. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_set_entry(dag_node_t *node, + const char *entry_name, + const svn_fs_x__id_t *id, + svn_node_kind_t kind, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + + +/* Make a new mutable clone of the node named NAME in PARENT, and + adjust PARENT's directory entry to point to it, unless NAME in + PARENT already refers to a mutable node. In either case, set + *CHILD_P to a reference to the new node, allocated in POOL. PARENT + must be mutable. NAME must be a single path component; it cannot + be a slash-separated directory path. PARENT_PATH must be the + canonicalized absolute path of the parent directory. + + COPY_ID, if non-NULL, is a key into the `copies' table, and + indicates that this new node is being created as the result of a + copy operation, and specifically which operation that was. + + PATH is the canonicalized absolute path at which this node is being + created. + + TXN_ID is the Subversion transaction under which this occurs. + + Allocate *CHILD_P in RESULT_POOL and use SCRATCH_POOL for temporaries. + */ +svn_error_t * +svn_fs_x__dag_clone_child(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + svn_boolean_t is_parent_copyroot, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Delete the directory entry named NAME from PARENT, allocating from + POOL. PARENT must be mutable. NAME must be a single path + component; it cannot be a slash-separated directory path. If the + node being deleted is a mutable directory, remove all mutable nodes + reachable from it. TXN_ID is the Subversion transaction under + which this occurs. + + If return SVN_ERR_FS_NO_SUCH_ENTRY, then there is no entry NAME in + PARENT. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_delete(dag_node_t *parent, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + + +/* Create a new mutable directory named NAME in PARENT. Set *CHILD_P + to a reference to the new node, allocated in RESULT_POOL. The new + directory has no contents, and no properties. PARENT must be + mutable. NAME must be a single path component; it cannot be a + slash-separated directory path. PARENT_PATH must be the + canonicalized absolute path of the parent directory. PARENT must + not currently have an entry named NAME. TXN_ID is the Subversion + transaction under which this occurs. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_make_dir(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + + +/* Files. */ + + +/* Set *CONTENTS to a readable generic stream which yields the + contents of FILE. Allocate the stream in RESULT_POOL. + + If FILE is not a file, return SVN_ERR_FS_NOT_FILE. + */ +svn_error_t * +svn_fs_x__dag_get_contents(svn_stream_t **contents, + dag_node_t *file, + apr_pool_t *result_pool); + +/* Attempt to fetch the contents of NODE and pass it along with the BATON + to the PROCESSOR. Set *SUCCESS only of the data could be provided + and the processor had been called. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success, + dag_node_t *node, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *scratch_pool); + + +/* Set *STREAM_P to a delta stream that will turn the contents of SOURCE into + the contents of TARGET, allocated in RESULT_POOL. If SOURCE is null, the + empty string will be used is its stead. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p, + dag_node_t *source, + dag_node_t *target, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return a generic writable stream in *CONTENTS with which to set the + contents of FILE. Allocate the stream in RESULT_POOL. + + Any previous edits on the file will be deleted, and a new edit + stream will be constructed. + */ +svn_error_t * +svn_fs_x__dag_get_edit_stream(svn_stream_t **contents, + dag_node_t *file, + apr_pool_t *result_pool); + + +/* Signify the completion of edits to FILE made using the stream + returned by svn_fs_x__dag_get_edit_stream. + + If CHECKSUM is non-null, it must match the checksum for FILE's + contents (note: this is not recalculated, the recorded checksum is + used), else the error SVN_ERR_CHECKSUM_MISMATCH is returned. + + This operation is a no-op if no edits are present. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_finalize_edits(dag_node_t *file, + const svn_checksum_t *checksum, + apr_pool_t *scratch_pool); + + +/* Set *LENGTH to the length of the contents of FILE. + */ +svn_error_t * +svn_fs_x__dag_file_length(svn_filesize_t *length, + dag_node_t *file); + +/* Put the recorded checksum of type KIND for FILE into CHECKSUM, allocating + from RESULT_POOL. + + If no stored checksum is available, do not calculate the checksum, + just put NULL into CHECKSUM. + */ +svn_error_t * +svn_fs_x__dag_file_checksum(svn_checksum_t **checksum, + dag_node_t *file, + svn_checksum_kind_t kind, + apr_pool_t *result_pool); + +/* Create a new mutable file named NAME in PARENT. Set *CHILD_P to a + reference to the new node, allocated in RESULT_POOL. The new file's + contents are the empty string, and it has no properties. PARENT + must be mutable. NAME must be a single path component; it cannot + be a slash-separated directory path. PARENT_PATH must be the + canonicalized absolute path of the parent directory. TXN_ID is the + Subversion transaction under which this occurs. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_make_file(dag_node_t **child_p, + dag_node_t *parent, + const char *parent_path, + const char *name, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + + +/* Copies */ + +/* Make ENTRY in TO_NODE be a copy of FROM_NODE. TO_NODE must be mutable. + TXN_ID is the Subversion transaction under which this occurs. + + If PRESERVE_HISTORY is true, the new node will record that it was + copied from FROM_PATH in FROM_REV; therefore, FROM_NODE should be + the node found at FROM_PATH in FROM_REV, although this is not + checked. FROM_PATH should be canonicalized before being passed + here. + + If PRESERVE_HISTORY is false, FROM_PATH and FROM_REV are ignored. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_copy(dag_node_t *to_node, + const char *entry, + dag_node_t *from_node, + svn_boolean_t preserve_history, + svn_revnum_t from_rev, + const char *from_path, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + + +/* Comparison */ + +/* Find out what is the same between two nodes. If STRICT is FALSE, + this function may report false positives, i.e. report changes even + if the resulting contents / props are equal. + + If PROPS_CHANGED is non-null, set *PROPS_CHANGED to 1 if the two + nodes have different property lists, or to 0 if same. + + If CONTENTS_CHANGED is non-null, set *CONTENTS_CHANGED to 1 if the + two nodes have different contents, or to 0 if same. NODE1 and NODE2 + must refer to files from the same filesystem. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_things_different(svn_boolean_t *props_changed, + svn_boolean_t *contents_changed, + dag_node_t *node1, + dag_node_t *node2, + svn_boolean_t strict, + apr_pool_t *scratch_pool); + + +/* Set *REV and *PATH to the copyroot revision and path of node NODE, or + to SVN_INVALID_REVNUM and NULL if no copyroot exists. + */ +svn_error_t * +svn_fs_x__dag_get_copyroot(svn_revnum_t *rev, + const char **path, + dag_node_t *node); + +/* Set *REV to the copyfrom revision associated with NODE. + */ +svn_error_t * +svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev, + dag_node_t *node); + +/* Set *PATH to the copyfrom path associated with NODE. + */ +svn_error_t * +svn_fs_x__dag_get_copyfrom_path(const char **path, + dag_node_t *node); + +/* Update *TARGET so that SOURCE is it's predecessor. + + Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__dag_update_ancestry(dag_node_t *target, + dag_node_t *source, + apr_pool_t *scratch_pool); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_DAG_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs.c b/contrib/subversion/subversion/libsvn_fs_x/fs.c new file mode 100644 index 000000000..abc564db3 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs.c @@ -0,0 +1,669 @@ +/* fs.c --- creating, opening and closing filesystems + * + * ==================================================================== + * 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 +#include +#include + +#include +#include +#include +#include + +#include "svn_fs.h" +#include "svn_delta.h" +#include "svn_version.h" +#include "svn_pools.h" +#include "fs.h" +#include "fs_x.h" +#include "pack.h" +#include "recovery.h" +#include "hotcopy.h" +#include "verify.h" +#include "tree.h" +#include "lock.h" +#include "id.h" +#include "revprops.h" +#include "rep-cache.h" +#include "transaction.h" +#include "util.h" +#include "svn_private_config.h" +#include "private/svn_fs_util.h" + +#include "../libsvn_fs/fs-loader.h" + +/* A prefix for the pool userdata variables used to hold + per-filesystem shared data. See fs_serialized_init. */ +#define SVN_FSX_SHARED_USERDATA_PREFIX "svn-fsx-shared-" + + + +/* Initialize the part of FS that requires global serialization across all + instances. The caller is responsible of ensuring that serialization. + Use COMMON_POOL for process-wide and SCRATCH_POOL for temporary + allocations. */ +static svn_error_t * +x_serialized_init(svn_fs_t *fs, + apr_pool_t *common_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + const char *key; + void *val; + svn_fs_x__shared_data_t *ffsd; + apr_status_t status; + + /* Note that we are allocating a small amount of long-lived data for + each separate repository opened during the lifetime of the + svn_fs_initialize pool. It's unlikely that anyone will notice + the modest expenditure; the alternative is to allocate each structure + in a subpool, add a reference-count, and add a serialized destructor + to the FS vtable. That's more machinery than it's worth. + + Picking an appropriate key for the shared data is tricky, because, + unfortunately, a filesystem UUID is not really unique. It is implicitly + shared between hotcopied (1), dump / loaded (2) or naively copied (3) + filesystems. We tackle this problem by using a combination of the UUID + and an instance ID as the key. This allows us to avoid key clashing + in (1) and (2). + + Speaking of (3), there is not so much we can do about it, except maybe + provide a convenient way of fixing things. Naively copied filesystems + have identical filesystem UUIDs *and* instance IDs. With the key being + a combination of these two, clashes can be fixed by changing either of + them (or both), e.g. with svn_fs_set_uuid(). */ + + + SVN_ERR_ASSERT(fs->uuid); + SVN_ERR_ASSERT(ffd->instance_id); + + key = apr_pstrcat(scratch_pool, SVN_FSX_SHARED_USERDATA_PREFIX, + fs->uuid, ":", ffd->instance_id, SVN_VA_NULL); + status = apr_pool_userdata_get(&val, key, common_pool); + if (status) + return svn_error_wrap_apr(status, _("Can't fetch FSX shared data")); + ffsd = val; + + if (!ffsd) + { + ffsd = apr_pcalloc(common_pool, sizeof(*ffsd)); + ffsd->common_pool = common_pool; + + /* POSIX fcntl locks are per-process, so we need a mutex for + intra-process synchronization when grabbing the repository write + lock. */ + SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); + + /* ... the pack lock ... */ + SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); + + /* ... not to mention locking the txn-current file. */ + SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); + + /* We also need a mutex for synchronizing access to the active + transaction list and free transaction pointer. */ + SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool)); + + key = apr_pstrdup(common_pool, key); + status = apr_pool_userdata_set(ffsd, key, NULL, common_pool); + if (status) + return svn_error_wrap_apr(status, _("Can't store FSX shared data")); + } + + ffd->shared = ffsd; + + return SVN_NO_ERROR; +} + + + +/* This function is provided for Subversion 1.0.x compatibility. It + has no effect for fsx backed Subversion filesystems. It conforms + to the fs_library_vtable_t.bdb_set_errcall() API. */ +static svn_error_t * +x_set_errcall(svn_fs_t *fs, + void (*db_errcall_fcn)(const char *errpfx, char *msg)) +{ + + return SVN_NO_ERROR; +} + +typedef struct x_freeze_baton_t { + svn_fs_t *fs; + svn_fs_freeze_func_t freeze_func; + void *freeze_baton; +} x_freeze_baton_t; + +static svn_error_t * +x_freeze_body(void *baton, + apr_pool_t *scratch_pool) +{ + x_freeze_baton_t *b = baton; + svn_boolean_t exists; + + SVN_ERR(svn_fs_x__exists_rep_cache(&exists, b->fs, scratch_pool)); + if (exists) + SVN_ERR(svn_fs_x__with_rep_cache_lock(b->fs, + b->freeze_func, b->freeze_baton, + scratch_pool)); + else + SVN_ERR(b->freeze_func(b->freeze_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +x_freeze_body2(void *baton, + apr_pool_t *scratch_pool) +{ + x_freeze_baton_t *b = baton; + SVN_ERR(svn_fs_x__with_write_lock(b->fs, x_freeze_body, baton, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +x_freeze(svn_fs_t *fs, + svn_fs_freeze_func_t freeze_func, + void *freeze_baton, + apr_pool_t *scratch_pool) +{ + x_freeze_baton_t b; + + b.fs = fs; + b.freeze_func = freeze_func; + b.freeze_baton = freeze_baton; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + SVN_ERR(svn_fs_x__with_pack_lock(fs, x_freeze_body2, &b, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +x_info(const void **fsx_info, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_fsx_info_t *info = apr_palloc(result_pool, sizeof(*info)); + info->fs_type = SVN_FS_TYPE_FSX; + info->shard_size = ffd->max_files_per_dir; + info->min_unpacked_rev = ffd->min_unpacked_rev; + *fsx_info = info; + return SVN_NO_ERROR; +} + +/* Wrapper around svn_fs_x__revision_prop() adapting between function + signatures. */ +static svn_error_t * +x_revision_prop(svn_string_t **value_p, + svn_fs_t *fs, + svn_revnum_t rev, + const char *propname, + apr_pool_t *pool) +{ + apr_pool_t *scratch_pool = svn_pool_create(pool); + SVN_ERR(svn_fs_x__revision_prop(value_p, fs, rev, propname, pool, + scratch_pool)); + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +/* Wrapper around svn_fs_x__get_revision_proplist() adapting between function + signatures. */ +static svn_error_t * +x_revision_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + apr_pool_t *scratch_pool = svn_pool_create(pool); + + /* No need to bypass the caches for r/o access to revprops. */ + SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE, + pool, scratch_pool)); + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +/* Wrapper around svn_fs_x__set_uuid() adapting between function + signatures. */ +static svn_error_t * +x_set_uuid(svn_fs_t *fs, + const char *uuid, + apr_pool_t *scratch_pool) +{ + /* Whenever we set a new UUID, imply that FS will also be a different + * instance (on formats that support this). */ + return svn_error_trace(svn_fs_x__set_uuid(fs, uuid, NULL, scratch_pool)); +} + +/* Wrapper around svn_fs_x__begin_txn() providing the scratch pool. */ +static svn_error_t * +x_begin_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_uint32_t flags, + apr_pool_t *pool) +{ + apr_pool_t *scratch_pool = svn_pool_create(pool); + SVN_ERR(svn_fs_x__begin_txn(txn_p, fs, rev, flags, pool, scratch_pool)); + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + + + +/* The vtable associated with a specific open filesystem. */ +static fs_vtable_t fs_vtable = { + svn_fs_x__youngest_rev, + x_revision_prop, + x_revision_proplist, + svn_fs_x__change_rev_prop, + x_set_uuid, + svn_fs_x__revision_root, + x_begin_txn, + svn_fs_x__open_txn, + svn_fs_x__purge_txn, + svn_fs_x__list_transactions, + svn_fs_x__deltify, + svn_fs_x__lock, + svn_fs_x__generate_lock_token, + svn_fs_x__unlock, + svn_fs_x__get_lock, + svn_fs_x__get_locks, + svn_fs_x__info_format, + svn_fs_x__info_config_files, + x_info, + svn_fs_x__verify_root, + x_freeze, + x_set_errcall +}; + + +/* Creating a new filesystem. */ + +/* Set up vtable and fsap_data fields in FS. */ +static svn_error_t * +initialize_fs_struct(svn_fs_t *fs) +{ + svn_fs_x__data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); + fs->vtable = &fs_vtable; + fs->fsap_data = ffd; + return SVN_NO_ERROR; +} + +/* Reset vtable and fsap_data fields in FS such that the FS is basically + * closed now. Note that FS must not hold locks when you call this. */ +static void +uninitialize_fs_struct(svn_fs_t *fs) +{ + fs->vtable = NULL; + fs->fsap_data = NULL; +} + +/* This implements the fs_library_vtable_t.create() API. Create a new + fsx-backed Subversion filesystem at path PATH and link it into + *FS. + + Perform temporary allocations in SCRATCH_POOL, and fs-global allocations + in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */ +static svn_error_t * +x_create(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + SVN_ERR(svn_fs__check_fs(fs, FALSE)); + + SVN_ERR(initialize_fs_struct(fs)); + + SVN_ERR(svn_fs_x__create(fs, path, scratch_pool)); + + SVN_ERR(svn_fs_x__initialize_caches(fs, scratch_pool)); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + x_serialized_init(fs, common_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + + + +/* Gaining access to an existing filesystem. */ + +/* This implements the fs_library_vtable_t.open() API. Open an FSX + Subversion filesystem located at PATH, set *FS to point to the + correct vtable for the filesystem. Use SCRATCH_POOL for any temporary + allocations, and COMMON_POOL for fs-global allocations. + The latter must be serialized using COMMON_POOL_LOCK. */ +static svn_error_t * +x_open(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_fs__check_fs(fs, FALSE)); + + SVN_ERR(initialize_fs_struct(fs)); + + SVN_ERR(svn_fs_x__open(fs, path, subpool)); + + SVN_ERR(svn_fs_x__initialize_caches(fs, subpool)); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + x_serialized_init(fs, common_pool, subpool)); + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + + + +/* This implements the fs_library_vtable_t.open_for_recovery() API. */ +static svn_error_t * +x_open_for_recovery(svn_fs_t *fs, + const char *path, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + svn_error_t * err; + svn_revnum_t youngest_rev; + apr_pool_t * subpool = svn_pool_create(scratch_pool); + + /* Recovery for FSFS is currently limited to recreating the 'current' + file from the latest revision. */ + + /* The only thing we have to watch out for is that the 'current' file + might not exist or contain garbage. So we'll try to read it here + and provide or replace the existing file if we couldn't read it. + (We'll also need it to exist later anyway as a source for the new + file's permissions). */ + + /* Use a partly-filled fs pointer first to create 'current'. */ + fs->path = apr_pstrdup(fs->pool, path); + + SVN_ERR(initialize_fs_struct(fs)); + + /* Figure out the repo format and check that we can even handle it. */ + SVN_ERR(svn_fs_x__read_format_file(fs, subpool)); + + /* Now, read 'current' and try to patch it if necessary. */ + err = svn_fs_x__youngest_rev(&youngest_rev, fs, subpool); + if (err) + { + const char *file_path; + + /* 'current' file is missing or contains garbage. Since we are trying + * to recover from whatever problem there is, being picky about the + * error code here won't do us much good. If there is a persistent + * problem that we can't fix, it will show up when we try rewrite the + * file a few lines further below and we will report the failure back + * to the caller. + * + * Start recovery with HEAD = 0. */ + svn_error_clear(err); + file_path = svn_fs_x__path_current(fs, subpool); + + /* Best effort to ensure the file exists and is valid. + * This may fail for r/o filesystems etc. */ + SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool)); + SVN_ERR(svn_io_file_create_empty(file_path, subpool)); + SVN_ERR(svn_fs_x__write_current(fs, 0, subpool)); + } + + uninitialize_fs_struct(fs); + svn_pool_destroy(subpool); + + /* Now open the filesystem properly by calling the vtable method directly. */ + return x_open(fs, path, common_pool_lock, scratch_pool, common_pool); +} + + + +/* This implements the fs_library_vtable_t.upgrade_fs() API. */ +static svn_error_t * +x_upgrade(svn_fs_t *fs, + const char *path, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool)); + return svn_fs_x__upgrade(fs, notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool); +} + +static svn_error_t * +x_verify(svn_fs_t *fs, + const char *path, + 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, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool)); + return svn_fs_x__verify(fs, start, end, notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool); +} + +static svn_error_t * +x_pack(svn_fs_t *fs, + const char *path, + svn_fs_pack_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool)); + return svn_fs_x__pack(fs, notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool); +} + + + + +/* This implements the fs_library_vtable_t.hotcopy() API. Copy a + possibly live Subversion filesystem SRC_FS from SRC_PATH to a + DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to + re-copy data which already exists in DST_FS. + The CLEAN_LOGS argument is ignored and included for Subversion + 1.0.x compatibility. The NOTIFY_FUNC and NOTIFY_BATON arguments + are also currently ignored. + Perform all temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +x_hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *src_path, + const char *dst_path, + svn_boolean_t clean_logs, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_mutex__t *common_pool_lock, + apr_pool_t *scratch_pool, + apr_pool_t *common_pool) +{ + /* Open the source repo as usual. */ + SVN_ERR(x_open(src_fs, src_path, common_pool_lock, scratch_pool, + common_pool)); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Test target repo when in INCREMENTAL mode, initialize it when not. + * For this, we need our FS internal data structures to be temporarily + * available. */ + SVN_ERR(initialize_fs_struct(dst_fs)); + SVN_ERR(svn_fs_x__hotcopy_prepare_target(src_fs, dst_fs, dst_path, + incremental, scratch_pool)); + uninitialize_fs_struct(dst_fs); + + /* Now, the destination repo should open just fine. */ + SVN_ERR(x_open(dst_fs, dst_path, common_pool_lock, scratch_pool, + common_pool)); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Now, we may copy data as needed ... */ + return svn_fs_x__hotcopy(src_fs, dst_fs, incremental, + notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool); +} + + + +/* This function is included for Subversion 1.0.x compatibility. It + has no effect for fsx backed Subversion filesystems. It conforms + to the fs_library_vtable_t.bdb_logfiles() API. */ +static svn_error_t * +x_logfiles(apr_array_header_t **logfiles, + const char *path, + svn_boolean_t only_unused, + apr_pool_t *pool) +{ + /* A no-op for FSX. */ + *logfiles = apr_array_make(pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; +} + + + + + +/* Delete the filesystem located at path PATH. Perform any temporary + allocations in SCRATCH_POOL. */ +static svn_error_t * +x_delete_fs(const char *path, + apr_pool_t *scratch_pool) +{ + /* Remove everything. */ + return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, + scratch_pool)); +} + +static const svn_version_t * +x_version(void) +{ + SVN_VERSION_BODY; +} + +static const char * +x_get_description(void) +{ + return _("Module for working with an experimental (FSX) repository."); +} + +static svn_error_t * +x_set_svn_fs_open(svn_fs_t *fs, + svn_error_t *(*svn_fs_open_)(svn_fs_t **, + const char *, + apr_hash_t *, + apr_pool_t *, + apr_pool_t *)) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + ffd->svn_fs_open_ = svn_fs_open_; + return SVN_NO_ERROR; +} + +static void * +x_info_dup(const void *fsx_info_void, + apr_pool_t *result_pool) +{ + /* All fields are either ints or static strings. */ + const svn_fs_fsx_info_t *fsx_info = fsx_info_void; + return apr_pmemdup(result_pool, fsx_info, sizeof(*fsx_info)); +} + + +/* Base FS library vtable, used by the FS loader library. */ + +static fs_library_vtable_t library_vtable = { + x_version, + x_create, + x_open, + x_open_for_recovery, + x_upgrade, + x_verify, + x_delete_fs, + x_hotcopy, + x_get_description, + svn_fs_x__recover, + x_pack, + x_logfiles, + NULL /* parse_id */, + x_set_svn_fs_open, + x_info_dup +}; + +svn_error_t * +svn_fs_x__init(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, + apr_pool_t* common_pool) +{ + static const svn_version_checklist_t checklist[] = + { + { "svn_subr", svn_subr_version }, + { "svn_delta", svn_delta_version }, + { "svn_fs_util", svn_fs_util__version }, + { NULL, NULL } + }; + + /* Simplified version check to make sure we can safely use the + VTABLE parameter. The FS loader does a more exhaustive check. */ + if (loader_version->major != SVN_VER_MAJOR) + return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, + _("Unsupported FS loader version (%d) for fsx"), + loader_version->major); + SVN_ERR(svn_ver_check_list2(x_version(), checklist, svn_ver_equal)); + + *vtable = &library_vtable; + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs.h b/contrib/subversion/subversion/libsvn_fs_x/fs.h new file mode 100644 index 000000000..afb4b2a63 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs.h @@ -0,0 +1,574 @@ +/* fs.h : interface to Subversion filesystem, private to libsvn_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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS_X_H +#define SVN_LIBSVN_FS_X_H + +#include +#include +#include +#include +#include + +#include "svn_fs.h" +#include "svn_config.h" +#include "private/svn_atomic.h" +#include "private/svn_cache.h" +#include "private/svn_fs_private.h" +#include "private/svn_sqlite.h" +#include "private/svn_mutex.h" + +#include "id.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*** The filesystem structure. ***/ + +/* Following are defines that specify the textual elements of the + native filesystem directories and revision files. */ + +/* Names of special files in the fs_x filesystem. */ +#define PATH_FORMAT "format" /* Contains format number */ +#define PATH_UUID "uuid" /* Contains UUID */ +#define PATH_CURRENT "current" /* Youngest revision */ +#define PATH_LOCK_FILE "write-lock" /* Revision lock file */ +#define PATH_PACK_LOCK_FILE "pack-lock" /* Pack lock file */ +#define PATH_REVS_DIR "revs" /* Directory of revisions */ +#define PATH_REVPROPS_DIR "revprops" /* Directory of revprops */ +#define PATH_TXNS_DIR "transactions" /* Directory of transactions */ +#define PATH_NODE_ORIGINS_DIR "node-origins" /* Lazy node-origin cache */ +#define PATH_TXN_PROTOS_DIR "txn-protorevs" /* Directory of proto-revs */ +#define PATH_TXN_CURRENT "txn-current" /* File with next txn key */ +#define PATH_TXN_CURRENT_LOCK "txn-current-lock" /* Lock for txn-current */ +#define PATH_LOCKS_DIR "locks" /* Directory of locks */ +#define PATH_MIN_UNPACKED_REV "min-unpacked-rev" /* Oldest revision which + has not been packed. */ +#define PATH_REVPROP_GENERATION "revprop-generation" + /* Current revprop generation*/ +#define PATH_MANIFEST "manifest" /* Manifest file name */ +#define PATH_PACKED "pack" /* Packed revision data file */ +#define PATH_EXT_PACKED_SHARD ".pack" /* Extension for packed + shards */ +#define PATH_EXT_L2P_INDEX ".l2p" /* extension of the log- + to-phys index */ +#define PATH_EXT_P2L_INDEX ".p2l" /* extension of the phys- + to-log index */ +/* If you change this, look at tests/svn_test_fs.c(maybe_install_fsx_conf) */ +#define PATH_CONFIG "fsx.conf" /* Configuration */ + +/* Names of special files and file extensions for transactions */ +#define PATH_CHANGES "changes" /* Records changes made so far */ +#define PATH_TXN_PROPS "props" /* Transaction properties */ +#define PATH_TXN_PROPS_FINAL "props-final" /* Final transaction properties + before moving to revprops */ +#define PATH_NEXT_IDS "next-ids" /* Next temporary ID assignments */ +#define PATH_PREFIX_NODE "node." /* Prefix for node filename */ +#define PATH_EXT_TXN ".txn" /* Extension of txn dir */ +#define PATH_EXT_CHILDREN ".children" /* Extension for dir contents */ +#define PATH_EXT_PROPS ".props" /* Extension for node props */ +#define PATH_EXT_REV ".rev" /* Extension of protorev file */ +#define PATH_EXT_REV_LOCK ".rev-lock" /* Extension of protorev lock file */ +#define PATH_TXN_ITEM_INDEX "itemidx" /* File containing the current item + index number */ +#define PATH_INDEX "index" /* name of index files w/o ext */ + +/* Names of files in legacy FS formats */ +#define PATH_REV "rev" /* Proto rev file */ +#define PATH_REV_LOCK "rev-lock" /* Proto rev (write) lock file */ + +/* Names of sections and options in fsx.conf. */ +#define CONFIG_SECTION_CACHES "caches" +#define CONFIG_OPTION_FAIL_STOP "fail-stop" +#define CONFIG_SECTION_REP_SHARING "rep-sharing" +#define CONFIG_OPTION_ENABLE_REP_SHARING "enable-rep-sharing" +#define CONFIG_SECTION_DELTIFICATION "deltification" +#define CONFIG_OPTION_MAX_DELTIFICATION_WALK "max-deltification-walk" +#define CONFIG_OPTION_MAX_LINEAR_DELTIFICATION "max-linear-deltification" +#define CONFIG_OPTION_COMPRESSION_LEVEL "compression-level" +#define CONFIG_SECTION_PACKED_REVPROPS "packed-revprops" +#define CONFIG_OPTION_REVPROP_PACK_SIZE "revprop-pack-size" +#define CONFIG_OPTION_COMPRESS_PACKED_REVPROPS "compress-packed-revprops" +#define CONFIG_SECTION_IO "io" +#define CONFIG_OPTION_BLOCK_SIZE "block-size" +#define CONFIG_OPTION_L2P_PAGE_SIZE "l2p-page-size" +#define CONFIG_OPTION_P2L_PAGE_SIZE "p2l-page-size" +#define CONFIG_SECTION_DEBUG "debug" +#define CONFIG_OPTION_PACK_AFTER_COMMIT "pack-after-commit" + +/* The format number of this filesystem. + This is independent of the repository format number, and + independent of any other FS back ends. + + Note: If you bump this, please update the switch statement in + svn_fs_x__create() as well. + */ +#define SVN_FS_X__FORMAT_NUMBER 1 + +/* On most operating systems apr implements file locks per process, not + per file. On Windows apr implements the locking as per file handle + locks, so we don't have to add our own mutex for just in-process + synchronization. */ +#if APR_HAS_THREADS && !defined(WIN32) +#define SVN_FS_X__USE_LOCK_MUTEX 1 +#else +#define SVN_FS_X__USE_LOCK_MUTEX 0 +#endif + +/* Private FSX-specific data shared between all svn_txn_t objects that + relate to a particular transaction in a filesystem (as identified + by transaction id and filesystem UUID). Objects of this type are + allocated in their own subpool of the common pool. */ +typedef struct svn_fs_x__shared_txn_data_t +{ + /* The next transaction in the list, or NULL if there is no following + transaction. */ + struct svn_fs_x__shared_txn_data_t *next; + + /* ID of this transaction. */ + svn_fs_x__txn_id_t txn_id; + + /* Whether the transaction's prototype revision file is locked for + writing by any thread in this process (including the current + thread; recursive locks are not permitted). This is effectively + a non-recursive mutex. */ + svn_boolean_t being_written; + + /* The pool in which this object has been allocated; a subpool of the + common pool. */ + apr_pool_t *pool; +} svn_fs_x__shared_txn_data_t; + +/* Private FSX-specific data shared between all svn_fs_t objects that + relate to a particular filesystem, as identified by filesystem UUID. + Objects of this type are allocated in the common pool. */ +typedef struct svn_fs_x__shared_data_t +{ + /* A list of shared transaction objects for each transaction that is + currently active, or NULL if none are. All access to this list, + including the contents of the objects stored in it, is synchronised + under TXN_LIST_LOCK. */ + svn_fs_x__shared_txn_data_t *txns; + + /* A free transaction object, or NULL if there is no free object. + Access to this object is synchronised under TXN_LIST_LOCK. */ + svn_fs_x__shared_txn_data_t *free_txn; + + /* The following lock must be taken out in reverse order of their + declaration here. Any subset may be acquired and held at any given + time but their relative acquisition order must not change. + + (lock 'txn-current' before 'pack' before 'write' before 'txn-list') */ + + /* A lock for intra-process synchronization when accessing the TXNS list. */ + svn_mutex__t *txn_list_lock; + + /* A lock for intra-process synchronization when grabbing the + repository write lock. */ + svn_mutex__t *fs_write_lock; + + /* A lock for intra-process synchronization when grabbing the + repository pack operation lock. */ + svn_mutex__t *fs_pack_lock; + + /* A lock for intra-process synchronization when locking the + txn-current file. */ + svn_mutex__t *txn_current_lock; + + /* The common pool, under which this object is allocated, subpools + of which are used to allocate the transaction objects. */ + apr_pool_t *common_pool; +} svn_fs_x__shared_data_t; + +/* Data structure for the 1st level DAG node cache. */ +typedef struct svn_fs_x__dag_cache_t svn_fs_x__dag_cache_t; + +/* Key type for all caches that use revision + offset / counter as key. + + Note: Cache keys should be 16 bytes for best performance and there + should be no padding. */ +typedef struct svn_fs_x__pair_cache_key_t +{ + /* The object's revision. Use the 64 data type to prevent padding. */ + apr_int64_t revision; + + /* Sub-address: item index, revprop generation, packed flag, etc. */ + apr_int64_t second; +} svn_fs_x__pair_cache_key_t; + +/* Key type that identifies a representation / rep header. + + Note: Cache keys should require no padding. */ +typedef struct svn_fs_x__representation_cache_key_t +{ + /* Revision that contains the representation */ + apr_int64_t revision; + + /* Packed or non-packed representation (boolean)? */ + apr_int64_t is_packed; + + /* Item index of the representation */ + apr_uint64_t item_index; +} svn_fs_x__representation_cache_key_t; + +/* Key type that identifies a txdelta window. + + Note: Cache keys should require no padding. */ +typedef struct svn_fs_x__window_cache_key_t +{ + /* The object's revision. Use the 64 data type to prevent padding. */ + apr_int64_t revision; + + /* Window number within that representation. */ + apr_int64_t chunk_index; + + /* Item index of the representation */ + apr_uint64_t item_index; +} svn_fs_x__window_cache_key_t; + +/* Private (non-shared) FSX-specific data for each svn_fs_t object. + Any caches in here may be NULL. */ +typedef struct svn_fs_x__data_t +{ + /* The format number of this FS. */ + int format; + + /* The maximum number of files to store per directory. */ + int max_files_per_dir; + + /* Rev / pack file read granularity in bytes. */ + apr_int64_t block_size; + + /* Rev / pack file granularity (in bytes) covered by a single phys-to-log + * index page. */ + /* Capacity in entries of log-to-phys index pages */ + apr_int64_t l2p_page_size; + + /* Rev / pack file granularity covered by phys-to-log index pages */ + apr_int64_t p2l_page_size; + + /* The revision that was youngest, last time we checked. */ + svn_revnum_t youngest_rev_cache; + + /* Caches of immutable data. (Note that these may be shared between + multiple svn_fs_t's for the same filesystem.) */ + + /* Access to the configured memcached instances. May be NULL. */ + svn_memcache_t *memcache; + + /* If TRUE, don't ignore any cache-related errors. If FALSE, errors from + e.g. memcached may be ignored as caching is an optional feature. */ + svn_boolean_t fail_stop; + + /* Caches native dag_node_t* instances and acts as a 1st level cache */ + svn_fs_x__dag_cache_t *dag_node_cache; + + /* DAG node cache for immutable nodes. Maps (revision, fspath) + to (dag_node_t *). This is the 2nd level cache for DAG nodes. */ + svn_cache__t *rev_node_cache; + + /* A cache of the contents of immutable directories; maps from + unparsed FS ID to a apr_hash_t * mapping (const char *) dirent + names to (svn_fs_x__dirent_t *). */ + svn_cache__t *dir_cache; + + /* Fulltext cache; currently only used with memcached. Maps from + rep key (revision/offset) to svn_stringbuf_t. */ + svn_cache__t *fulltext_cache; + + /* Access object to the revprop "generation". Will be NULL until + the first access. May be also get closed and set to NULL again. */ + apr_file_t *revprop_generation_file; + + /* Revision property cache. Maps from (rev,generation) to apr_hash_t. */ + svn_cache__t *revprop_cache; + + /* Node properties cache. Maps from rep key to apr_hash_t. */ + svn_cache__t *properties_cache; + + /* Pack manifest cache; a cache mapping (svn_revnum_t) shard number to + a manifest; and a manifest is a mapping from (svn_revnum_t) revision + number offset within a shard to (apr_off_t) byte-offset in the + respective pack file. */ + svn_cache__t *packed_offset_cache; + + /* Cache for txdelta_window_t objects; + * the key is svn_fs_x__window_cache_key_t */ + svn_cache__t *txdelta_window_cache; + + /* Cache for combined windows as svn_stringbuf_t objects; + the key is svn_fs_x__window_cache_key_t */ + svn_cache__t *combined_window_cache; + + /* Cache for svn_fs_x__rep_header_t objects; + * the key is (revision, item index) */ + svn_cache__t *node_revision_cache; + + /* Cache for noderevs_t containers; + the key is a (pack file revision, file offset) pair */ + svn_cache__t *noderevs_container_cache; + + /* Cache for change lists as APR arrays of svn_fs_x__change_t * objects; + the key is the revision */ + svn_cache__t *changes_cache; + + /* Cache for change_list_t containers; + the key is a (pack file revision, file offset) pair */ + svn_cache__t *changes_container_cache; + + /* Cache for star-delta / representation containers; + the key is a (pack file revision, file offset) pair */ + svn_cache__t *reps_container_cache; + + /* Cache for svn_fs_x__rep_header_t objects; the key is a + (revision, item index) pair */ + svn_cache__t *rep_header_cache; + + /* Cache for svn_mergeinfo_t objects; the key is a combination of + revision, inheritance flags and path. */ + svn_cache__t *mergeinfo_cache; + + /* Cache for presence of svn_mergeinfo_t on a noderev; the key is a + combination of revision, inheritance flags and path; value is "1" + if the node has mergeinfo, "0" if it doesn't. */ + svn_cache__t *mergeinfo_existence_cache; + + /* Cache for l2p_header_t objects; the key is (revision, is-packed). + Will be NULL for pre-format7 repos */ + svn_cache__t *l2p_header_cache; + + /* Cache for l2p_page_t objects; the key is svn_fs_x__page_cache_key_t. + Will be NULL for pre-format7 repos */ + svn_cache__t *l2p_page_cache; + + /* Cache for p2l_header_t objects; the key is (revision, is-packed). + Will be NULL for pre-format7 repos */ + svn_cache__t *p2l_header_cache; + + /* Cache for apr_array_header_t objects containing svn_fs_x__p2l_entry_t + elements; the key is svn_fs_x__page_cache_key_t. + Will be NULL for pre-format7 repos */ + svn_cache__t *p2l_page_cache; + + /* TRUE while the we hold a lock on the write lock file. */ + svn_boolean_t has_write_lock; + + /* Data shared between all svn_fs_t objects for a given filesystem. */ + svn_fs_x__shared_data_t *shared; + + /* The sqlite database used for rep caching. */ + svn_sqlite__db_t *rep_cache_db; + + /* Thread-safe boolean */ + svn_atomic_t rep_cache_db_opened; + + /* The oldest revision not in a pack file. It also applies to revprops + * if revprop packing has been enabled by the FSX format version. */ + svn_revnum_t min_unpacked_rev; + + /* Whether rep-sharing is supported by the filesystem + * and allowed by the configuration. */ + svn_boolean_t rep_sharing_allowed; + + /* File size limit in bytes up to which multiple revprops shall be packed + * into a single file. */ + apr_int64_t revprop_pack_size; + + /* Whether packed revprop files shall be compressed. */ + svn_boolean_t compress_packed_revprops; + + /* Restart deltification histories after each multiple of this value */ + apr_int64_t max_deltification_walk; + + /* Maximum number of length of the linear part at the top of the + * deltification history after which skip deltas will be used. */ + apr_int64_t max_linear_deltification; + + /* Compression level to use with txdelta storage format in new revs. */ + int delta_compression_level; + + /* Pack after every commit. */ + svn_boolean_t pack_after_commit; + + /* Per-instance filesystem ID, which provides an additional level of + uniqueness for filesystems that share the same UUID, but should + still be distinguishable (e.g. backups produced by svn_fs_hotcopy() + or dump / load cycles). */ + const char *instance_id; + + /* Pointer to svn_fs_open. */ + svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, + apr_pool_t *, apr_pool_t *); +} svn_fs_x__data_t; + + +/*** Filesystem Transaction ***/ +typedef struct svn_fs_x__transaction_t +{ + /* property list (const char * name, svn_string_t * value). + may be NULL if there are no properties. */ + apr_hash_t *proplist; + + /* revision upon which this txn is base. (unfinished only) */ + svn_revnum_t base_rev; + + /* copies list (const char * copy_ids), or NULL if there have been + no copies in this transaction. */ + apr_array_header_t *copies; + +} svn_fs_x__transaction_t; + + +/*** Representation ***/ +/* If you add fields to this, check to see if you need to change + * svn_fs_x__rep_copy. */ +typedef struct svn_fs_x__representation_t +{ + /* Checksums digests for the contents produced by this representation. + This checksum is for the contents the rep shows to consumers, + regardless of how the rep stores the data under the hood. It is + independent of the storage (fulltext, delta, whatever). + + If has_sha1 is FALSE, then for compatibility behave as though this + checksum matches the expected checksum. + + The md5 checksum is always filled, unless this is rep which was + retrieved from the rep-cache. The sha1 checksum is only computed on + a write, for use with rep-sharing. */ + svn_boolean_t has_sha1; + unsigned char sha1_digest[APR_SHA1_DIGESTSIZE]; + unsigned char md5_digest[APR_MD5_DIGESTSIZE]; + + /* Change set and item number where this representation is located. */ + svn_fs_x__id_t id; + + /* The size of the representation in bytes as seen in the revision + file. */ + svn_filesize_t size; + + /* The size of the fulltext of the representation. */ + svn_filesize_t expanded_size; + +} svn_fs_x__representation_t; + + +/*** Node-Revision ***/ +/* If you add fields to this, check to see if you need to change + * copy_node_revision in dag.c. */ +typedef struct svn_fs_x__noderev_t +{ + /* Predecessor node revision id. Will be "unused" if there is no + predecessor for this node revision. */ + svn_fs_x__id_t predecessor_id; + + /* The ID of this noderev */ + svn_fs_x__id_t noderev_id; + + /* Identifier of the node that this noderev belongs to. */ + svn_fs_x__id_t node_id; + + /* Copy identifier of this line of history. */ + svn_fs_x__id_t copy_id; + + /* If this node-rev is a copy, where was it copied from? */ + const char *copyfrom_path; + svn_revnum_t copyfrom_rev; + + /* Helper for history tracing, root of the parent tree from whence + this node-rev was copied. */ + svn_revnum_t copyroot_rev; + const char *copyroot_path; + + /* node kind */ + svn_node_kind_t kind; + + /* number of predecessors this node revision has (recursively). */ + int predecessor_count; + + /* representation key for this node's properties. may be NULL if + there are no properties. */ + svn_fs_x__representation_t *prop_rep; + + /* representation for this node's data. may be NULL if there is + no data. */ + svn_fs_x__representation_t *data_rep; + + /* path at which this node first came into existence. */ + const char *created_path; + + /* Does this node itself have svn:mergeinfo? */ + svn_boolean_t has_mergeinfo; + + /* Number of nodes with svn:mergeinfo properties that are + descendants of this node (including it itself) */ + apr_int64_t mergeinfo_count; + +} svn_fs_x__noderev_t; + + +/** The type of a directory entry. */ +typedef struct svn_fs_x__dirent_t +{ + + /** The name of this directory entry. */ + const char *name; + + /** The node revision ID it names. */ + svn_fs_x__id_t id; + + /** The node kind. */ + svn_node_kind_t kind; +} svn_fs_x__dirent_t; + + +/*** Change ***/ +typedef struct svn_fs_x__change_t +{ + /* Path of the change. */ + svn_string_t path; + + /* node revision id of changed path */ + svn_fs_x__id_t noderev_id; + + /* See svn_fs_path_change2_t for a description for the remaining elements. + */ + 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_boolean_t copyfrom_known; + svn_revnum_t copyfrom_rev; + const char *copyfrom_path; + + svn_tristate_t mergeinfo_mod; +} svn_fs_x__change_t; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_X_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs_id.c b/contrib/subversion/subversion/libsvn_fs_x/fs_id.c new file mode 100644 index 000000000..16f8f26af --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs_id.c @@ -0,0 +1,319 @@ +/* fs_id.c : FSX's implementation of svn_fs_id_t + * + * ==================================================================== + * 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_pools.h" + +#include "cached_data.h" +#include "fs_id.h" + +#include "../libsvn_fs/fs-loader.h" +#include "private/svn_string_private.h" + + + +/* Structure holding everything needed to implement svn_fs_id_t for FSX. + */ +typedef struct fs_x__id_t +{ + /* API visible part. + The fsap_data member points to our svn_fs_x__id_context_t object. */ + svn_fs_id_t generic_id; + + /* Private members. + This addresses the DAG node identified by this ID object. + If it refers to a TXN, it may become . */ + svn_fs_x__id_t noderev_id; + +} fs_x__id_t; + + + +/* The state machine behind this is as follows: + + (A) FS passed in during context construction still open and uses a + different pool as the context (Usually the initial state). In that + case, FS_PATH is NULL and we watch for either pool's cleanup. + + Next states: + (B). Transition triggered by FS->POOL cleanup. + (D). Transition triggered by OWNER cleanup. + + (B) FS has been closed but not the OWNER pool, i.e. the context is valid. + FS is NULL, FS_NAME has been set. No cleanup functions are registered. + + Next states: + (C). Transition triggered by successful access to the file system. + (D). Transition triggered by OWNER cleanup. + + (C) FS is open, allocated in the context's OWNER pool (maybe the initial + state but that is atypical). No cleanup functions are registered. + + Next states: + (D). Transition triggered by OWNER cleanup. + + (D) Destroyed. No access nor notification is allowed. + Final state. + + */ +struct svn_fs_x__id_context_t +{ + /* If this is NULL, FS_PATH points to the on-disk path to the file system + we need to re-open the FS. */ + svn_fs_t *fs; + + /* If FS is NULL, this points to the on-disk path to pass into svn_fs_open2 + to reopen the filesystem. Allocated in OWNER. May only be NULL if FS + is not.*/ + const char *fs_path; + + /* If FS is NULL, this points to svn_fs_open() as passed to the library. */ + svn_error_t *(*svn_fs_open_)(svn_fs_t **, + const char *, + apr_hash_t *, + apr_pool_t *, + apr_pool_t *); + + /* Pool that this context struct got allocated in. */ + apr_pool_t *owner; + + /* A sub-pool of ONWER. We use this when querying data from FS. Gets + cleanup up immediately after usage. NULL until needed for the first + time. */ + apr_pool_t *aux_pool; +}; + +/* Forward declaration. */ +static apr_status_t +fs_cleanup(void *baton); + +/* APR pool cleanup notification for the svn_fs_x__id_context_t given as + BATON. Sent at state (A)->(D) transition. */ +static apr_status_t +owner_cleanup(void *baton) +{ + svn_fs_x__id_context_t *context = baton; + + /* Everything in CONTEXT gets cleaned up automatically. + However, we must prevent notifications from other pools. */ + apr_pool_cleanup_kill(context->fs->pool, context, fs_cleanup); + + return APR_SUCCESS; +} + +/* APR pool cleanup notification for the svn_fs_x__id_context_t given as + BATON. Sent at state (A)->(B) transition. */ +static apr_status_t +fs_cleanup(void *baton) +{ + svn_fs_x__id_context_t *context = baton; + svn_fs_x__data_t *ffd = context->fs->fsap_data; + + /* Remember the FS_PATH to potentially reopen and mark the FS as n/a. */ + context->fs_path = apr_pstrdup(context->owner, context->fs->path); + context->svn_fs_open_ = ffd->svn_fs_open_; + context->fs = NULL; + + + /* No need for further notifications because from now on, everything is + allocated in OWNER. */ + apr_pool_cleanup_kill(context->owner, context, owner_cleanup); + + return APR_SUCCESS; +} + +/* Return the filesystem provided by CONTEXT. Re-open it if necessary. + Returns NULL if the FS could not be opened. */ +static svn_fs_t * +get_fs(svn_fs_x__id_context_t *context) +{ + if (!context->fs) + { + svn_error_t *err; + + SVN_ERR_ASSERT_NO_RETURN(context->svn_fs_open_); + + err = context->svn_fs_open_(&context->fs, context->fs_path, NULL, + context->owner, context->owner); + if (err) + { + svn_error_clear(err); + context->fs = NULL; + } + } + + return context->fs; +} + +/* Provide the auto-created auxiliary pool from ID's context object. */ +static apr_pool_t * +get_aux_pool(const fs_x__id_t *id) +{ + svn_fs_x__id_context_t *context = id->generic_id.fsap_data; + if (!context->aux_pool) + context->aux_pool = svn_pool_create(context->owner); + + return context->aux_pool; +} + +/* Return the noderev structure identified by ID. Returns NULL for invalid + IDs or inaccessible repositories. The caller should clear the auxiliary + pool before returning to its respective caller. */ +static svn_fs_x__noderev_t * +get_noderev(const fs_x__id_t *id) +{ + svn_fs_x__noderev_t *result = NULL; + + svn_fs_x__id_context_t *context = id->generic_id.fsap_data; + svn_fs_t *fs = get_fs(context); + apr_pool_t *pool = get_aux_pool(id); + + if (fs) + { + svn_error_t *err = svn_fs_x__get_node_revision(&result, fs, + &id->noderev_id, + pool, pool); + if (err) + { + svn_error_clear(err); + result = NULL; + } + } + + return result; +} + + + +/*** Implement v-table functions ***/ + +/* Implement id_vtable_t.unparse */ +static svn_string_t * +id_unparse(const svn_fs_id_t *fs_id, + apr_pool_t *result_pool) +{ + const fs_x__id_t *id = (const fs_x__id_t *)fs_id; + return svn_fs_x__id_unparse(&id->noderev_id, result_pool); +} + +/* Implement id_vtable_t.compare. + + The result is efficiently computed for matching IDs. The far less + meaningful "common ancestor" relationship has a larger latency when + evaluated first for a given context object. Subsequent calls are + moderately fast. */ +static svn_fs_node_relation_t +id_compare(const svn_fs_id_t *a, + const svn_fs_id_t *b) +{ + const fs_x__id_t *id_a = (const fs_x__id_t *)a; + const fs_x__id_t *id_b = (const fs_x__id_t *)b; + svn_fs_x__noderev_t *noderev_a, *noderev_b; + svn_boolean_t same_node; + + /* Quick check: same IDs? */ + if (svn_fs_x__id_eq(&id_a->noderev_id, &id_b->noderev_id)) + return svn_fs_node_unchanged; + + /* Fetch the nodesrevs, compare the IDs of the nodes they belong to and + clean up any temporaries. If we can't find one of the noderevs, don't + get access to the FS etc., report the IDs as "unrelated" as only + valid / existing things may be related. */ + noderev_a = get_noderev(id_a); + noderev_b = get_noderev(id_b); + + if (noderev_a && noderev_b) + same_node = svn_fs_x__id_eq(&noderev_a->node_id, &noderev_b->node_id); + else + same_node = FALSE; + + svn_pool_clear(get_aux_pool(id_a)); + svn_pool_clear(get_aux_pool(id_b)); + + /* Return result. */ + return same_node ? svn_fs_node_common_ancestor : svn_fs_node_unrelated; +} + + +/* Creating ID's. */ + +static id_vtable_t id_vtable = { + id_unparse, + id_compare +}; + +svn_fs_x__id_context_t * +svn_fs_x__id_create_context(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + svn_fs_x__id_context_t *result = apr_pcalloc(result_pool, sizeof(*result)); + result->fs = fs; + result->owner = result_pool; + + /* Check for a special case: + If the owner of the context also owns the FS, there will be no reason + to notify them of the respective other's cleanup. */ + if (result_pool != fs->pool) + { + /* If the context's owner gets cleaned up before FS, we must disconnect + from the FS. */ + apr_pool_cleanup_register(result_pool, + result, + owner_cleanup, + apr_pool_cleanup_null); + + /* If the FS gets cleaned up before the context's owner, disconnect + from the FS and remember its path on disk to be able to re-open it + later if necessary. */ + apr_pool_cleanup_register(fs->pool, + result, + fs_cleanup, + apr_pool_cleanup_null); + } + + return result; +} + +svn_fs_id_t * +svn_fs_x__id_create(svn_fs_x__id_context_t *context, + const svn_fs_x__id_t *noderev_id, + apr_pool_t *result_pool) +{ + fs_x__id_t *id; + + /* Special case: NULL IDs */ + if (!svn_fs_x__id_used(noderev_id)) + return NULL; + + /* In theory, the CONTEXT might not be owned by POOL. It's FS might even + have been closed. Make sure we have a context owned by POOL. */ + if (context->owner != result_pool) + context = svn_fs_x__id_create_context(get_fs(context), result_pool); + + /* Finally, construct the ID object. */ + id = apr_pcalloc(result_pool, sizeof(*id)); + id->noderev_id = *noderev_id; + + id->generic_id.vtable = &id_vtable; + id->generic_id.fsap_data = context; + + return (svn_fs_id_t *)id; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs_id.h b/contrib/subversion/subversion/libsvn_fs_x/fs_id.h new file mode 100644 index 000000000..6d6a08ac3 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs_id.h @@ -0,0 +1,62 @@ +/* fs_id.h : FSX's implementation of svn_fs_id_t + * + * ==================================================================== + * 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_FS_X_FS_ID_H +#define SVN_LIBSVN_FS_X_FS_ID_H + +#include "id.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Transparent FS access object to be used with FSX's implementation for + svn_fs_id_t. It allows the ID object query data from the respective FS + to check for node relationships etc. It also allows to re-open the repo + after the original svn_fs_t object got cleaned up, i.e. the ID object's + functionality does not depend on any other object's lifetime. + + For efficiency, multiple svn_fs_id_t should share the same context. + */ +typedef struct svn_fs_x__id_context_t svn_fs_x__id_context_t; + +/* Return a context object for filesystem FS; construct it in RESULT_POOL. */ +svn_fs_x__id_context_t * +svn_fs_x__id_create_context(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Create a permanent ID based on NODEREV_ID, allocated in RESULT_POOL. + For complex requests, access the filesystem provided with CONTEXT. + + For efficiency, CONTEXT should have been created in RESULT_POOL and be + shared between multiple ID instances allocated in the same pool. + */ +svn_fs_id_t * +svn_fs_x__id_create(svn_fs_x__id_context_t *context, + const svn_fs_x__id_t *noderev_id, + apr_pool_t *result_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_X_FS_ID_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs_x.c b/contrib/subversion/subversion/libsvn_fs_x/fs_x.c new file mode 100644 index 000000000..b766b58b2 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs_x.c @@ -0,0 +1,1228 @@ +/* fs_x.c --- filesystem operations specific to fs_x + * + * ==================================================================== + * 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_x.h" + +#include + +#include "svn_hash.h" +#include "svn_props.h" +#include "svn_time.h" +#include "svn_dirent_uri.h" +#include "svn_sorts.h" +#include "svn_version.h" + +#include "cached_data.h" +#include "id.h" +#include "rep-cache.h" +#include "revprops.h" +#include "transaction.h" +#include "tree.h" +#include "util.h" +#include "index.h" + +#include "private/svn_fs_util.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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_X_DEFAULT_MAX_FILES_PER_DIR +#define SVN_FS_X_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_X_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_X_MAX_DELTIFICATION_WALK 1023 + + + + +/* 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 SCRATCH_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 *scratch_pool) +{ + return svn_fs_x__check_file_buffer_numeric(buf, offset, path, "Format", + scratch_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) +{ + /* Put blacklisted versions here. */ + + /* We support all formats from 1-current simultaneously */ + if (1 <= format && format <= SVN_FS_X__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_X__FORMAT_NUMBER, format); +} + +/* Read the format file at PATH and set *PFORMAT to the format version found + * and *MAX_FILES_PER_DIR to the shard size. Use SCRATCH_POOL for temporary + * allocations. */ +static svn_error_t * +read_format(int *pformat, + int *max_files_per_dir, + const char *path, + apr_pool_t *scratch_pool) +{ + svn_stream_t *stream; + svn_stringbuf_t *content; + svn_stringbuf_t *buf; + svn_boolean_t eos = FALSE; + + SVN_ERR(svn_stringbuf_from_file2(&content, path, scratch_pool)); + stream = svn_stream_from_stringbuf(content, scratch_pool); + SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_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, scratch_pool)); + } + + /* Check that the first line contains only digits. */ + SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, scratch_pool)); + SVN_ERR(svn_cstring_atoi(pformat, buf->data)); + + /* Check that we support this format at all */ + SVN_ERR(check_format(*pformat)); + + /* Read any options. */ + SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool)); + if (!eos && strncmp(buf->data, "layout sharded ", 15) == 0) + { + /* Check that the argument is numeric. */ + SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path, + scratch_pool)); + SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15)); + } + else + return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, + _("'%s' contains invalid filesystem format option '%s'"), + svn_dirent_local_style(path, scratch_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 SCRATCH_POOL for temporary allocation. */ +svn_error_t * +svn_fs_x__write_format(svn_fs_t *fs, + svn_boolean_t overwrite, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *sb; + const char *path = svn_fs_x__path_format(fs, scratch_pool); + svn_fs_x__data_t *ffd = fs->fsap_data; + + SVN_ERR_ASSERT(1 <= ffd->format && ffd->format <= SVN_FS_X__FORMAT_NUMBER); + + sb = svn_stringbuf_createf(scratch_pool, "%d\n", ffd->format); + svn_stringbuf_appendcstr(sb, apr_psprintf(scratch_pool, + "layout sharded %d\n", + ffd->max_files_per_dir)); + + /* 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, scratch_pool)); + } + else + { + SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len, + NULL /* copy_perms_path */, scratch_pool)); + } + + /* And set the perms to make it read only */ + return svn_io_set_file_read_only(path, FALSE, scratch_pool); +} + +/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within + * the range of what the current system may address in RAM and it is a + * power of 2. Assume that the element size within the block is ITEM_SIZE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +verify_block_size(apr_int64_t block_size, + apr_size_t item_size, + const char *name, + apr_pool_t *scratch_pool) +{ + /* Limit range. */ + if (block_size <= 0) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too small for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + if (block_size > SVN_MAX_OBJECT_SIZE / item_size) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is too large for fsfs.conf setting '%s'."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + /* Ensure it is a power of two. + * For positive X, X & (X-1) will reset the lowest bit set. + * If the result is 0, at most one bit has been set. */ + if (0 != (block_size & (block_size - 1))) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("%s is invalid for fsfs.conf setting '%s' " + "because it is not a power of 2."), + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + block_size), + name); + + return SVN_NO_ERROR; +} + +/* Read the configuration information of the file system at FS_PATH + * and set the respective values in FFD. Use pools as usual. + */ +static svn_error_t * +read_config(svn_fs_x__data_t *ffd, + const char *fs_path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_config_t *config; + apr_int64_t compression_level; + + SVN_ERR(svn_config_read3(&config, + svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool), + FALSE, FALSE, FALSE, scratch_pool)); + + /* Initialize ffd->rep_sharing_allowed. */ + SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed, + CONFIG_SECTION_REP_SHARING, + CONFIG_OPTION_ENABLE_REP_SHARING, TRUE)); + + /* Initialize deltification settings in ffd. */ + SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_MAX_DELTIFICATION_WALK, + SVN_FS_X_MAX_DELTIFICATION_WALK)); + SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_MAX_LINEAR_DELTIFICATION, + SVN_FS_X_MAX_LINEAR_DELTIFICATION)); + SVN_ERR(svn_config_get_int64(config, &compression_level, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_COMPRESSION_LEVEL, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); + ffd->delta_compression_level + = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level), + SVN_DELTA_COMPRESSION_LEVEL_MAX); + + /* Initialize revprop packing settings in ffd. */ + SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops, + CONFIG_SECTION_PACKED_REVPROPS, + CONFIG_OPTION_COMPRESS_PACKED_REVPROPS, + TRUE)); + SVN_ERR(svn_config_get_int64(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; + + /* I/O settings in ffd. */ + SVN_ERR(svn_config_get_int64(config, &ffd->block_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_BLOCK_SIZE, + 64)); + SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_L2P_PAGE_SIZE, + 0x2000)); + SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size, + CONFIG_SECTION_IO, + CONFIG_OPTION_P2L_PAGE_SIZE, + 0x400)); + + /* Don't accept unreasonable or illegal values. + * Block size and P2L page size are in kbytes; + * L2P blocks are arrays of apr_off_t. */ + SVN_ERR(verify_block_size(ffd->block_size, 0x400, + CONFIG_OPTION_BLOCK_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400, + CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool)); + SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t), + CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool)); + + /* convert kBytes to bytes */ + ffd->block_size *= 0x400; + ffd->p2l_page_size *= 0x400; + /* L2P pages are in entries - not in (k)Bytes */ + + /* Debug options. */ + SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit, + CONFIG_SECTION_DEBUG, + CONFIG_OPTION_PACK_AFTER_COMMIT, + FALSE)); + + /* memcached configuration */ + SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config, + result_pool, scratch_pool)); + + SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop, + CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, + FALSE)); + + return SVN_NO_ERROR; +} + +/* Write FS' initial configuration file. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +write_config(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ +#define NL APR_EOL_STR + static const char * const fsx_conf_contents = +"### This file controls the configuration of the FSX filesystem." NL +"" NL +"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL +"### These options name memcached servers used to cache internal FSX" NL +"### data. See http://www.danga.com/memcached/ for more information on" NL +"### memcached. To use memcached with FSX, 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." 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.9, the default value is 1023." NL +"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023" NL +"###" NL +"### The skip-delta scheme used by FSX tends to repeatably store redundant" NL +"### delta information where a simple delta against the latest version is" NL +"### often smaller. By default, 1.9+ 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." NL +"### For 1.8, the default value is 16." NL +"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL +"###" NL +"### After deltification, we compress the data through zlib to minimize on-" NL +"### disk size. That can be an expensive and ineffective process. This" NL +"### setting controls the usage of zlib in future revisions." NL +"### Revisions with highly compressible data in them may shrink in size" NL +"### if the setting is increased but may take much longer to commit. The" NL +"### time taken to uncompress that data again is widely independent of the" NL +"### compression level." NL +"### Compression will be ineffective if the incoming content is already" NL +"### highly compressed. In that case, disabling the compression entirely" NL +"### will speed up commits as well as reading the data. Repositories with" NL +"### many small compressible files (source code) but also a high percentage" NL +"### of large incompressible ones (artwork) may benefit from compression" NL +"### levels lowered to e.g. 1." NL +"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL +"### and 0 disabling it altogether." NL +"### The default value is 5." NL +"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5" 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 enabled by default." NL +"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = true" NL +"" NL +"[" CONFIG_SECTION_IO "]" NL +"### Parameters in this section control the data access granularity in" NL +"### format 7 repositories and later. The defaults should translate into" NL +"### decent performance over a wide range of setups." NL +"###" NL +"### When a specific piece of information needs to be read from disk, a" NL +"### data block is being read at once and its contents are being cached." NL +"### If the repository is being stored on a RAID, the block size should be" NL +"### either 50% or 100% of RAID block size / granularity. Also, your file" NL +"### system blocks/clusters should be properly aligned and sized. In that" NL +"### setup, each access will hit only one disk (minimizes I/O load) but" NL +"### uses all the data provided by the disk in a single access." NL +"### For SSD-based storage systems, slightly lower values around 16 kB" NL +"### may improve latency while still maximizing throughput." NL +"### Can be changed at any time but must be a power of 2." NL +"### block-size is given in kBytes and with a default of 64 kBytes." NL +"# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL +"###" NL +"### The log-to-phys index maps data item numbers to offsets within the" NL +"### rev or pack file. This index is organized in pages of a fixed maximum" NL +"### capacity. To access an item, the page table and the respective page" NL +"### must be read." NL +"### This parameter only affects revisions with thousands of changed paths." NL +"### If you have several extremely large revisions (~1 mio changes), think" NL +"### about increasing this setting. Reducing the value will rarely result" NL +"### in a net speedup." NL +"### This is an expert setting. Must be a power of 2." NL +"### l2p-page-size is 8192 entries by default." NL +"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL +"###" NL +"### The phys-to-log index maps positions within the rev or pack file to" NL +"### to data items, i.e. describes what piece of information is being" NL +"### stored at any particular offset. The index describes the rev file" NL +"### in chunks (pages) and keeps a global list of all those pages. Large" NL +"### pages mean a shorter page table but a larger per-page description of" NL +"### data items in it. The latency sweet spot depends on the change size" NL +"### distribution but covers a relatively wide range." NL +"### If the repository contains very large files, i.e. individual changes" NL +"### of tens of MB each, increasing the page size will shorten the index" NL +"### file at the expense of a slightly increased latency in sections with" NL +"### smaller changes." NL +"### For source code repositories, this should be about 16x the block-size." NL +"### Must be a power of 2." NL +"### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL +"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL +; +#undef NL + return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, + scratch_pool), + fsx_conf_contents, scratch_pool); +} + +/* Read FS's UUID file and store the data in the FS struct. */ +static svn_error_t * +read_uuid(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_file_t *uuid_file; + char buf[APR_UUID_FORMATTED_LENGTH + 2]; + apr_size_t limit; + + /* Read the repository uuid. */ + SVN_ERR(svn_io_file_open(&uuid_file, svn_fs_x__path_uuid(fs, scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + limit = sizeof(buf); + SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool)); + fs->uuid = apr_pstrdup(fs->pool, buf); + + /* Read the instance ID. */ + limit = sizeof(buf); + SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, + scratch_pool)); + ffd->instance_id = apr_pstrdup(fs->pool, buf); + + SVN_ERR(svn_io_file_close(uuid_file, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_format_file(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + int format, max_files_per_dir; + + /* Read info from format file. */ + SVN_ERR(read_format(&format, &max_files_per_dir, + svn_fs_x__path_format(fs, scratch_pool), scratch_pool)); + + /* Now that we've got *all* info, store / update values in FFD. */ + ffd->format = format; + ffd->max_files_per_dir = max_files_per_dir; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__open(svn_fs_t *fs, + const char *path, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + fs->path = apr_pstrdup(fs->pool, path); + + /* Read the FS format file. */ + SVN_ERR(svn_fs_x__read_format_file(fs, scratch_pool)); + + /* Read in and cache the repository uuid. */ + SVN_ERR(read_uuid(fs, scratch_pool)); + + /* Read the min unpacked revision. */ + SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool)); + + /* Read the configuration file. */ + SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool)); + + return svn_error_trace(svn_fs_x__read_current(&ffd->youngest_rev_cache, + fs, scratch_pool)); +} + +/* Baton type bridging svn_fs_x__upgrade and upgrade_body carrying + * parameters over between them. */ +typedef struct upgrade_baton_t +{ + svn_fs_t *fs; + svn_fs_upgrade_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_baton; +} upgrade_baton_t; + +/* Upgrade the FS given in upgrade_baton_t *)BATON to the latest format + * version. Apply options an invoke callback from that BATON. + * Temporary allocations are to be made from SCRATCH_POOL. + * + * At the moment, this is a simple placeholder as we don't support upgrades + * from experimental FSX versions. + */ +static svn_error_t * +upgrade_body(void *baton, + apr_pool_t *scratch_pool) +{ + upgrade_baton_t *upgrade_baton = baton; + svn_fs_t *fs = upgrade_baton->fs; + int format, max_files_per_dir; + const char *format_path = svn_fs_x__path_format(fs, scratch_pool); + + /* Read the FS format number and max-files-per-dir setting. */ + SVN_ERR(read_format(&format, &max_files_per_dir, format_path, + scratch_pool)); + + /* If we're already up-to-date, there's nothing else to be done here. */ + if (format == SVN_FS_X__FORMAT_NUMBER) + return SVN_NO_ERROR; + + /* Done */ + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__upgrade(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + upgrade_baton_t baton; + baton.fs = fs; + baton.notify_func = notify_func; + baton.notify_baton = notify_baton; + baton.cancel_func = cancel_func; + baton.cancel_baton = cancel_baton; + + return svn_fs_x__with_all_locks(fs, upgrade_body, (void *)&baton, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__youngest_rev(svn_revnum_t *youngest_p, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + SVN_ERR(svn_fs_x__read_current(youngest_p, fs, scratch_pool)); + ffd->youngest_rev_cache = *youngest_p; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__ensure_revision_exists(svn_revnum_t rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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(svn_fs_x__read_current(&ffd->youngest_rev_cache, fs, scratch_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_x__file_length(svn_filesize_t *length, + svn_fs_x__noderev_t *noderev) +{ + if (noderev->data_rep) + *length = noderev->data_rep->expanded_size; + else + *length = 0; + + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_fs_x__file_text_rep_equal(svn_fs_x__representation_t *a, + svn_fs_x__representation_t *b) +{ + svn_boolean_t a_empty = a == NULL || a->expanded_size == 0; + svn_boolean_t b_empty = b == NULL || b->expanded_size == 0; + + /* This makes sure that neither rep will be NULL later on */ + if (a_empty && b_empty) + return TRUE; + + if (a_empty != b_empty) + return FALSE; + + /* Same physical representation? Note that these IDs are always up-to-date + instead of e.g. being set lazily. */ + if (svn_fs_x__id_eq(&a->id, &b->id)) + return TRUE; + + /* Contents are equal if the checksums match. These are also always known. + */ + return memcmp(a->md5_digest, b->md5_digest, sizeof(a->md5_digest)) == 0 + && memcmp(a->sha1_digest, b->sha1_digest, sizeof(a->sha1_digest)) == 0; +} + +svn_error_t * +svn_fs_x__prop_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + svn_fs_x__noderev_t *a, + svn_fs_x__noderev_t *b, + svn_boolean_t strict, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_t *rep_a = a->prop_rep; + svn_fs_x__representation_t *rep_b = b->prop_rep; + apr_hash_t *proplist_a; + apr_hash_t *proplist_b; + + /* Mainly for a==b==NULL */ + if (rep_a == rep_b) + { + *equal = TRUE; + return SVN_NO_ERROR; + } + + /* Committed property lists can be compared quickly */ + if ( rep_a && rep_b + && svn_fs_x__is_revision(rep_a->id.change_set) + && svn_fs_x__is_revision(rep_b->id.change_set)) + { + /* MD5 must be given. Having the same checksum is good enough for + accepting the prop lists as equal. */ + *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest, + sizeof(rep_a->md5_digest)) == 0; + return SVN_NO_ERROR; + } + + /* Same path in same txn? */ + if (svn_fs_x__id_eq(&a->noderev_id, &b->noderev_id)) + { + *equal = TRUE; + return SVN_NO_ERROR; + } + + /* Skip the expensive bits unless we are in strict mode. + Simply assume that there is a different. */ + if (!strict) + { + *equal = FALSE; + return SVN_NO_ERROR; + } + + /* At least one of the reps has been modified in a txn. + Fetch and compare them. */ + SVN_ERR(svn_fs_x__get_proplist(&proplist_a, fs, a, scratch_pool, + scratch_pool)); + SVN_ERR(svn_fs_x__get_proplist(&proplist_b, fs, b, scratch_pool, + scratch_pool)); + + *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool); + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__file_checksum(svn_checksum_t **checksum, + svn_fs_x__noderev_t *noderev, + svn_checksum_kind_t kind, + apr_pool_t *result_pool) +{ + *checksum = NULL; + + if (noderev->data_rep) + { + svn_checksum_t temp; + temp.kind = kind; + + switch(kind) + { + case svn_checksum_md5: + temp.digest = noderev->data_rep->md5_digest; + break; + + case svn_checksum_sha1: + if (! noderev->data_rep->has_sha1) + return SVN_NO_ERROR; + + temp.digest = noderev->data_rep->sha1_digest; + break; + + default: + return SVN_NO_ERROR; + } + + *checksum = svn_checksum_dup(&temp, result_pool); + } + + return SVN_NO_ERROR; +} + +svn_fs_x__representation_t * +svn_fs_x__rep_copy(svn_fs_x__representation_t *rep, + apr_pool_t *result_pool) +{ + if (rep == NULL) + return NULL; + + return apr_pmemdup(result_pool, rep, sizeof(*rep)); +} + + +/* Write out the zeroth revision for filesystem FS. + Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +write_revision_zero(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + /* Use an explicit sub-pool to have full control over temp file lifetimes. + * Since we have it, use it for everything else as well. */ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + const char *path_revision_zero = svn_fs_x__path_rev(fs, 0, subpool); + apr_hash_t *proplist; + svn_string_t date; + + apr_array_header_t *index_entries; + svn_fs_x__p2l_entry_t *entry; + svn_fs_x__revision_file_t *rev_file; + const char *l2p_proto_index, *p2l_proto_index; + + /* Construct a skeleton r0 with no indexes. */ + svn_string_t *noderev_str = svn_string_create("id: 2+0\n" + "node: 0+0\n" + "copy: 0+0\n" + "type: dir\n" + "count: 0\n" + "cpath: /\n" + "\n", + subpool); + svn_string_t *changes_str = svn_string_create("\n", + subpool); + svn_string_t *r0 = svn_string_createf(subpool, "%s%s", + noderev_str->data, + changes_str->data); + + /* Write skeleton r0 to disk. */ + SVN_ERR(svn_io_file_create(path_revision_zero, r0->data, subpool)); + + /* Construct the index P2L contents: describe the 2 items we have. + Be sure to create them in on-disk order. */ + index_entries = apr_array_make(subpool, 2, sizeof(entry)); + + entry = apr_pcalloc(subpool, sizeof(*entry)); + entry->offset = 0; + entry->size = (apr_off_t)noderev_str->len; + entry->type = SVN_FS_X__ITEM_TYPE_NODEREV; + entry->item_count = 1; + entry->items = apr_pcalloc(subpool, sizeof(*entry->items)); + entry->items[0].change_set = 0; + entry->items[0].number = SVN_FS_X__ITEM_INDEX_ROOT_NODE; + APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry; + + entry = apr_pcalloc(subpool, sizeof(*entry)); + entry->offset = (apr_off_t)noderev_str->len; + entry->size = (apr_off_t)changes_str->len; + entry->type = SVN_FS_X__ITEM_TYPE_CHANGES; + entry->item_count = 1; + entry->items = apr_pcalloc(subpool, sizeof(*entry->items)); + entry->items[0].change_set = 0; + entry->items[0].number = SVN_FS_X__ITEM_INDEX_CHANGES; + APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry; + + /* Now re-open r0, create proto-index files from our entries and + rewrite the index section of r0. */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file_writable(&rev_file, fs, 0, + subpool, subpool)); + SVN_ERR(svn_fs_x__p2l_index_from_p2l_entries(&p2l_proto_index, fs, + rev_file, index_entries, + subpool, subpool)); + SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&l2p_proto_index, fs, + index_entries, + subpool, subpool)); + SVN_ERR(svn_fs_x__add_index_data(fs, rev_file->file, l2p_proto_index, + p2l_proto_index, 0, subpool)); + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + + 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 svn_fs_x__set_revision_proplist(fs, 0, proplist, fs->pool); +} + +svn_error_t * +svn_fs_x__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + fs->path = apr_pstrdup(fs->pool, path); + ffd->format = format; + + /* Use an appropriate sharding mode if supported by the format. */ + ffd->max_files_per_dir = shard_size; + + /* Create the revision data directories. */ + SVN_ERR(svn_io_make_dir_recursively( + svn_fs_x__path_rev_shard(fs, 0, scratch_pool), + scratch_pool)); + + /* Create the revprops directory. */ + SVN_ERR(svn_io_make_dir_recursively( + svn_fs_x__path_revprops_shard(fs, 0, scratch_pool), + scratch_pool)); + + /* Create the transaction directory. */ + SVN_ERR(svn_io_make_dir_recursively( + svn_fs_x__path_txns_dir(fs, scratch_pool), + scratch_pool)); + + /* Create the protorevs directory. */ + SVN_ERR(svn_io_make_dir_recursively( + svn_fs_x__path_txn_proto_revs(fs, scratch_pool), + scratch_pool)); + + /* Create the 'current' file. */ + SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_current(fs, scratch_pool), + scratch_pool)); + SVN_ERR(svn_fs_x__write_current(fs, 0, scratch_pool)); + + /* Create the 'uuid' file. */ + SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_lock(fs, scratch_pool), + scratch_pool)); + SVN_ERR(svn_fs_x__set_uuid(fs, NULL, NULL, scratch_pool)); + + /* Create the fsfs.conf file. */ + SVN_ERR(write_config(fs, scratch_pool)); + SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool)); + + /* Add revision 0. */ + SVN_ERR(write_revision_zero(fs, scratch_pool)); + + /* Create the min unpacked rev file. */ + SVN_ERR(svn_io_file_create( + svn_fs_x__path_min_unpacked_rev(fs, scratch_pool), + "0\n", scratch_pool)); + + /* Create the txn-current file if the repository supports + the transaction sequence file. */ + SVN_ERR(svn_io_file_create(svn_fs_x__path_txn_current(fs, scratch_pool), + "0\n", scratch_pool)); + SVN_ERR(svn_io_file_create_empty( + svn_fs_x__path_txn_current_lock(fs, scratch_pool), + scratch_pool)); + + /* Initialize the revprop caching info. */ + SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, scratch_pool)); + + ffd->youngest_rev_cache = 0; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__create(svn_fs_t *fs, + const char *path, + apr_pool_t *scratch_pool) +{ + int format = SVN_FS_X__FORMAT_NUMBER; + svn_fs_x__data_t *ffd = fs->fsap_data; + + fs->path = apr_pstrdup(fs->pool, path); + /* See if compatibility with older versions was explicitly requested. */ + if (fs->config) + { + svn_version_t *compatible_version; + SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, + scratch_pool)); + + /* select format number */ + switch(compatible_version->minor) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, + _("FSX is not compatible with Subversion prior to 1.9")); + + default:format = SVN_FS_X__FORMAT_NUMBER; + } + } + + /* Actual FS creation. */ + SVN_ERR(svn_fs_x__create_file_tree(fs, path, format, + SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR, + scratch_pool)); + + /* This filesystem is ready. Stamp it with a format number. */ + SVN_ERR(svn_fs_x__write_format(fs, FALSE, scratch_pool)); + + ffd->youngest_rev_cache = 0; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__set_uuid(svn_fs_t *fs, + const char *uuid, + const char *instance_id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + const char *uuid_path = svn_fs_x__path_uuid(fs, scratch_pool); + svn_stringbuf_t *contents = svn_stringbuf_create_empty(scratch_pool); + + if (! uuid) + uuid = svn_uuid_generate(scratch_pool); + + if (! instance_id) + instance_id = svn_uuid_generate(scratch_pool); + + svn_stringbuf_appendcstr(contents, uuid); + svn_stringbuf_appendcstr(contents, "\n"); + svn_stringbuf_appendcstr(contents, instance_id); + svn_stringbuf_appendcstr(contents, "\n"); + + /* We use the permissions of the 'current' file, because the 'uuid' + file does not exist during repository creation. */ + SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len, + /* perms */ + svn_fs_x__path_current(fs, scratch_pool), + scratch_pool)); + + fs->uuid = apr_pstrdup(fs->pool, uuid); + ffd->instance_id = apr_pstrdup(fs->pool, instance_id); + + 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_x__ensure_dir_exists(const char *path, + const char *fs_path, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, scratch_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, scratch_pool); +} + + +/*** Revisions ***/ + +svn_error_t * +svn_fs_x__revision_prop(svn_string_t **value_p, + svn_fs_t *fs, + svn_revnum_t rev, + const char *propname, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *table; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + SVN_ERR(svn_fs_x__get_revision_proplist(&table, fs, rev, FALSE, + scratch_pool, scratch_pool)); + + *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool); + + return SVN_NO_ERROR; +} + + +/* Baton used for change_rev_prop_body below. */ +typedef struct change_rev_prop_baton_t { + svn_fs_t *fs; + svn_revnum_t rev; + const char *name; + const svn_string_t *const *old_value_p; + const svn_string_t *value; +} change_rev_prop_baton_t; + +/* The work-horse for svn_fs_x__change_rev_prop, called with the FS + write lock. This implements the svn_fs_x__with_write_lock() + 'body' callback type. BATON is a 'change_rev_prop_baton_t *'. */ +static svn_error_t * +change_rev_prop_body(void *baton, + apr_pool_t *scratch_pool) +{ + change_rev_prop_baton_t *cb = baton; + apr_hash_t *table; + + /* Read current revprop values from disk (never from cache). + Even if somehow the cache got out of sync, we want to make sure that + we read, update and write up-to-date data. */ + SVN_ERR(svn_fs_x__get_revision_proplist(&table, cb->fs, cb->rev, TRUE, + scratch_pool, scratch_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 svn_fs_x__set_revision_proplist(cb->fs, cb->rev, table, + scratch_pool); +} + +svn_error_t * +svn_fs_x__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 *scratch_pool) +{ + change_rev_prop_baton_t 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_x__with_write_lock(fs, change_rev_prop_body, &cb, + scratch_pool); +} + + +svn_error_t * +svn_fs_x__info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + *fs_format = ffd->format; + *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); + + (*supports_version)->major = SVN_VER_MAJOR; + (*supports_version)->minor = 9; + (*supports_version)->patch = 0; + (*supports_version)->tag = ""; + + switch (ffd->format) + { + case 1: + break; +#ifdef SVN_DEBUG +# if SVN_FS_X__FORMAT_NUMBER != 1 +# error "Need to add a 'case' statement here" +# endif +#endif + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *files = apr_array_make(result_pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG, + result_pool); + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/fs_x.h b/contrib/subversion/subversion/libsvn_fs_x/fs_x.h new file mode 100644 index 000000000..98be70259 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/fs_x.h @@ -0,0 +1,202 @@ +/* fs_x.h : interface to the native filesystem layer + * + * ==================================================================== + * 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_FS__FS_X_H +#define SVN_LIBSVN_FS__FS_X_H + +#include "fs.h" + +/* Read the 'format' file of fsx filesystem FS and store its info in FS. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__read_format_file(svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Open the fsx filesystem pointed to by PATH and associate it with + filesystem object FS. Use SCRATCH_POOL for temporary allocations. + + ### Some parts of *FS must have been initialized beforehand; some parts + (including FS->path) are initialized by this function. */ +svn_error_t * +svn_fs_x__open(svn_fs_t *fs, + const char *path, + apr_pool_t *scratch_pool); + +/* Upgrade the fsx filesystem FS. Indicate progress via the optional + * NOTIFY_FUNC callback using NOTIFY_BATON. The optional CANCEL_FUNC + * will periodically be called with CANCEL_BATON to allow for preemption. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__upgrade(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* Set *YOUNGEST to the youngest revision in filesystem FS. Do any + temporary allocation in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__youngest_rev(svn_revnum_t *youngest, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Return SVN_ERR_FS_NO_SUCH_REVISION if the given revision REV is newer + than the current youngest revision in FS or is simply not a valid + revision number, else return success. Use SCRATCH_POOL for temporary + allocations. */ +svn_error_t * +svn_fs_x__ensure_revision_exists(svn_revnum_t rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Set *LENGTH to the be fulltext length of the node revision + specified by NODEREV. */ +svn_error_t * +svn_fs_x__file_length(svn_filesize_t *length, + svn_fs_x__noderev_t *noderev); + +/* Return TRUE if the representations in A and B have equal contents, else + return FALSE. */ +svn_boolean_t +svn_fs_x__file_text_rep_equal(svn_fs_x__representation_t *a, + svn_fs_x__representation_t *b); + +/* Set *EQUAL to TRUE if the property representations in A and B within FS + have equal contents, else set it to FALSE. If STRICT is not set, allow + for false negatives. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__prop_rep_equal(svn_boolean_t *equal, + svn_fs_t *fs, + svn_fs_x__noderev_t *a, + svn_fs_x__noderev_t *b, + svn_boolean_t strict, + apr_pool_t *scratch_pool); + + +/* Return a copy of the representation REP allocated from RESULT_POOL. */ +svn_fs_x__representation_t * +svn_fs_x__rep_copy(svn_fs_x__representation_t *rep, + apr_pool_t *result_pool); + + +/* Return the recorded checksum of type KIND for the text representation + of NODREV into CHECKSUM, allocating from RESULT_POOL. If no stored + checksum is available, put all NULL into CHECKSUM. */ +svn_error_t * +svn_fs_x__file_checksum(svn_checksum_t **checksum, + svn_fs_x__noderev_t *noderev, + svn_checksum_kind_t kind, + apr_pool_t *result_pool); + +/* Under the repository db PATH, create a FSFS repository with FORMAT, + * the given SHARD_SIZE. If not supported by the respective format, + * the latter two parameters will be ignored. FS will be updated. + * + * The only file not being written is the 'format' file. This allows + * callers such as hotcopy to modify the contents before turning the + * tree into an accessible repository. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__create_file_tree(svn_fs_t *fs, + const char *path, + int format, + int shard_size, + apr_pool_t *scratch_pool); + +/* Create a fs_x fileysystem referenced by FS at path PATH. Get any + temporary allocations from SCRATCH_POOL. + + ### Some parts of *FS must have been initialized beforehand; some parts + (including FS->path) are initialized by this function. */ +svn_error_t * +svn_fs_x__create(svn_fs_t *fs, + const char *path, + apr_pool_t *scratch_pool); + +/* Set the uuid of repository FS to UUID and the instance ID to INSTANCE_ID. + If any of them is NULL, use a newly generated UUID / ID instead. + Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__set_uuid(svn_fs_t *fs, + const char *uuid, + const char *instance_id, + apr_pool_t *scratch_pool); + +/* 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 SCRATCH_POOL for temporary allocation. */ +svn_error_t * +svn_fs_x__write_format(svn_fs_t *fs, + svn_boolean_t overwrite, + apr_pool_t *scratch_pool); + +/* Find the value of the property named PROPNAME in transaction REV. + Return the contents in *VALUE_P, allocated from RESULT_POOL. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__revision_prop(svn_string_t **value_p, + svn_fs_t *fs, + svn_revnum_t rev, + const char *propname, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Change, add, or delete a property on a revision REV in filesystem + FS. NAME gives the name of the property, and value, if non-NULL, + gives the new contents of the property. If value is NULL, then the + property will be deleted. If OLD_VALUE_P is not NULL, do nothing unless + the preexisting value is *OLD_VALUE_P. + Do any temporary allocation in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__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 *scratch_pool); + +/* If directory PATH does not exist, create it and give it the same + permissions as FS_PATH. Do any temporary allocation in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__ensure_dir_exists(const char *path, + const char *fs_path, + apr_pool_t *scratch_pool); + +/* Initialize all session-local caches in FS according to the global + cache settings. Use SCRATCH_POOL for temporary allocations. + + Please note that it is permissible for this function to set some + or all of these caches to NULL, regardless of any setting. */ +svn_error_t * +svn_fs_x__initialize_caches(svn_fs_t *fs, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/hotcopy.c b/contrib/subversion/subversion/libsvn_fs_x/hotcopy.c new file mode 100644 index 000000000..c9f0af2f7 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/hotcopy.c @@ -0,0 +1,991 @@ +/* hotcopys.c --- FS hotcopy functionality for FSX + * + * ==================================================================== + * 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_pools.h" +#include "svn_path.h" +#include "svn_dirent_uri.h" + +#include "fs_x.h" +#include "hotcopy.h" +#include "util.h" +#include "revprops.h" +#include "rep-cache.h" +#include "transaction.h" +#include "recovery.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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. + * Set *SKIPPED_P to FALSE only if the file was copied, do not change + * the value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not + * required. */ +static svn_error_t * +hotcopy_io_dir_file_copy(svn_boolean_t *skipped_p, + 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; + } + + if (skipped_p) + *skipped_p = FALSE; + + 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 *result_pool) +{ + svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, result_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, result_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. Set *SKIPPED_P to FALSE only if at least one + * file was copied, do not change the value in *SKIPPED_P otherwise. + * SKIPPED_P may be NULL if not required. */ +static svn_error_t * +hotcopy_io_copy_dir_recursively(svn_boolean_t *skipped_p, + 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 *scratch_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(scratch_pool); + + /* The 'dst_path' is simply dst_parent/dst_basename */ + dst_path = svn_dirent_join(dst_parent, dst_basename, scratch_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, scratch_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, + scratch_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, scratch_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(skipped_p, 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(skipped_p, + 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, scratch_pool)); + + status = apr_dir_close(this_dir); + if (status) + return svn_error_wrap_apr(status, _("Error closing directory '%s'"), + svn_dirent_local_style(src, scratch_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. + * Set *SKIPPED_P to FALSE only if the file was copied, do not change the + * value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not required. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +hotcopy_copy_shard_file(svn_boolean_t *skipped_p, + 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; + + 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(skipped_p, + 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. + * Set *SKIPPED_P to FALSE only if at least one part of the shard + * was copied, do not change the value in *SKIPPED_P otherwise. + * SKIPPED_P may be NULL if not required. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +hotcopy_copy_packed_shard(svn_boolean_t *skipped_p, + 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; + svn_fs_x__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(skipped_p, 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->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(skipped_p, 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(skipped_p, 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(skipped_p, + 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(svn_fs_x__write_min_unpacked_rev(dst_fs, + *dst_min_unpacked_rev, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Remove file PATH, if it exists - even if it is read-only. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +hotcopy_remove_file(const char *path, + apr_pool_t *scratch_pool) +{ + /* Make the rev file writable and remove it. */ + SVN_ERR(svn_io_set_file_read_write(path, TRUE, scratch_pool)); + SVN_ERR(svn_io_remove_file2(path, TRUE, scratch_pool)); + + 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++) + { + 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 */ + SVN_ERR(hotcopy_remove_file(svn_dirent_join(dst_subdir_shard, + apr_psprintf(iterpool, + "%ld", rev), + iterpool), + 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) +{ + svn_fs_x__data_t *src_ffd = src_fs->fsap_data; + svn_fs_x__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 FSX format (%d) of the hotcopy source does not match the " + "FSX 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 SCRATCH_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 *scratch_pool) +{ + svn_error_t *err = svn_io_remove_dir2(path, TRUE, + cancel_func, cancel_baton, + scratch_pool); + + if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err)) + { + svn_error_clear(err); + err = SVN_NO_ERROR; + } + + return svn_error_trace(err); +} + +/* Copy the revision and revprop files (possibly sharded / packed) from + * SRC_FS to DST_FS. Do not re-copy data which already exists in DST_FS. + * When copying packed or unpacked shards, checkpoint the result in DST_FS + * for every shard by updating the 'current' file if necessary. Assume + * the >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT filesystem format without + * global next-ID counters. Indicate progress via the optional NOTIFY_FUNC + * callback using NOTIFY_BATON. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +hotcopy_revisions(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_revnum_t src_youngest, + svn_revnum_t dst_youngest, + svn_boolean_t incremental, + const char *src_revs_dir, + const char *dst_revs_dir, + const char *src_revprops_dir, + const char *dst_revprops_dir, + svn_fs_hotcopy_notify_t notify_func, + void* notify_baton, + svn_cancel_func_t cancel_func, + void* cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *src_ffd = src_fs->fsap_data; + int max_files_per_dir = src_ffd->max_files_per_dir; + svn_revnum_t src_min_unpacked_rev; + svn_revnum_t dst_min_unpacked_rev; + svn_revnum_t rev; + apr_pool_t *iterpool; + + /* Copy the min unpacked rev, and read its value. */ + SVN_ERR(svn_fs_x__read_min_unpacked_rev(&src_min_unpacked_rev, src_fs, + scratch_pool)); + SVN_ERR(svn_fs_x__read_min_unpacked_rev(&dst_min_unpacked_rev, dst_fs, + scratch_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, scratch_pool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* + * Copy the necessary rev files. + */ + + iterpool = svn_pool_create(scratch_pool); + /* First, copy packed shards. */ + for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir) + { + svn_boolean_t skipped = TRUE; + svn_revnum_t pack_end_rev; + + svn_pool_clear(iterpool); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Copy the packed shard. */ + SVN_ERR(hotcopy_copy_packed_shard(&skipped, &dst_min_unpacked_rev, + src_fs, dst_fs, + rev, max_files_per_dir, + iterpool)); + + pack_end_rev = rev + max_files_per_dir - 1; + + /* Whenever this pack did not previously exist in the destination, + * update 'current' to the most recent packed rev (so readers can see + * new revisions which arrived in this pack). */ + if (pack_end_rev > dst_youngest) + { + SVN_ERR(svn_fs_x__write_current(dst_fs, pack_end_rev, iterpool)); + } + + /* When notifying about packed shards, make things simpler by either + * reporting a full revision range, i.e [pack start, pack end] or + * reporting nothing. There is one case when this approach might not + * be exact (incremental hotcopy with a pack replacing last unpacked + * revisions), but generally this is good enough. */ + if (notify_func && !skipped) + notify_func(notify_baton, rev, pack_end_rev, 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)); + 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(svn_fs_x__path_rev_shard(dst_fs, rev, iterpool), + cancel_func, cancel_baton, iterpool)); + if (rev > 0) + SVN_ERR(remove_folder(svn_fs_x__path_revprops_shard(dst_fs, rev, + iterpool), + cancel_func, cancel_baton, iterpool)); + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + SVN_ERR_ASSERT(rev == src_min_unpacked_rev); + SVN_ERR_ASSERT(src_min_unpacked_rev == dst_min_unpacked_rev); + + /* Now, copy pairs of non-packed revisions and revprop files. + * If necessary, update 'current' after copying all files from a shard. */ + for (; rev <= src_youngest; rev++) + { + svn_boolean_t skipped = TRUE; + + svn_pool_clear(iterpool); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Copying non-packed revisions is racy in case the source repository is + * being packed concurrently with this hotcopy operation. With the pack + * lock, however, the race is impossible, because hotcopy and pack + * operations block each other. + * + * We assume that all revisions coming after 'min-unpacked-rev' really + * are unpacked and that's not necessarily true with concurrent packing. + * Don't try to be smart in this edge case, because handling it properly + * might require copying *everything* from the start. Just abort the + * hotcopy with an ENOENT (revision file moved to a pack, so it is no + * longer where we expect it to be). */ + + /* Copy the rev file. */ + SVN_ERR(hotcopy_copy_shard_file(&skipped, src_revs_dir, dst_revs_dir, + rev, max_files_per_dir, + iterpool)); + + /* Copy the revprop file. */ + SVN_ERR(hotcopy_copy_shard_file(&skipped, src_revprops_dir, + dst_revprops_dir, + rev, max_files_per_dir, + iterpool)); + + /* Whenever this revision did not previously exist in the destination, + * checkpoint the progress via 'current' (do that once per full shard + * in order not to slow things down). */ + if (rev > dst_youngest) + { + if (max_files_per_dir && (rev % max_files_per_dir == 0)) + { + SVN_ERR(svn_fs_x__write_current(dst_fs, rev, iterpool)); + } + } + + if (notify_func && !skipped) + notify_func(notify_baton, rev, rev, iterpool); + } + svn_pool_destroy(iterpool); + + /* 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); + + return SVN_NO_ERROR; +} + +/* Baton for hotcopy_body(). */ +typedef struct hotcopy_body_baton_t { + svn_fs_t *src_fs; + svn_fs_t *dst_fs; + svn_boolean_t incremental; + svn_fs_hotcopy_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_baton; +} hotcopy_body_baton_t; + +/* 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 *scratch_pool) +{ + hotcopy_body_baton_t *hbb = baton; + svn_fs_t *src_fs = hbb->src_fs; + svn_fs_t *dst_fs = hbb->dst_fs; + svn_boolean_t incremental = hbb->incremental; + svn_fs_hotcopy_notify_t notify_func = hbb->notify_func; + void* notify_baton = hbb->notify_baton; + 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; + const char *src_revprops_dir; + const char *dst_revprops_dir; + const char *src_revs_dir; + const char *dst_revs_dir; + const char *src_subdir; + const char *dst_subdir; + 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. */ + SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG, + scratch_pool)); + + 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(svn_fs_x__read_current(&src_youngest, src_fs, scratch_pool)); + if (incremental) + { + SVN_ERR(svn_fs_x__youngest_rev(&dst_youngest, dst_fs, scratch_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; + + src_revs_dir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, scratch_pool); + dst_revs_dir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool); + src_revprops_dir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, + scratch_pool); + dst_revprops_dir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, + scratch_pool); + + /* Ensure that the required folders exist in the destination + * before actually copying the revisions and revprops. */ + SVN_ERR(svn_io_make_dir_recursively(dst_revs_dir, scratch_pool)); + SVN_ERR(svn_io_make_dir_recursively(dst_revprops_dir, scratch_pool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + /* Split the logic for new and old FS formats. The latter is much simpler + * due to the absense of sharding and packing. However, it requires special + * care when updating the 'current' file (which contains not just the + * revision number, but also the next-ID counters). */ + SVN_ERR(hotcopy_revisions(src_fs, dst_fs, src_youngest, dst_youngest, + incremental, src_revs_dir, dst_revs_dir, + src_revprops_dir, dst_revprops_dir, + notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool)); + SVN_ERR(svn_fs_x__write_current(dst_fs, src_youngest, scratch_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, scratch_pool); + SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton, + scratch_pool)); + src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, scratch_pool); + SVN_ERR(svn_io_check_path(src_subdir, &kind, scratch_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, + scratch_pool)); + + /* Now copy the node-origins cache tree. */ + src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, + scratch_pool); + SVN_ERR(svn_io_check_path(src_subdir, &kind, scratch_pool)); + if (kind == svn_node_dir) + SVN_ERR(hotcopy_io_copy_dir_recursively(NULL, src_subdir, dst_fs->path, + PATH_NODE_ORIGINS_DIR, TRUE, + cancel_func, cancel_baton, + scratch_pool)); + + /* + * NB: Data copied below is only read by writers, not readers. + * Writers are still locked out at this point. + */ + + /* 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, scratch_pool); + dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, scratch_pool); + SVN_ERR(svn_io_check_path(src_subdir, &kind, scratch_pool)); + if (kind == svn_node_file) + { + /* Copy the rep cache and then remove entries for revisions + * that did not make it into the destination. */ + SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, scratch_pool)); + SVN_ERR(svn_fs_x__del_rep_reference(dst_fs, src_youngest, + scratch_pool)); + } + + /* Copy the txn-current file. */ + SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path, + PATH_TXN_CURRENT, scratch_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_fs_x__reset_revprop_generation_file(dst_fs, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Wrapper around hotcopy_body taking out all necessary source repository + * locks. + */ +static svn_error_t * +hotcopy_locking_src_body(void *baton, + apr_pool_t *scratch_pool) +{ + hotcopy_body_baton_t *hbb = baton; + + return svn_error_trace(svn_fs_x__with_pack_lock(hbb->src_fs, hotcopy_body, + baton, scratch_pool)); +} + +/* 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 *scratch_pool) +{ + svn_fs_x__data_t *src_ffd = src_fs->fsap_data; + + /* Create the DST_FS repository with the same layout as SRC_FS. */ + SVN_ERR(svn_fs_x__create_file_tree(dst_fs, dst_path, src_ffd->format, + src_ffd->max_files_per_dir, + scratch_pool)); + + /* Copy the UUID. Hotcopy destination receives a new instance ID, but + * has the same filesystem UUID as the source. */ + SVN_ERR(svn_fs_x__set_uuid(dst_fs, src_fs->uuid, NULL, scratch_pool)); + + /* Remove revision 0 contents. Otherwise, it may not get overwritten + * due to having a newer timestamp. */ + SVN_ERR(hotcopy_remove_file(svn_fs_x__path_rev(dst_fs, 0, scratch_pool), + scratch_pool)); + SVN_ERR(hotcopy_remove_file(svn_fs_x__path_revprops(dst_fs, 0, + scratch_pool), + scratch_pool)); + + /* This filesystem is ready. Stamp it with a format number. Fail if + * the 'format' file should already exist. */ + SVN_ERR(svn_fs_x__write_format(dst_fs, FALSE, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__hotcopy_prepare_target(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *dst_path, + svn_boolean_t incremental, + apr_pool_t *scratch_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, + scratch_pool); + SVN_ERR(svn_io_check_path(dst_format_abspath, &dst_format_kind, + scratch_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, + scratch_pool)); + } + else + { + /* Check the existing repository. */ + SVN_ERR(svn_fs_x__open(dst_fs, dst_path, scratch_pool)); + SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs)); + } + } + 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, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + hotcopy_body_baton_t hbb; + + hbb.src_fs = src_fs; + hbb.dst_fs = dst_fs; + hbb.incremental = incremental; + hbb.notify_func = notify_func; + hbb.notify_baton = notify_baton; + hbb.cancel_func = cancel_func; + hbb.cancel_baton = cancel_baton; + SVN_ERR(svn_fs_x__with_all_locks(dst_fs, hotcopy_locking_src_body, &hbb, + scratch_pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/hotcopy.h b/contrib/subversion/subversion/libsvn_fs_x/hotcopy.h new file mode 100644 index 000000000..516c66ade --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/hotcopy.h @@ -0,0 +1,53 @@ +/* hotcopy.h : interface to the native filesystem layer + * + * ==================================================================== + * 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_FS__HOTCOPY_H +#define SVN_LIBSVN_FS__HOTCOPY_H + +#include "fs.h" + +/* Create an empty copy of the fsfs filesystem SRC_FS into a new DST_FS at + * DST_PATH. If INCREMENTAL is TRUE, perform a few pre-checks only if + * a repo already exists at DST_PATH. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__hotcopy_prepare_target(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *dst_path, + svn_boolean_t incremental, + apr_pool_t *scratch_pool); + +/* Copy the fsfs filesystem SRC_FS into DST_FS. If INCREMENTAL is TRUE, do + * not re-copy data which already exists in DST_FS. Indicate progress via + * the optional NOTIFY_FUNC callback using NOTIFY_BATON. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + svn_boolean_t incremental, + svn_fs_hotcopy_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/id.c b/contrib/subversion/subversion/libsvn_fs_x/id.c new file mode 100644 index 000000000..012717598 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/id.c @@ -0,0 +1,198 @@ +/* id.c : implements FSX-internal ID functions + * + * ==================================================================== + * 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 + +#include "id.h" +#include "index.h" +#include "util.h" + +#include "private/svn_string_private.h" + + + +svn_boolean_t +svn_fs_x__is_txn(svn_fs_x__change_set_t change_set) +{ + return change_set < SVN_FS_X__INVALID_CHANGE_SET; +} + +svn_boolean_t +svn_fs_x__is_revision(svn_fs_x__change_set_t change_set) +{ + return change_set > SVN_FS_X__INVALID_CHANGE_SET; +} + +svn_revnum_t +svn_fs_x__get_revnum(svn_fs_x__change_set_t change_set) +{ + return svn_fs_x__is_revision(change_set) + ? (svn_revnum_t)change_set + : SVN_INVALID_REVNUM; +} + +apr_int64_t +svn_fs_x__get_txn_id(svn_fs_x__change_set_t change_set) +{ + return svn_fs_x__is_txn(change_set) + ? -change_set + SVN_FS_X__INVALID_CHANGE_SET -1 + : SVN_FS_X__INVALID_TXN_ID; +} + + +svn_fs_x__change_set_t +svn_fs_x__change_set_by_rev(svn_revnum_t revnum) +{ + assert(revnum >= SVN_FS_X__INVALID_CHANGE_SET); + return revnum; +} + +svn_fs_x__change_set_t +svn_fs_x__change_set_by_txn(apr_int64_t txn_id) +{ + assert(txn_id >= SVN_FS_X__INVALID_CHANGE_SET); + return -txn_id + SVN_FS_X__INVALID_CHANGE_SET -1; +} + + +/* Parse the NUL-terminated ID part at DATA and write the result into *PART. + * Return TRUE if no errors were detected. */ +static svn_boolean_t +part_parse(svn_fs_x__id_t *part, + const char *data) +{ + part->number = svn__base36toui64(&data, data); + switch (data[0]) + { + /* txn number? */ + case '-': part->change_set = -svn__base36toui64(&data, data + 1); + return TRUE; + + /* revision number? */ + case '+': part->change_set = svn__base36toui64(&data, data + 1); + return TRUE; + + /* everything else is forbidden */ + default: return FALSE; + } +} + +/* Write the textual representation of *PART into P and return a pointer + * to the first position behind that string. + */ +static char * +part_unparse(char *p, + const svn_fs_x__id_t *part) +{ + p += svn__ui64tobase36(p, part->number); + if (part->change_set >= 0) + { + *(p++) = '+'; + p += svn__ui64tobase36(p, part->change_set); + } + else + { + *(p++) = '-'; + p += svn__ui64tobase36(p, -part->change_set); + } + + return p; +} + + + +/* Operations on ID parts */ + +svn_boolean_t +svn_fs_x__id_is_root(const svn_fs_x__id_t* part) +{ + return part->change_set == 0 && part->number == 0; +} + +svn_boolean_t +svn_fs_x__id_eq(const svn_fs_x__id_t *lhs, + const svn_fs_x__id_t *rhs) +{ + return lhs->change_set == rhs->change_set && lhs->number == rhs->number; +} + +svn_error_t * +svn_fs_x__id_parse(svn_fs_x__id_t *part, + const char *data) +{ + if (!part_parse(part, data)) + return svn_error_createf(SVN_ERR_FS_MALFORMED_NODEREV_ID, NULL, + "Malformed ID string"); + + return SVN_NO_ERROR; +} + +svn_string_t * +svn_fs_x__id_unparse(const svn_fs_x__id_t *id, + apr_pool_t *result_pool) +{ + char string[2 * SVN_INT64_BUFFER_SIZE + 1]; + char *p = part_unparse(string, id); + + return svn_string_ncreate(string, p - string, result_pool); +} + +void +svn_fs_x__id_reset(svn_fs_x__id_t *part) +{ + part->change_set = SVN_FS_X__INVALID_CHANGE_SET; + part->number = 0; +} + +svn_boolean_t +svn_fs_x__id_used(const svn_fs_x__id_t *part) +{ + return part->change_set != SVN_FS_X__INVALID_CHANGE_SET; +} + +void +svn_fs_x__init_txn_root(svn_fs_x__id_t *noderev_id, + svn_fs_x__txn_id_t txn_id) +{ + noderev_id->change_set = svn_fs_x__change_set_by_txn(txn_id); + noderev_id->number = SVN_FS_X__ITEM_INDEX_ROOT_NODE; +} + +void +svn_fs_x__init_rev_root(svn_fs_x__id_t *noderev_id, + svn_revnum_t rev) +{ + noderev_id->change_set = svn_fs_x__change_set_by_rev(rev); + noderev_id->number = SVN_FS_X__ITEM_INDEX_ROOT_NODE; +} + +int +svn_fs_x__id_compare(const svn_fs_x__id_t *a, + const svn_fs_x__id_t *b) +{ + if (a->change_set < b->change_set) + return -1; + if (a->change_set > b->change_set) + return 1; + + return a->number < b->number ? -1 : a->number == b->number ? 0 : 1; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/id.h b/contrib/subversion/subversion/libsvn_fs_x/id.h new file mode 100644 index 000000000..e58404316 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/id.h @@ -0,0 +1,135 @@ +/* id.h : interface to FSX-internal ID functions + * + * ==================================================================== + * 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_FS_X_ID_H +#define SVN_LIBSVN_FS_X_ID_H + +#include "svn_fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Unique identifier for a transaction within the given repository. */ +typedef apr_int64_t svn_fs_x__txn_id_t; + +/* svn_fs_x__txn_id_t value for everything that is not a transaction. */ +#define SVN_FS_X__INVALID_TXN_ID ((svn_fs_x__txn_id_t)(-1)) + +/* Change set is the umbrella term for transaction and revision in FSX. + * Revision numbers (>=0) map 1:1 onto change sets while txns are mapped + * onto the negatve value range. */ +typedef apr_int64_t svn_fs_x__change_set_t; + +/* Invalid / unused change set number. */ +#define SVN_FS_X__INVALID_CHANGE_SET ((svn_fs_x__change_set_t)(-1)) + +/* Return TRUE iff the CHANGE_SET refers to a revision + (will return FALSE for SVN_INVALID_REVNUM). */ +svn_boolean_t +svn_fs_x__is_revision(svn_fs_x__change_set_t change_set); + +/* Return TRUE iff the CHANGE_SET refers to a transaction + (will return FALSE for SVN_FS_X__INVALID_TXN_ID). */ +svn_boolean_t +svn_fs_x__is_txn(svn_fs_x__change_set_t change_set); + +/* Return the revision number that corresponds to CHANGE_SET. + Will SVN_INVALID_REVNUM for transactions. */ +svn_revnum_t +svn_fs_x__get_revnum(svn_fs_x__change_set_t change_set); + +/* Return the transaction ID that corresponds to CHANGE_SET. + Will SVN_FS_X__INVALID_TXN_ID for revisions. */ +svn_fs_x__txn_id_t +svn_fs_x__get_txn_id(svn_fs_x__change_set_t change_set); + +/* Convert REVNUM into a change set number */ +svn_fs_x__change_set_t +svn_fs_x__change_set_by_rev(svn_revnum_t revnum); + +/* Convert TXN_ID into a change set number */ +svn_fs_x__change_set_t +svn_fs_x__change_set_by_txn(svn_fs_x__txn_id_t txn_id); + +/* An ID in FSX consists of a creation CHANGE_SET number and some changeset- + * local counter value (NUMBER). + */ +typedef struct svn_fs_x__id_t +{ + svn_fs_x__change_set_t change_set; + + apr_uint64_t number; +} svn_fs_x__id_t; + + +/*** Operations on ID parts. ***/ + +/* Return TRUE, if both elements of the PART is 0, i.e. this is the default + * value if e.g. no copies were made of this node. */ +svn_boolean_t +svn_fs_x__id_is_root(const svn_fs_x__id_t *part); + +/* Return TRUE, if all element values of *LHS and *RHS match. */ +svn_boolean_t +svn_fs_x__id_eq(const svn_fs_x__id_t *lhs, + const svn_fs_x__id_t *rhs); + +/* Parse the NUL-terminated ID part at DATA and write the result into *PART. + */ +svn_error_t * +svn_fs_x__id_parse(svn_fs_x__id_t *part, + const char *data); + +/* Convert ID into string form, allocated in RESULT_POOL. */ +svn_string_t * +svn_fs_x__id_unparse(const svn_fs_x__id_t*id, + apr_pool_t *result_pool); + +/* Set *PART to "unused". */ +void +svn_fs_x__id_reset(svn_fs_x__id_t *part); + +/* Return TRUE if *PART is belongs to either a revision or transaction. */ +svn_boolean_t +svn_fs_x__id_used(const svn_fs_x__id_t *part); + +/* Return 0 if A and B are equal, 1 if A is "greater than" B, -1 otherwise. */ +int +svn_fs_x__id_compare(const svn_fs_x__id_t *a, + const svn_fs_x__id_t *b); + +/* Set *NODEREV_ID to the root node ID of transaction TXN_ID. */ +void +svn_fs_x__init_txn_root(svn_fs_x__id_t *noderev_id, + svn_fs_x__txn_id_t txn_id); + +/* Set *NODEREV_ID to the root node ID of revision REV. */ +void +svn_fs_x__init_rev_root(svn_fs_x__id_t *noderev_id, + svn_revnum_t rev); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_X_ID_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/index.c b/contrib/subversion/subversion/libsvn_fs_x/index.c new file mode 100644 index 000000000..7d568f918 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/index.c @@ -0,0 +1,3981 @@ +/* index.c indexing support for FSX 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. + * ==================================================================== + */ + +#include + +#include "svn_io.h" +#include "svn_pools.h" +#include "svn_sorts.h" + +#include "index.h" +#include "util.h" +#include "pack.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "svn_private_config.h" +#include "temp_serializer.h" +#include "fs_x.h" + +#include "../libsvn_fs/fs-loader.h" + +/* maximum length of a uint64 in an 7/8b encoding */ +#define ENCODED_INT_LENGTH 10 + +/* APR is missing an APR_OFF_T_MAX. So, define one. We will use it to + * limit file offsets stored in the indexes. + * + * We assume that everything shorter than 64 bits, it is at least 32 bits. + * We also assume that the type is always signed meaning we only have an + * effective positive range of 63 or 31 bits, respectively. + */ +static +const apr_uint64_t off_t_max = (sizeof(apr_off_t) == sizeof(apr_int64_t)) + ? APR_INT64_MAX + : APR_INT32_MAX; + +/* We store P2L proto-index entries as 6 values, 64 bits each on disk. + * See also svn_fs_fs__p2l_proto_index_add_entry(). + */ +#define P2L_PROTO_INDEX_ENTRY_SIZE (6 * sizeof(apr_uint64_t)) + +/* We put this string in front of the L2P index header. */ +#define L2P_STREAM_PREFIX "L2P-INDEX\n" + +/* We put this string in front of the P2L index header. */ +#define P2L_STREAM_PREFIX "P2L-INDEX\n" + +/* Size of the buffer that will fit the index header prefixes. */ +#define STREAM_PREFIX_LEN MAX(sizeof(L2P_STREAM_PREFIX), \ + sizeof(P2L_STREAM_PREFIX)) + +/* Page tables in the log-to-phys index file exclusively contain entries + * of this type to describe position and size of a given page. + */ +typedef struct l2p_page_table_entry_t +{ + /* global offset on the page within the index file */ + apr_uint64_t offset; + + /* number of mapping entries in that page */ + apr_uint32_t entry_count; + + /* size of the page on disk (in the index file) */ + apr_uint32_t size; +} l2p_page_table_entry_t; + +/* Master run-time data structure of an log-to-phys index. It contains + * the page tables of every revision covered by that index - but not the + * pages themselves. + */ +typedef struct l2p_header_t +{ + /* first revision covered by this index */ + svn_revnum_t first_revision; + + /* number of revisions covered */ + apr_size_t revision_count; + + /* (max) number of entries per page */ + apr_uint32_t page_size; + + /* indexes into PAGE_TABLE that mark the first page of the respective + * revision. PAGE_TABLE_INDEX[REVISION_COUNT] points to the end of + * PAGE_TABLE. + */ + apr_size_t * page_table_index; + + /* Page table covering all pages in the index */ + l2p_page_table_entry_t * page_table; +} l2p_header_t; + +/* Run-time data structure containing a single log-to-phys index page. + */ +typedef struct l2p_page_t +{ + /* number of entries in the OFFSETS array */ + apr_uint32_t entry_count; + + /* global file offsets (item index is the array index) within the + * packed or non-packed rev file. Offset will be -1 for unused / + * invalid item index values. */ + apr_off_t *offsets; + + /* In case that the item is stored inside a container, this is the + * identifying index of the item within that container. 0 for the + * container itself or for items that aren't containers. */ + apr_uint32_t *sub_items; +} l2p_page_t; + +/* All of the log-to-phys proto index file consist of entries of this type. + */ +typedef struct l2p_proto_entry_t +{ + /* phys offset + 1 of the data container. 0 for "new revision" entries. */ + apr_uint64_t offset; + + /* corresponding item index. 0 for "new revision" entries. */ + apr_uint64_t item_index; + + /* index within the container starting @ offset. 0 for "new revision" + * entries and for items with no outer container. */ + apr_uint32_t sub_item; +} l2p_proto_entry_t; + +/* Master run-time data structure of an phys-to-log index. It contains + * an array with one offset value for each rev file cluster. + */ +typedef struct p2l_header_t +{ + /* first revision covered by the index (and rev file) */ + svn_revnum_t first_revision; + + /* number of bytes in the rev files covered by each p2l page */ + apr_uint64_t page_size; + + /* number of pages / clusters in that rev file */ + apr_size_t page_count; + + /* number of bytes in the rev file */ + apr_uint64_t file_size; + + /* offsets of the pages / cluster descriptions within the index file */ + apr_off_t *offsets; +} p2l_header_t; + +/* + * packed stream array + */ + +/* How many numbers we will pre-fetch and buffer in a packed number stream. + */ +enum { MAX_NUMBER_PREFETCH = 64 }; + +/* Prefetched number entry in a packed number stream. + */ +typedef struct value_position_pair_t +{ + /* prefetched number */ + apr_uint64_t value; + + /* number of bytes read, *including* this number, since the buffer start */ + apr_size_t total_len; +} value_position_pair_t; + +/* State of a prefetching packed number stream. It will read compressed + * index data efficiently and present it as a series of non-packed uint64. + */ +struct svn_fs_x__packed_number_stream_t +{ + /* underlying data file containing the packed values */ + apr_file_t *file; + + /* Offset within FILE at which the stream data starts + * (i.e. which offset will reported as offset 0 by packed_stream_offset). */ + apr_off_t stream_start; + + /* First offset within FILE after the stream data. + * Attempts to read beyond this will cause an "Unexpected End Of Stream" + * error. */ + apr_off_t stream_end; + + /* number of used entries in BUFFER (starting at index 0) */ + apr_size_t used; + + /* index of the next number to read from the BUFFER (0 .. USED). + * If CURRENT == USED, we need to read more data upon get() */ + apr_size_t current; + + /* offset in FILE from which the first entry in BUFFER has been read */ + apr_off_t start_offset; + + /* offset in FILE from which the next number has to be read */ + apr_off_t next_offset; + + /* read the file in chunks of this size */ + apr_size_t block_size; + + /* pool to be used for file ops etc. */ + apr_pool_t *pool; + + /* buffer for prefetched values */ + value_position_pair_t buffer[MAX_NUMBER_PREFETCH]; +}; + +/* Return an svn_error_t * object for error ERR on STREAM with the given + * MESSAGE string. The latter must have a placeholder for the index file + * name ("%s") and the current read offset (e.g. "0x%lx"). + */ +static svn_error_t * +stream_error_create(svn_fs_x__packed_number_stream_t *stream, + apr_status_t err, + const char *message) +{ + const char *file_name; + apr_off_t offset; + SVN_ERR(svn_io_file_name_get(&file_name, stream->file, + stream->pool)); + SVN_ERR(svn_fs_x__get_file_offset(&offset, stream->file, stream->pool)); + + return svn_error_createf(err, NULL, message, file_name, + apr_psprintf(stream->pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)offset)); +} + +/* Read up to MAX_NUMBER_PREFETCH numbers from the STREAM->NEXT_OFFSET in + * STREAM->FILE and buffer them. + * + * We don't want GCC and others to inline this (infrequently called) + * function into packed_stream_get() because it prevents the latter from + * being inlined itself. + */ +SVN__PREVENT_INLINE +static svn_error_t * +packed_stream_read(svn_fs_x__packed_number_stream_t *stream) +{ + unsigned char buffer[MAX_NUMBER_PREFETCH]; + apr_size_t read = 0; + apr_size_t i; + value_position_pair_t *target; + apr_off_t block_start = 0; + apr_off_t block_left = 0; + apr_status_t err; + + /* all buffered data will have been read starting here */ + stream->start_offset = stream->next_offset; + + /* packed numbers are usually not aligned to MAX_NUMBER_PREFETCH blocks, + * i.e. the last number has been incomplete (and not buffered in stream) + * and need to be re-read. Therefore, always correct the file pointer. + */ + SVN_ERR(svn_io_file_aligned_seek(stream->file, stream->block_size, + &block_start, stream->next_offset, + stream->pool)); + + /* prefetch at least one number but, if feasible, don't cross block + * boundaries. This shall prevent jumping back and forth between two + * blocks because the extra data was not actually request _now_. + */ + read = sizeof(buffer); + block_left = stream->block_size - (stream->next_offset - block_start); + if (block_left >= 10 && block_left < read) + read = (apr_size_t)block_left; + + /* Don't read beyond the end of the file section that belongs to this + * index / stream. */ + read = (apr_size_t)MIN(read, stream->stream_end - stream->next_offset); + + err = apr_file_read(stream->file, buffer, &read); + if (err && !APR_STATUS_IS_EOF(err)) + return stream_error_create(stream, err, + _("Can't read index file '%s' at offset 0x%")); + + /* if the last number is incomplete, trim it from the buffer */ + while (read > 0 && buffer[read-1] >= 0x80) + --read; + + /* we call read() only if get() requires more data. So, there must be + * at least *one* further number. */ + if SVN__PREDICT_FALSE(read == 0) + return stream_error_create(stream, err, + _("Unexpected end of index file %s at offset 0x%")); + + /* parse file buffer and expand into stream buffer */ + target = stream->buffer; + for (i = 0; i < read;) + { + if (buffer[i] < 0x80) + { + /* numbers < 128 are relatively frequent and particularly easy + * to decode. Give them special treatment. */ + target->value = buffer[i]; + ++i; + target->total_len = i; + ++target; + } + else + { + apr_uint64_t value = 0; + apr_uint64_t shift = 0; + while (buffer[i] >= 0x80) + { + value += ((apr_uint64_t)buffer[i] & 0x7f) << shift; + shift += 7; + ++i; + } + + target->value = value + ((apr_uint64_t)buffer[i] << shift); + ++i; + target->total_len = i; + ++target; + + /* let's catch corrupted data early. It would surely cause + * havoc further down the line. */ + if SVN__PREDICT_FALSE(shift > 8 * sizeof(value)) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Corrupt index: number too large")); + } + } + + /* update stream state */ + stream->used = target - stream->buffer; + stream->next_offset = stream->start_offset + i; + stream->current = 0; + + return SVN_NO_ERROR; +} + +/* Create and open a packed number stream reading from offsets START to + * END in FILE and return it in *STREAM. Access the file in chunks of + * BLOCK_SIZE bytes. Expect the stream to be prefixed by STREAM_PREFIX. + * Allocate *STREAM in RESULT_POOL and use SCRATCH_POOL for temporaries. + */ +static svn_error_t * +packed_stream_open(svn_fs_x__packed_number_stream_t **stream, + apr_file_t *file, + apr_off_t start, + apr_off_t end, + const char *stream_prefix, + apr_size_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char buffer[STREAM_PREFIX_LEN + 1] = { 0 }; + apr_size_t len = strlen(stream_prefix); + svn_fs_x__packed_number_stream_t *result; + + /* If this is violated, we forgot to adjust STREAM_PREFIX_LEN after + * changing the index header prefixes. */ + SVN_ERR_ASSERT(len < sizeof(buffer)); + + /* Read the header prefix and compare it with the expected prefix */ + SVN_ERR(svn_io_file_aligned_seek(file, block_size, NULL, start, + scratch_pool)); + SVN_ERR(svn_io_file_read_full2(file, buffer, len, NULL, NULL, + scratch_pool)); + + if (strncmp(buffer, stream_prefix, len)) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index stream header prefix mismatch.\n" + " expected: %s" + " found: %s"), stream_prefix, buffer); + + /* Construct the actual stream object. */ + result = apr_palloc(result_pool, sizeof(*result)); + + result->pool = result_pool; + result->file = file; + result->stream_start = start + len; + result->stream_end = end; + + result->used = 0; + result->current = 0; + result->start_offset = result->stream_start; + result->next_offset = result->stream_start; + result->block_size = block_size; + + *stream = result; + + return SVN_NO_ERROR; +} + +/* + * The forced inline is required for performance reasons: This is a very + * hot code path (called for every item we read) but e.g. GCC would rather + * chose to inline packed_stream_read() here, preventing packed_stream_get + * from being inlined itself. + */ +SVN__FORCE_INLINE +static svn_error_t* +packed_stream_get(apr_uint64_t *value, + svn_fs_x__packed_number_stream_t *stream) +{ + if (stream->current == stream->used) + SVN_ERR(packed_stream_read(stream)); + + *value = stream->buffer[stream->current].value; + ++stream->current; + + return SVN_NO_ERROR; +} + +/* Navigate STREAM to packed stream offset OFFSET. There will be no checks + * whether the given OFFSET is valid. + */ +static void +packed_stream_seek(svn_fs_x__packed_number_stream_t *stream, + apr_off_t offset) +{ + apr_off_t file_offset = offset + stream->stream_start; + + if ( stream->used == 0 + || offset < stream->start_offset + || offset >= stream->next_offset) + { + /* outside buffered data. Next get() will read() from OFFSET. */ + stream->start_offset = file_offset; + stream->next_offset = file_offset; + stream->current = 0; + stream->used = 0; + } + else + { + /* Find the suitable location in the stream buffer. + * Since our buffer is small, it is efficient enough to simply scan + * it for the desired position. */ + apr_size_t i; + for (i = 0; i < stream->used; ++i) + if (stream->buffer[i].total_len > file_offset - stream->start_offset) + break; + + stream->current = i; + } +} + +/* Return the packed stream offset of at which the next number in the stream + * can be found. + */ +static apr_off_t +packed_stream_offset(svn_fs_x__packed_number_stream_t *stream) +{ + apr_off_t file_offset + = stream->current == 0 + ? stream->start_offset + : stream->buffer[stream->current-1].total_len + stream->start_offset; + + return file_offset - stream->stream_start; +} + +/* Write VALUE to the PROTO_INDEX file, using SCRATCH_POOL for temporary + * allocations. + * + * The point of this function is to ensure an architecture-independent + * proto-index file format. All data is written as unsigned 64 bits ints + * in little endian byte order. 64 bits is the largest portable integer + * we have and unsigned values have well-defined conversions in C. + */ +static svn_error_t * +write_uint64_to_proto_index(apr_file_t *proto_index, + apr_uint64_t value, + apr_pool_t *scratch_pool) +{ + apr_byte_t buffer[sizeof(value)]; + int i; + apr_size_t written; + + /* Split VALUE into 8 bytes using LE ordering. */ + for (i = 0; i < sizeof(buffer); ++i) + { + /* Unsigned conversions are well-defined ... */ + buffer[i] = (apr_byte_t)value; + value >>= CHAR_BIT; + } + + /* Write it all to disk. */ + SVN_ERR(svn_io_file_write_full(proto_index, buffer, sizeof(buffer), + &written, scratch_pool)); + SVN_ERR_ASSERT(written == sizeof(buffer)); + + return SVN_NO_ERROR; +} + +/* Read one unsigned 64 bit value from PROTO_INDEX file and return it in + * *VALUE_P. If EOF is NULL, error out when trying to read beyond EOF. + * Use SCRATCH_POOL for temporary allocations. + * + * This function is the inverse to write_uint64_to_proto_index (see there), + * reading the external LE byte order and convert it into host byte order. + */ +static svn_error_t * +read_uint64_from_proto_index(apr_file_t *proto_index, + apr_uint64_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_byte_t buffer[sizeof(*value_p)]; + apr_size_t read; + + /* Read the full 8 bytes or our 64 bit value, unless we hit EOF. + * Assert that we never read partial values. */ + SVN_ERR(svn_io_file_read_full2(proto_index, buffer, sizeof(buffer), + &read, eof, scratch_pool)); + SVN_ERR_ASSERT((eof && *eof) || read == sizeof(buffer)); + + /* If we did not hit EOF, reconstruct the uint64 value and return it. */ + if (!eof || !*eof) + { + int i; + apr_uint64_t value; + + /* This could only overflow if CHAR_BIT had a value that is not + * a divisor of 64. */ + value = 0; + for (i = sizeof(buffer) - 1; i >= 0; --i) + value = (value << CHAR_BIT) + buffer[i]; + + *value_p = value; + } + + return SVN_NO_ERROR; +} + +/* Convenience function similar to read_uint64_from_proto_index, but returns + * an uint32 value in VALUE_P. Return an error if the value does not fit. + */ +static svn_error_t * +read_uint32_from_proto_index(apr_file_t *proto_index, + apr_uint32_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_uint64_t value; + SVN_ERR(read_uint64_from_proto_index(proto_index, &value, eof, + scratch_pool)); + if (!eof || !*eof) + { + if (value > APR_UINT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("UINT32 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + value), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)APR_UINT32_MAX)); + + /* This conversion is not lossy because the value can be represented + * in the target type. */ + *value_p = (apr_uint32_t)value; + } + + return SVN_NO_ERROR; +} + +/* Convenience function similar to read_uint64_from_proto_index, but returns + * an off_t value in VALUE_P. Return an error if the value does not fit. + */ +static svn_error_t * +read_off_t_from_proto_index(apr_file_t *proto_index, + apr_off_t *value_p, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_uint64_t value; + SVN_ERR(read_uint64_from_proto_index(proto_index, &value, eof, + scratch_pool)); + if (!eof || !*eof) + { + if (value > off_t_max) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("File offset 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + value), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + off_t_max)); + + /* Shortening conversion from unsigned to signed int is well-defined + * and not lossy in C because the value can be represented in the + * target type. */ + *value_p = (apr_off_t)value; + } + + return SVN_NO_ERROR; +} + +/* + * log-to-phys index + */ +svn_error_t * +svn_fs_x__l2p_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool) +{ + SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE + | APR_CREATE | APR_APPEND | APR_BUFFERED, + APR_OS_DEFAULT, result_pool)); + + return SVN_NO_ERROR; +} + +/* Append ENTRY to log-to-phys PROTO_INDEX file. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_l2p_entry_to_proto_index(apr_file_t *proto_index, + l2p_proto_entry_t entry, + apr_pool_t *scratch_pool) +{ + SVN_ERR(write_uint64_to_proto_index(proto_index, entry.offset, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry.item_index, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry.sub_item, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read *ENTRY from log-to-phys PROTO_INDEX file and indicate end-of-file + * in *EOF, or error out in that case if EOF is NULL. *ENTRY is in an + * undefined state if an end-of-file occurred. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +read_l2p_entry_from_proto_index(apr_file_t *proto_index, + l2p_proto_entry_t *entry, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + SVN_ERR(read_uint64_from_proto_index(proto_index, &entry->offset, eof, + scratch_pool)); + SVN_ERR(read_uint64_from_proto_index(proto_index, &entry->item_index, eof, + scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->sub_item, eof, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__l2p_proto_index_add_revision(apr_file_t *proto_index, + apr_pool_t *scratch_pool) +{ + l2p_proto_entry_t entry = { 0 }; + return svn_error_trace(write_l2p_entry_to_proto_index(proto_index, entry, + scratch_pool)); +} + +svn_error_t * +svn_fs_x__l2p_proto_index_add_entry(apr_file_t *proto_index, + apr_off_t offset, + apr_uint32_t sub_item, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + l2p_proto_entry_t entry = { 0 }; + + /* make sure the conversion to uint64 works */ + SVN_ERR_ASSERT(offset >= -1); + + /* we support offset '-1' as a "not used" indication */ + entry.offset = (apr_uint64_t)offset + 1; + + /* make sure we can use item_index as an array index when building the + * final index file */ + SVN_ERR_ASSERT(item_index < UINT_MAX / 2); + entry.item_index = item_index; + + /* no limits on the container sub-item index */ + entry.sub_item = sub_item; + + return svn_error_trace(write_l2p_entry_to_proto_index(proto_index, entry, + scratch_pool)); +} + +/* Encode VALUE as 7/8b into P and return the number of bytes written. + * This will be used when _writing_ packed data. packed_stream_* is for + * read operations only. + */ +static apr_size_t +encode_uint(unsigned char *p, apr_uint64_t value) +{ + unsigned char *start = p; + while (value >= 0x80) + { + *p = (unsigned char)((value % 0x80) + 0x80); + value /= 0x80; + ++p; + } + + *p = (unsigned char)(value % 0x80); + return (p - start) + 1; +} + +/* Encode VALUE as 7/8b into P and return the number of bytes written. + * This maps signed ints onto unsigned ones. + */ +static apr_size_t +encode_int(unsigned char *p, apr_int64_t value) +{ + return encode_uint(p, (apr_uint64_t)(value < 0 ? -1 - 2*value : 2*value)); +} + +/* Append VALUE to STREAM in 7/8b encoding. + */ +static svn_error_t * +stream_write_encoded(svn_stream_t *stream, + apr_uint64_t value) +{ + unsigned char encoded[ENCODED_INT_LENGTH]; + + apr_size_t len = encode_uint(encoded, value); + return svn_error_trace(svn_stream_write(stream, (char *)encoded, &len)); +} + +/* Run-length-encode the uint64 numbers in ARRAY starting at index START + * up to but not including END. All numbers must be > 0. + * Return the number of remaining entries in ARRAY after START. + */ +static int +rle_array(apr_array_header_t *array, int start, int end) +{ + int i; + int target = start; + for (i = start; i < end; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(array, i, apr_uint64_t); + assert(value > 0); + + if (value == 1) + { + int counter; + for (counter = 1; i + counter < end; ++counter) + if (APR_ARRAY_IDX(array, i + counter, apr_uint64_t) != 1) + break; + + if (--counter) + { + APR_ARRAY_IDX(array, target, apr_uint64_t) = 0; + APR_ARRAY_IDX(array, target + 1, apr_uint64_t) = counter; + target += 2; + i += counter; + continue; + } + } + + APR_ARRAY_IDX(array, target, apr_uint64_t) = value; + ++target; + } + + return target; +} + +/* Utility data structure describing an log-2-phys page entry. + * This is only used as a transient representation during index creation. + */ +typedef struct l2p_page_entry_t +{ + apr_uint64_t offset; + apr_uint32_t sub_item; +} l2p_page_entry_t; + +/* qsort-compatible compare function taking two l2p_page_entry_t and + * ordering them by offset. + */ +static int +compare_l2p_entries_by_offset(const l2p_page_entry_t *lhs, + const l2p_page_entry_t *rhs) +{ + return lhs->offset > rhs->offset ? 1 + : lhs->offset == rhs->offset ? 0 : -1; +} + +/* Write the log-2-phys index page description for the l2p_page_entry_t + * array ENTRIES, starting with element START up to but not including END. + * Write the resulting representation into BUFFER. Use SCRATCH_POOL for + * temporary allocations. + */ +static svn_error_t * +encode_l2p_page(apr_array_header_t *entries, + int start, + int end, + svn_spillbuf_t *buffer, + apr_pool_t *scratch_pool) +{ + unsigned char encoded[ENCODED_INT_LENGTH]; + apr_hash_t *containers = apr_hash_make(scratch_pool); + int count = end - start; + int container_count = 0; + apr_uint64_t last_offset = 0; + int i; + + apr_size_t data_size = count * sizeof(l2p_page_entry_t); + svn_stringbuf_t *container_offsets + = svn_stringbuf_create_ensure(count * 2, scratch_pool); + + /* SORTED: relevant items from ENTRIES, sorted by offset */ + l2p_page_entry_t *sorted + = apr_pmemdup(scratch_pool, + entries->elts + start * sizeof(l2p_page_entry_t), + data_size); + qsort(sorted, end - start, sizeof(l2p_page_entry_t), + (int (*)(const void *, const void *))compare_l2p_entries_by_offset); + + /* identify container offsets and create container list */ + for (i = 0; i < count; ++i) + { + /* skip "unused" entries */ + if (sorted[i].offset == 0) + continue; + + /* offset already covered? */ + if (i > 0 && sorted[i].offset == sorted[i-1].offset) + continue; + + /* is this a container item + * (appears more than once or accesses to sub-items other than 0)? */ + if ( (i != count-1 && sorted[i].offset == sorted[i+1].offset) + || (sorted[i].sub_item != 0)) + { + svn_stringbuf_appendbytes(container_offsets, (const char *)encoded, + encode_uint(encoded, sorted[i].offset + - last_offset)); + last_offset = sorted[i].offset; + apr_hash_set(containers, + &sorted[i].offset, + sizeof(sorted[i].offset), + (void *)(apr_uintptr_t)++container_count); + } + } + + /* write container list to BUFFER */ + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, container_count), + scratch_pool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)container_offsets->data, + container_offsets->len, scratch_pool)); + + /* encode items */ + for (i = start; i < end; ++i) + { + l2p_page_entry_t *entry = &APR_ARRAY_IDX(entries, i, l2p_page_entry_t); + if (entry->offset == 0) + { + SVN_ERR(svn_spillbuf__write(buffer, "\0", 1, scratch_pool)); + } + else + { + void *void_idx = apr_hash_get(containers, &entry->offset, + sizeof(entry->offset)); + if (void_idx == NULL) + { + apr_uint64_t value = entry->offset + container_count; + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, value), + scratch_pool)); + } + else + { + apr_uintptr_t idx = (apr_uintptr_t)void_idx; + apr_uint64_t value = entry->sub_item; + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, idx), + scratch_pool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, value), + scratch_pool)); + } + } + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t * result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_file_t *proto_index = NULL; + svn_stream_t *stream; + int i; + int end; + apr_uint64_t entry; + svn_boolean_t eof = FALSE; + + int last_page_count = 0; /* total page count at the start of + the current revision */ + + /* temporary data structures that collect the data which will be moved + to the target file in a second step */ + apr_pool_t *local_pool = svn_pool_create(scratch_pool); + apr_pool_t *iterpool = svn_pool_create(local_pool); + apr_array_header_t *page_counts + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + apr_array_header_t *page_sizes + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + apr_array_header_t *entry_counts + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + + /* collect the item offsets and sub-item value for the current revision */ + apr_array_header_t *entries + = apr_array_make(local_pool, 256, sizeof(l2p_page_entry_t)); + + /* 64k blocks, spill after 16MB */ + svn_spillbuf_t *buffer + = svn_spillbuf__create(0x10000, 0x1000000, local_pool); + + /* Paranoia check that makes later casting to int32 safe. + * The current implementation is limited to 2G entries per page. */ + if (ffd->l2p_page_size > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("L2P index page size %s" + " exceeds current limit of 2G entries"), + apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, + ffd->l2p_page_size)); + + /* start at the beginning of the source file */ + SVN_ERR(svn_io_file_open(&proto_index, proto_file_name, + APR_READ | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, local_pool)); + + /* process all entries until we fail due to EOF */ + for (entry = 0; !eof; ++entry) + { + l2p_proto_entry_t proto_entry; + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_l2p_entry_from_proto_index(proto_index, &proto_entry, + &eof, local_pool)); + + /* handle new revision */ + if ((entry > 0 && proto_entry.offset == 0) || eof) + { + /* dump entries, grouped into pages */ + + int entry_count = 0; + for (i = 0; i < entries->nelts; i += entry_count) + { + /* 1 page with up to L2P_PAGE_SIZE entries. + * fsfs.conf settings validation guarantees this to fit into + * our address space. */ + apr_size_t last_buffer_size + = (apr_size_t)svn_spillbuf__get_size(buffer); + + svn_pool_clear(iterpool); + + entry_count = ffd->l2p_page_size < entries->nelts - i + ? (int)ffd->l2p_page_size + : entries->nelts - i; + SVN_ERR(encode_l2p_page(entries, i, i + entry_count, + buffer, iterpool)); + + APR_ARRAY_PUSH(entry_counts, apr_uint64_t) = entry_count; + APR_ARRAY_PUSH(page_sizes, apr_uint64_t) + = svn_spillbuf__get_size(buffer) - last_buffer_size; + } + + apr_array_clear(entries); + + /* store the number of pages in this revision */ + APR_ARRAY_PUSH(page_counts, apr_uint64_t) + = page_sizes->nelts - last_page_count; + + last_page_count = page_sizes->nelts; + } + else + { + int idx; + + /* store the mapping in our array */ + l2p_page_entry_t page_entry = { 0 }; + + if (proto_entry.item_index > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s too large " + "in l2p proto index for revision %ld"), + apr_psprintf(local_pool, + "%" APR_UINT64_T_FMT, + proto_entry.item_index), + revision + page_counts->nelts); + + idx = (int)proto_entry.item_index; + while (idx >= entries->nelts) + APR_ARRAY_PUSH(entries, l2p_page_entry_t) = page_entry; + + page_entry.offset = proto_entry.offset; + page_entry.sub_item = proto_entry.sub_item; + APR_ARRAY_IDX(entries, idx, l2p_page_entry_t) = page_entry; + } + } + + /* we are now done with the source file */ + SVN_ERR(svn_io_file_close(proto_index, local_pool)); + + /* Paranoia check that makes later casting to int32 safe. + * The current implementation is limited to 2G pages per index. */ + if (page_counts->nelts > APR_INT32_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("L2P index page count %d" + " exceeds current limit of 2G pages"), + page_counts->nelts); + + /* open target stream. */ + stream = svn_stream_checksummed2(svn_stream_from_aprfile2(index_file, TRUE, + local_pool), + NULL, checksum, svn_checksum_md5, FALSE, + result_pool); + + + /* write header info */ + SVN_ERR(svn_stream_puts(stream, L2P_STREAM_PREFIX)); + SVN_ERR(stream_write_encoded(stream, revision)); + SVN_ERR(stream_write_encoded(stream, page_counts->nelts)); + SVN_ERR(stream_write_encoded(stream, ffd->l2p_page_size)); + SVN_ERR(stream_write_encoded(stream, page_sizes->nelts)); + + /* write the revision table */ + end = rle_array(page_counts, 0, page_counts->nelts); + for (i = 0; i < end; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(page_counts, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* write the page table */ + for (i = 0; i < page_sizes->nelts; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(page_sizes, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + value = APR_ARRAY_IDX(entry_counts, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* append page contents and implicitly close STREAM */ + SVN_ERR(svn_stream_copy3(svn_stream__from_spillbuf(buffer, local_pool), + stream, NULL, NULL, local_pool)); + + svn_pool_destroy(local_pool); + + return SVN_NO_ERROR; +} + +/* Return the base revision used to identify the p2l or lp2 index covering + * REVISION in FS. + */ +static svn_revnum_t +base_revision(svn_fs_t *fs, svn_revnum_t revision) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + return svn_fs_x__is_packed_rev(fs, revision) + ? revision - (revision % ffd->max_files_per_dir) + : revision; +} + +/* Data structure that describes which l2p page info shall be extracted + * from the cache and contains the fields that receive the result. + */ +typedef struct l2p_page_info_baton_t +{ + /* input data: we want the page covering (REVISION,ITEM_INDEX) */ + svn_revnum_t revision; + apr_uint64_t item_index; + + /* out data */ + /* page location and size of the page within the l2p index file */ + l2p_page_table_entry_t entry; + + /* page number within the pages for REVISION (not l2p index global!) */ + apr_uint32_t page_no; + + /* offset of ITEM_INDEX within that page */ + apr_uint32_t page_offset; + + /* revision identifying the l2p index file, also the first rev in that */ + svn_revnum_t first_revision; +} l2p_page_info_baton_t; + + +/* Utility function that copies the info requested by BATON->REVISION and + * BATON->ITEM_INDEX and from HEADER and PAGE_TABLE into the output fields + * of *BATON. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_header_copy(l2p_page_info_baton_t *baton, + const l2p_header_t *header, + const l2p_page_table_entry_t *page_table, + const apr_size_t *page_table_index, + apr_pool_t *scratch_pool) +{ + /* revision offset within the index file */ + apr_size_t rel_revision = baton->revision - header->first_revision; + if (rel_revision >= header->revision_count) + return svn_error_createf(SVN_ERR_FS_INDEX_REVISION , NULL, + _("Revision %ld not covered by item index"), + baton->revision); + + /* select the relevant page */ + if (baton->item_index < header->page_size) + { + /* most revs fit well into a single page */ + baton->page_offset = (apr_uint32_t)baton->item_index; + baton->page_no = 0; + baton->entry = page_table[page_table_index[rel_revision]]; + } + else + { + const l2p_page_table_entry_t *first_entry; + const l2p_page_table_entry_t *last_entry; + apr_uint64_t max_item_index; + + /* range of pages for this rev */ + first_entry = page_table + page_table_index[rel_revision]; + last_entry = page_table + page_table_index[rel_revision + 1]; + + /* do we hit a valid index page? */ + max_item_index = (apr_uint64_t)header->page_size + * (last_entry - first_entry); + if (baton->item_index >= max_item_index) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s exceeds l2p limit " + "of %s for revision %ld"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + baton->item_index), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + max_item_index), + baton->revision); + + /* all pages are of the same size and full, except for the last one */ + baton->page_offset = (apr_uint32_t)(baton->item_index % header->page_size); + baton->page_no = (apr_uint32_t)(baton->item_index / header->page_size); + baton->entry = first_entry[baton->page_no]; + } + + baton->first_revision = header->first_revision; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_page_info_baton_t *BATON from l2p_header_t *DATA into the output + * fields in *BATON. + */ +static svn_error_t * +l2p_header_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve all pointer values of in-cache data */ + const l2p_header_t *header = data; + const l2p_page_table_entry_t *page_table + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table); + const apr_size_t *page_table_index + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table_index); + + /* copy the info */ + return l2p_header_copy(baton, header, page_table, page_table_index, + result_pool); +} + +/* Read COUNT run-length-encoded (see rle_array) uint64 from STREAM and + * return them in VALUES. + */ +static svn_error_t * +expand_rle(apr_array_header_t *values, + svn_fs_x__packed_number_stream_t *stream, + apr_size_t count) +{ + apr_array_clear(values); + + while (count) + { + apr_uint64_t value; + SVN_ERR(packed_stream_get(&value, stream)); + + if (value) + { + APR_ARRAY_PUSH(values, apr_uint64_t) = value; + --count; + } + else + { + apr_uint64_t i; + apr_uint64_t repetitions; + SVN_ERR(packed_stream_get(&repetitions, stream)); + if (++repetitions > count) + repetitions = count; + + for (i = 0; i < repetitions; ++i) + APR_ARRAY_PUSH(values, apr_uint64_t) = 1; + + count -= repetitions; + } + } + + return SVN_NO_ERROR; +} + +/* If REV_FILE->L2P_STREAM is NULL, create a new stream for the log-to-phys + * index for REVISION in FS and return it in REV_FILE. + */ +static svn_error_t * +auto_open_l2p_index(svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision) +{ + if (rev_file->l2p_stream == NULL) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); + SVN_ERR(packed_stream_open(&rev_file->l2p_stream, + rev_file->file, + rev_file->l2p_offset, + rev_file->p2l_offset, + L2P_STREAM_PREFIX, + (apr_size_t)ffd->block_size, + rev_file->pool, + rev_file->pool)); + } + + return SVN_NO_ERROR; +} + +/* Read the header data structure of the log-to-phys index for REVISION + * in FS and return it in *HEADER, allocated in RESULT_POOL. Use REV_FILE + * to access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_header_body(l2p_header_t **header, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_uint64_t value; + apr_size_t i; + apr_size_t page, page_count; + apr_off_t offset; + l2p_header_t *result = apr_pcalloc(result_pool, sizeof(*result)); + apr_size_t page_table_index; + svn_revnum_t next_rev; + apr_array_header_t *expanded_values + = apr_array_make(scratch_pool, 16, sizeof(apr_uint64_t)); + + svn_fs_x__pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(auto_open_l2p_index(rev_file, fs, revision)); + packed_stream_seek(rev_file->l2p_stream, 0); + + /* Read the table sizes. Check the data for plausibility and + * consistency with other bits. */ + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->first_revision = (svn_revnum_t)value; + if (result->first_revision != rev_file->start_revision) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index rev / pack file revision numbers do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->revision_count = (int)value; + if ( result->revision_count != 1 + && result->revision_count != (apr_uint64_t)ffd->max_files_per_dir) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid number of revisions in L2P index")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->page_size = (apr_uint32_t)value; + if (!result->page_size || (result->page_size & (result->page_size - 1))) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P index page size is not a power of two")); + + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + page_count = (apr_size_t)value; + if (page_count < result->revision_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Fewer L2P index pages than revisions")); + if (page_count > (rev_file->p2l_offset - rev_file->l2p_offset) / 2) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P index page count implausibly large")); + + next_rev = result->first_revision + (svn_revnum_t)result->revision_count; + if (result->first_revision > revision || next_rev <= revision) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Corrupt L2P index for r%ld only covers r%ld:%ld"), + revision, result->first_revision, next_rev); + + /* allocate the page tables */ + result->page_table + = apr_pcalloc(result_pool, page_count * sizeof(*result->page_table)); + result->page_table_index + = apr_pcalloc(result_pool, (result->revision_count + 1) + * sizeof(*result->page_table_index)); + + /* read per-revision page table sizes (i.e. number of pages per rev) */ + page_table_index = 0; + result->page_table_index[0] = page_table_index; + SVN_ERR(expand_rle(expanded_values, rev_file->l2p_stream, + result->revision_count)); + for (i = 0; i < result->revision_count; ++i) + { + value = (apr_size_t)APR_ARRAY_IDX(expanded_values, i, apr_uint64_t); + if (value == 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Revision with no L2P index pages")); + + page_table_index += (apr_size_t)value; + if (page_table_index > page_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P page table exceeded")); + + result->page_table_index[i+1] = page_table_index; + } + + if (page_table_index != page_count) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Revisions do not cover the full L2P index page table")); + + /* read actual page tables */ + for (page = 0; page < page_count; ++page) + { + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value == 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Empty L2P index page")); + + result->page_table[page].size = (apr_uint32_t)value; + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value > result->page_size) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Page exceeds L2P index page size")); + + result->page_table[page].entry_count = (apr_uint32_t)value; + } + + /* correct the page description offsets */ + offset = packed_stream_offset(rev_file->l2p_stream); + for (page = 0; page < page_count; ++page) + { + result->page_table[page].offset = offset; + offset += result->page_table[page].size; + } + + /* return and cache the header */ + *header = result; + SVN_ERR(svn_cache__set(ffd->l2p_header_cache, &key, result, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Get the page info requested in *BATON from FS and set the output fields + * in *BATON. + * To maximize efficiency, use or return the data stream in *STREAM. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_page_info(l2p_page_info_baton_t *baton, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + l2p_header_t *result; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* try to find the info in the cache */ + svn_fs_x__pair_cache_key_t key; + key.revision = base_revision(fs, baton->revision); + key.second = svn_fs_x__is_packed_rev(fs, baton->revision); + SVN_ERR(svn_cache__get_partial((void**)&dummy, &is_cached, + ffd->l2p_header_cache, &key, + l2p_header_access_func, baton, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* read from disk, cache and copy the result */ + SVN_ERR(get_l2p_header_body(&result, rev_file, fs, baton->revision, + scratch_pool, scratch_pool)); + SVN_ERR(l2p_header_copy(baton, result, result->page_table, + result->page_table_index, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the log-to-phys header info of the index covering REVISION from FS + * and return it in *HEADER. REV_FILE provides the pack / rev status. + * Allocate *HEADER in RESULT_POOL, use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +get_l2p_header(l2p_header_t **header, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + + /* first, try cache lookop */ + svn_fs_x__pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->l2p_header_cache, + &key, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* read from disk and cache the result */ + SVN_ERR(get_l2p_header_body(header, rev_file, fs, revision, result_pool, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* From the log-to-phys index file starting at START_REVISION in FS, read + * the mapping page identified by TABLE_ENTRY and return it in *PAGE. + * Use REV_FILE to access on-disk files. + * Use RESULT_POOL for allocations. + */ +static svn_error_t * +get_l2p_page(l2p_page_t **page, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t start_revision, + l2p_page_table_entry_t *table_entry, + apr_pool_t *result_pool) +{ + apr_uint64_t value, last_value = 0; + apr_uint32_t i; + l2p_page_t *result = apr_pcalloc(result_pool, sizeof(*result)); + apr_uint64_t container_count; + apr_off_t *container_offsets; + + /* open index file and select page */ + SVN_ERR(auto_open_l2p_index(rev_file, fs, start_revision)); + packed_stream_seek(rev_file->l2p_stream, table_entry->offset); + + /* initialize the page content */ + result->entry_count = table_entry->entry_count; + result->offsets = apr_pcalloc(result_pool, result->entry_count + * sizeof(*result->offsets)); + result->sub_items = apr_pcalloc(result_pool, result->entry_count + * sizeof(*result->sub_items)); + + /* container offsets array */ + + SVN_ERR(packed_stream_get(&container_count, rev_file->l2p_stream)); + container_offsets = apr_pcalloc(result_pool, + container_count * sizeof(*result)); + for (i = 0; i < container_count; ++i) + { + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + last_value += value; + container_offsets[i] = (apr_off_t)last_value - 1; + /* '-1' is represented as '0' in the index file */ + } + + /* read all page entries (offsets in rev file and container sub-items) */ + for (i = 0; i < result->entry_count; ++i) + { + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + if (value == 0) + { + result->offsets[i] = -1; + result->sub_items[i] = 0; + } + else if (value <= container_count) + { + result->offsets[i] = container_offsets[value - 1]; + SVN_ERR(packed_stream_get(&value, rev_file->l2p_stream)); + result->sub_items[i] = (apr_uint32_t)value; + } + else + { + result->offsets[i] = (apr_off_t)(value - 1 - container_count); + result->sub_items[i] = 0; + } + } + + /* After reading all page entries, the read cursor must have moved by + * TABLE_ENTRY->SIZE bytes. */ + if ( packed_stream_offset(rev_file->l2p_stream) + != table_entry->offset + table_entry->size) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("L2P actual page size does not match page table value.")); + + *page = result; + + return SVN_NO_ERROR; +} + +/* Request data structure for l2p_page_access_func. + */ +typedef struct l2p_page_baton_t +{ + /* in data */ + /* revision. Used for error messages only */ + svn_revnum_t revision; + + /* item index to look up. Used for error messages only */ + apr_uint64_t item_index; + + /* offset within the cached page */ + apr_uint32_t page_offset; + + /* out data */ + /* absolute item or container offset in rev / pack file */ + apr_off_t offset; + + /* 0 -> container / item itself; sub-item in container otherwise */ + apr_uint32_t sub_item; + +} l2p_page_baton_t; + +/* Return the rev / pack file offset of the item at BATON->PAGE_OFFSET in + * OFFSETS of PAGE and write it to *OFFSET. + * Allocate temporaries in SCRATCH_POOL. + */ +static svn_error_t * +l2p_page_get_offset(l2p_page_baton_t *baton, + const l2p_page_t *page, + const apr_off_t *offsets, + const apr_uint32_t *sub_items, + apr_pool_t *scratch_pool) +{ + /* overflow check */ + if (page->entry_count <= baton->page_offset) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Item index %s too large in" + " revision %ld"), + apr_psprintf(scratch_pool, "%" APR_UINT64_T_FMT, + baton->item_index), + baton->revision); + + /* return the result */ + baton->offset = offsets[baton->page_offset]; + baton->sub_item = sub_items[baton->page_offset]; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_page_baton_t *BATON from l2p_page_t *DATA into apr_off_t *OUT. + */ +static svn_error_t * +l2p_page_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve all in-cache pointers */ + const l2p_page_t *page = data; + const apr_off_t *offsets + = svn_temp_deserializer__ptr(page, (const void *const *)&page->offsets); + const apr_uint32_t *sub_items + = svn_temp_deserializer__ptr(page, (const void *const *)&page->sub_items); + + /* return the requested data */ + return l2p_page_get_offset(baton, page, offsets, sub_items, result_pool); +} + +/* Data request structure used by l2p_page_table_access_func. + */ +typedef struct l2p_page_table_baton_t +{ + /* revision for which to read the page table */ + svn_revnum_t revision; + + /* page table entries (of type l2p_page_table_entry_t). + * Must be created by caller and will be filled by callee. */ + apr_array_header_t *pages; +} l2p_page_table_baton_t; + +/* Implement svn_cache__partial_getter_func_t: copy the data requested in + * l2p_page_baton_t *BATON from l2p_page_t *DATA into BATON->PAGES and *OUT. + */ +static svn_error_t * +l2p_page_table_access_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* resolve in-cache pointers */ + l2p_page_table_baton_t *table_baton = baton; + const l2p_header_t *header = (const l2p_header_t *)data; + const l2p_page_table_entry_t *page_table + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table); + const apr_size_t *page_table_index + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->page_table_index); + + /* copy the revision's page table into BATON */ + apr_size_t rel_revision = table_baton->revision - header->first_revision; + if (rel_revision < header->revision_count) + { + const l2p_page_table_entry_t *entry + = page_table + page_table_index[rel_revision]; + const l2p_page_table_entry_t *last_entry + = page_table + page_table_index[rel_revision + 1]; + + for (; entry < last_entry; ++entry) + APR_ARRAY_PUSH(table_baton->pages, l2p_page_table_entry_t) + = *entry; + } + + /* set output as a courtesy to the caller */ + *out = table_baton->pages; + + return SVN_NO_ERROR; +} + +/* Read the l2p index page table for REVISION in FS from cache and return + * it in PAGES. The later must be provided by the caller (and can be + * re-used); existing entries will be removed before writing the result. + * If the data cannot be found in the cache, the result will be empty + * (it never can be empty for a valid REVISION if the data is cached). + * Use the info from REV_FILE to determine pack / rev file properties. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_l2p_page_table(apr_array_header_t *pages, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_boolean_t is_cached = FALSE; + l2p_page_table_baton_t baton; + + svn_fs_x__pair_cache_key_t key; + key.revision = base_revision(fs, revision); + key.second = svn_fs_x__is_packed_rev(fs, revision); + + apr_array_clear(pages); + baton.revision = revision; + baton.pages = pages; + SVN_ERR(svn_cache__get_partial((void**)&pages, &is_cached, + ffd->l2p_header_cache, &key, + l2p_page_table_access_func, &baton, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Utility function. Read the l2p index pages for REVISION in FS from + * STREAM and put them into the cache. Skip page number EXLCUDED_PAGE_NO + * (use -1 for 'skip none') and pages outside the MIN_OFFSET, MAX_OFFSET + * range in the l2p index file. The index is being identified by + * FIRST_REVISION. PAGES is a scratch container provided by the caller. + * SCRATCH_POOL is used for temporary allocations. + * + * This function may be a no-op if the header cache lookup fails / misses. + */ +static svn_error_t * +prefetch_l2p_pages(svn_boolean_t *end, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t first_revision, + svn_revnum_t revision, + apr_array_header_t *pages, + int exlcuded_page_no, + apr_off_t min_offset, + apr_off_t max_offset, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + int i; + apr_pool_t *iterpool; + svn_fs_x__page_cache_key_t key = { 0 }; + + /* Parameter check. */ + if (min_offset < 0) + min_offset = 0; + + if (max_offset <= 0) + { + /* Nothing to do */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* get the page table for REVISION from cache */ + *end = FALSE; + SVN_ERR(get_l2p_page_table(pages, fs, revision, scratch_pool)); + if (pages->nelts == 0) + { + /* not found -> we can't continue without hitting the disk again */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* prefetch pages individually until all are done or we found one in + * the cache */ + iterpool = svn_pool_create(scratch_pool); + assert(revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)revision; + key.is_packed = svn_fs_x__is_packed_rev(fs, revision); + + for (i = 0; i < pages->nelts && !*end; ++i) + { + svn_boolean_t is_cached; + + l2p_page_table_entry_t *entry + = &APR_ARRAY_IDX(pages, i, l2p_page_table_entry_t); + svn_pool_clear(iterpool); + + if (i == exlcuded_page_no) + continue; + + /* skip pages outside the specified index file range */ + if ( entry->offset < (apr_uint64_t)min_offset + || entry->offset + entry->size > (apr_uint64_t)max_offset) + { + *end = TRUE; + continue; + } + + /* page already in cache? */ + key.page = i; + SVN_ERR(svn_cache__has_key(&is_cached, ffd->l2p_page_cache, + &key, iterpool)); + if (!is_cached) + { + /* no in cache -> read from stream (data already buffered in APR) + * and cache the result */ + l2p_page_t *page = NULL; + SVN_ERR(get_l2p_page(&page, rev_file, fs, first_revision, + entry, iterpool)); + + SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page, + iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Using the log-to-phys indexes in FS, find the absolute offset in the + * rev file for (REVISION, ITEM_INDEX) and return it in *OFFSET. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_index_lookup(apr_off_t *offset, + apr_uint32_t *sub_item, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + l2p_page_info_baton_t info_baton; + l2p_page_baton_t page_baton; + l2p_page_t *page = NULL; + svn_fs_x__page_cache_key_t key = { 0 }; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* read index master data structure and extract the info required to + * access the l2p index page for (REVISION,ITEM_INDEX)*/ + info_baton.revision = revision; + info_baton.item_index = item_index; + SVN_ERR(get_l2p_page_info(&info_baton, rev_file, fs, scratch_pool)); + + /* try to find the page in the cache and get the OFFSET from it */ + page_baton.revision = revision; + page_baton.item_index = item_index; + page_baton.page_offset = info_baton.page_offset; + + assert(revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)revision; + key.is_packed = svn_fs_x__is_packed_rev(fs, revision); + key.page = info_baton.page_no; + + SVN_ERR(svn_cache__get_partial(&dummy, &is_cached, + ffd->l2p_page_cache, &key, + l2p_page_access_func, &page_baton, + scratch_pool)); + + if (!is_cached) + { + /* we need to read the info from disk (might already be in the + * APR file buffer, though) */ + apr_array_header_t *pages; + svn_revnum_t prefetch_revision; + svn_revnum_t last_revision + = info_baton.first_revision + + svn_fs_x__pack_size(fs, info_baton.first_revision); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_boolean_t end; + apr_off_t max_offset + = APR_ALIGN(info_baton.entry.offset + info_baton.entry.size, + ffd->block_size); + apr_off_t min_offset = max_offset - ffd->block_size; + + /* read the relevant page */ + SVN_ERR(get_l2p_page(&page, rev_file, fs, info_baton.first_revision, + &info_baton.entry, scratch_pool)); + + /* cache the page and extract the result we need */ + SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page, scratch_pool)); + SVN_ERR(l2p_page_get_offset(&page_baton, page, page->offsets, + page->sub_items, scratch_pool)); + + /* prefetch pages from following and preceding revisions */ + pages = apr_array_make(scratch_pool, 16, + sizeof(l2p_page_table_entry_t)); + end = FALSE; + for (prefetch_revision = revision; + prefetch_revision < last_revision && !end; + ++prefetch_revision) + { + int excluded_page_no = prefetch_revision == revision + ? info_baton.page_no + : -1; + svn_pool_clear(iterpool); + + SVN_ERR(prefetch_l2p_pages(&end, fs, rev_file, + info_baton.first_revision, + prefetch_revision, pages, + excluded_page_no, min_offset, + max_offset, iterpool)); + } + + end = FALSE; + for (prefetch_revision = revision-1; + prefetch_revision >= info_baton.first_revision && !end; + --prefetch_revision) + { + svn_pool_clear(iterpool); + + SVN_ERR(prefetch_l2p_pages(&end, fs, rev_file, + info_baton.first_revision, + prefetch_revision, pages, -1, + min_offset, max_offset, iterpool)); + } + + svn_pool_destroy(iterpool); + } + + *offset = page_baton.offset; + *sub_item = page_baton.sub_item; + + return SVN_NO_ERROR; +} + +/* Using the log-to-phys proto index in transaction TXN_ID in FS, find the + * absolute offset in the proto rev file for the given ITEM_INDEX and return + * it in *OFFSET. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +l2p_proto_index_lookup(apr_off_t *offset, + apr_uint32_t *sub_item, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + svn_boolean_t eof = FALSE; + apr_file_t *file = NULL; + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_l2p_proto_index(fs, txn_id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + /* process all entries until we fail due to EOF */ + *offset = -1; + while (!eof) + { + l2p_proto_entry_t entry; + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_l2p_entry_from_proto_index(file, &entry, &eof, + scratch_pool)); + + /* handle new revision */ + if (!eof && entry.item_index == item_index) + { + *offset = (apr_off_t)entry.offset - 1; + *sub_item = entry.sub_item; + break; + } + } + + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__l2p_get_max_ids(apr_array_header_t **max_ids, + svn_fs_t *fs, + svn_revnum_t start_rev, + apr_size_t count, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + l2p_header_t *header = NULL; + svn_revnum_t revision; + svn_revnum_t last_rev = (svn_revnum_t)(start_rev + count); + svn_fs_x__revision_file_t *rev_file; + apr_pool_t *header_pool = svn_pool_create(scratch_pool); + + /* read index master data structure for the index covering START_REV */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start_rev, + header_pool, header_pool)); + SVN_ERR(get_l2p_header(&header, rev_file, fs, start_rev, header_pool, + header_pool)); + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + + /* Determine the length of the item index list for each rev. + * Read new index headers as required. */ + *max_ids = apr_array_make(result_pool, (int)count, sizeof(apr_uint64_t)); + for (revision = start_rev; revision < last_rev; ++revision) + { + apr_uint64_t full_page_count; + apr_uint64_t item_count; + apr_size_t first_page_index, last_page_index; + + if (revision >= header->first_revision + header->revision_count) + { + /* need to read the next index. Clear up memory used for the + * previous one. Note that intermittent pack runs do not change + * the number of items in a revision, i.e. there is no consistency + * issue here. */ + svn_pool_clear(header_pool); + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision, + header_pool, header_pool)); + SVN_ERR(get_l2p_header(&header, rev_file, fs, revision, + header_pool, header_pool)); + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + } + + /* in a revision with N index pages, the first N-1 index pages are + * "full", i.e. contain HEADER->PAGE_SIZE entries */ + first_page_index + = header->page_table_index[revision - header->first_revision]; + last_page_index + = header->page_table_index[revision - header->first_revision + 1]; + full_page_count = last_page_index - first_page_index - 1; + item_count = full_page_count * header->page_size + + header->page_table[last_page_index - 1].entry_count; + + APR_ARRAY_PUSH(*max_ids, apr_uint64_t) = item_count; + } + + svn_pool_destroy(header_pool); + return SVN_NO_ERROR; +} + +/* + * phys-to-log index + */ +svn_fs_x__p2l_entry_t * +svn_fs_x__p2l_entry_dup(const svn_fs_x__p2l_entry_t *entry, + apr_pool_t *result_pool) +{ + svn_fs_x__p2l_entry_t *new_entry = apr_pmemdup(result_pool, entry, + sizeof(*new_entry)); + if (new_entry->item_count) + new_entry->items = apr_pmemdup(result_pool, + entry->items, + entry->item_count * sizeof(*entry->items)); + + return new_entry; +} + +/* + * phys-to-log index + */ +svn_error_t * +svn_fs_x__p2l_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool) +{ + SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE + | APR_CREATE | APR_APPEND | APR_BUFFERED, + APR_OS_DEFAULT, result_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__p2l_proto_index_add_entry(apr_file_t *proto_index, + const svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + apr_uint64_t revision; + apr_int32_t i; + + /* Make sure all signed elements of ENTRY have non-negative values. + * + * For file offsets and sizes, this is a given as we use them to describe + * absolute positions and sizes. For revisions, SVN_INVALID_REVNUM is + * valid, hence we have to shift it by 1. + */ + SVN_ERR_ASSERT(entry->offset >= 0); + SVN_ERR_ASSERT(entry->size >= 0); + + /* Now, all values will nicely convert to uint64. */ + /* Make sure to keep P2L_PROTO_INDEX_ENTRY_SIZE consistent with this: */ + + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->offset, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->size, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->type, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->fnv1_checksum, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, entry->item_count, + scratch_pool)); + + /* Add sub-items. */ + for (i = 0; i < entry->item_count; ++i) + { + const svn_fs_x__id_t *sub_item = &entry->items[i]; + + /* Make sure all signed elements of ENTRY have non-negative values. + * + * For file offsets and sizes, this is a given as we use them to + * describe absolute positions and sizes. For revisions, + * SVN_INVALID_REVNUM is valid, hence we have to shift it by 1. + */ + SVN_ERR_ASSERT( sub_item->change_set >= 0 + || sub_item->change_set == SVN_INVALID_REVNUM); + + /* Write sub- record. */ + revision = sub_item->change_set == SVN_INVALID_REVNUM + ? 0 + : ((apr_uint64_t)sub_item->change_set + 1); + + SVN_ERR(write_uint64_to_proto_index(proto_index, revision, + scratch_pool)); + SVN_ERR(write_uint64_to_proto_index(proto_index, sub_item->number, + scratch_pool)); + } + + /* Add trailer: rev / pack file offset of the next item */ + SVN_ERR(write_uint64_to_proto_index(proto_index, + entry->offset + entry->size, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read *ENTRY from log-to-phys PROTO_INDEX file and indicate end-of-file + * in *EOF, or error out in that case if EOF is NULL. *ENTRY is in an + * undefined state if an end-of-file occurred. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +read_p2l_entry_from_proto_index(apr_file_t *proto_index, + svn_fs_x__p2l_entry_t *entry, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + SVN_ERR(read_off_t_from_proto_index(proto_index, &entry->offset, + eof, scratch_pool)); + SVN_ERR(read_off_t_from_proto_index(proto_index, &entry->size, + eof, scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->type, + eof, scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->fnv1_checksum, + eof, scratch_pool)); + SVN_ERR(read_uint32_from_proto_index(proto_index, &entry->item_count, + eof, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +read_p2l_sub_items_from_proto_index(apr_file_t *proto_index, + svn_fs_x__p2l_entry_t *entry, + svn_boolean_t *eof, + apr_pool_t *scratch_pool) +{ + apr_int32_t i; + for (i = 0; i < entry->item_count; ++i) + { + apr_uint64_t revision; + svn_fs_x__id_t *sub_item = &entry->items[i]; + + SVN_ERR(read_uint64_from_proto_index(proto_index, &revision, + eof, scratch_pool)); + SVN_ERR(read_uint64_from_proto_index(proto_index, &sub_item->number, + eof, scratch_pool)); + + /* Do the inverse REVSION number conversion (see + * svn_fs_x__p2l_proto_index_add_entry), if we actually read a + * complete record. + */ + if (!eof || !*eof) + { + /* Be careful with the arithmetics here (overflows and wrap-around): + */ + if (revision > 0 && revision - 1 > LONG_MAX) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW, NULL, + _("Revision 0x%s too large, max = 0x%s"), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + revision), + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_FMT, + (apr_uint64_t)LONG_MAX)); + + /* Shortening conversion from unsigned to signed int is well- + * defined and not lossy in C because the value can be represented + * in the target type. Also, cast to 'long' instead of + * 'svn_revnum_t' here to provoke a compiler warning if those + * types should differ and we would need to change the overflow + * checking logic. + */ + sub_item->change_set = revision == 0 + ? SVN_INVALID_REVNUM + : (long)(revision - 1); + } + + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_proto_index_next_offset(apr_off_t *next_offset, + apr_file_t *proto_index, + apr_pool_t *scratch_pool) +{ + apr_off_t offset = 0; + + /* Empty index file? */ + SVN_ERR(svn_io_file_seek(proto_index, APR_END, &offset, scratch_pool)); + if (offset == 0) + { + *next_offset = 0; + } + else + { + /* The last 64 bits contain the value we are looking for. */ + offset -= sizeof(apr_uint64_t); + SVN_ERR(svn_io_file_seek(proto_index, APR_SET, &offset, scratch_pool)); + SVN_ERR(read_off_t_from_proto_index(proto_index, next_offset, NULL, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_uint64_t page_size = ffd->p2l_page_size; + apr_file_t *proto_index = NULL; + svn_stream_t *stream; + int i; + apr_uint32_t sub_item; + svn_boolean_t eof = FALSE; + unsigned char encoded[ENCODED_INT_LENGTH]; + + apr_uint64_t last_entry_end = 0; + apr_uint64_t last_page_end = 0; + apr_size_t last_buffer_size = 0; /* byte offset in the spill buffer at + the begin of the current revision */ + apr_uint64_t file_size = 0; + + /* temporary data structures that collect the data which will be moved + to the target file in a second step */ + apr_pool_t *local_pool = svn_pool_create(scratch_pool); + apr_array_header_t *table_sizes + = apr_array_make(local_pool, 16, sizeof(apr_uint64_t)); + + /* 64k blocks, spill after 16MB */ + svn_spillbuf_t *buffer + = svn_spillbuf__create(0x10000, 0x1000000, local_pool); + + /* for loop temps ... */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* start at the beginning of the source file */ + SVN_ERR(svn_io_file_open(&proto_index, proto_file_name, + APR_READ | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, local_pool)); + + /* process all entries until we fail due to EOF */ + while (!eof) + { + svn_fs_x__p2l_entry_t entry; + apr_uint64_t entry_end; + svn_boolean_t new_page = svn_spillbuf__get_size(buffer) == 0; + svn_revnum_t last_revision = revision; + apr_uint64_t last_number = 0; + + svn_pool_clear(iterpool); + + /* (attempt to) read the next entry from the source */ + SVN_ERR(read_p2l_entry_from_proto_index(proto_index, &entry, + &eof, iterpool)); + + if (entry.item_count && !eof) + { + entry.items = apr_palloc(iterpool, + entry.item_count * sizeof(*entry.items)); + SVN_ERR(read_p2l_sub_items_from_proto_index(proto_index, &entry, + &eof, iterpool)); + } + + /* Read entry trailer. However, we won't need its content. */ + if (!eof) + { + apr_uint64_t trailer; + SVN_ERR(read_uint64_from_proto_index(proto_index, &trailer, &eof, + scratch_pool)); + } + + /* "unused" (and usually non-existent) section to cover the offsets + at the end the of the last page. */ + if (eof) + { + file_size = last_entry_end; + + entry.offset = last_entry_end; + entry.size = APR_ALIGN(entry.offset, page_size) - entry.offset; + entry.type = SVN_FS_X__ITEM_TYPE_UNUSED; + entry.fnv1_checksum = 0; + entry.item_count = 0; + entry.items = NULL; + } + + for (sub_item = 0; sub_item < entry.item_count; ++sub_item) + if (entry.items[sub_item].change_set == SVN_FS_X__INVALID_CHANGE_SET) + entry.items[sub_item].change_set + = svn_fs_x__change_set_by_rev(revision); + + /* end pages if entry is extending beyond their boundaries */ + entry_end = entry.offset + entry.size; + while (entry_end - last_page_end > page_size) + { + apr_uint64_t buffer_size = svn_spillbuf__get_size(buffer); + APR_ARRAY_PUSH(table_sizes, apr_uint64_t) + = buffer_size - last_buffer_size; + + last_buffer_size = buffer_size; + last_page_end += page_size; + new_page = TRUE; + } + + /* this entry starts a new table -> store its offset + (all following entries in the same table will store sizes only) */ + if (new_page) + { + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.offset), + iterpool)); + last_revision = revision; + } + + /* write simple item / container entry */ + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.size), + iterpool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.type + entry.item_count * 16), + iterpool)); + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_uint(encoded, entry.fnv1_checksum), + iterpool)); + + /* container contents (only one for non-container items) */ + for (sub_item = 0; sub_item < entry.item_count; ++sub_item) + { + svn_revnum_t item_rev + = svn_fs_x__get_revnum(entry.items[sub_item].change_set); + apr_int64_t diff = item_rev - last_revision; + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_int(encoded, diff), + iterpool)); + last_revision = item_rev; + } + + for (sub_item = 0; sub_item < entry.item_count; ++sub_item) + { + apr_int64_t diff = entry.items[sub_item].number - last_number; + SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, + encode_int(encoded, diff), + iterpool)); + last_number = entry.items[sub_item].number; + } + + last_entry_end = entry_end; + } + + /* close the source file */ + SVN_ERR(svn_io_file_close(proto_index, local_pool)); + + /* store length of last table */ + APR_ARRAY_PUSH(table_sizes, apr_uint64_t) + = svn_spillbuf__get_size(buffer) - last_buffer_size; + + /* Open target stream. */ + stream = svn_stream_checksummed2(svn_stream_from_aprfile2(index_file, TRUE, + local_pool), + NULL, checksum, svn_checksum_md5, FALSE, + result_pool); + + /* write the start revision, file size and page size */ + SVN_ERR(svn_stream_puts(stream, P2L_STREAM_PREFIX)); + SVN_ERR(stream_write_encoded(stream, revision)); + SVN_ERR(stream_write_encoded(stream, file_size)); + SVN_ERR(stream_write_encoded(stream, page_size)); + + /* write the page table (actually, the sizes of each page description) */ + SVN_ERR(stream_write_encoded(stream, table_sizes->nelts)); + for (i = 0; i < table_sizes->nelts; ++i) + { + apr_uint64_t value = APR_ARRAY_IDX(table_sizes, i, apr_uint64_t); + SVN_ERR(stream_write_encoded(stream, value)); + } + + /* append page contents and implicitly close STREAM */ + SVN_ERR(svn_stream_copy3(svn_stream__from_spillbuf(buffer, local_pool), + stream, NULL, NULL, local_pool)); + + svn_pool_destroy(iterpool); + svn_pool_destroy(local_pool); + + return SVN_NO_ERROR; +} + +/* If REV_FILE->P2L_STREAM is NULL, create a new stream for the phys-to-log + * index for REVISION in FS using the rev / pack file provided by REV_FILE. + */ +static svn_error_t * +auto_open_p2l_index(svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision) +{ + if (rev_file->p2l_stream == NULL) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); + SVN_ERR(packed_stream_open(&rev_file->p2l_stream, + rev_file->file, + rev_file->p2l_offset, + rev_file->footer_offset, + P2L_STREAM_PREFIX, + (apr_size_t)ffd->block_size, + rev_file->pool, + rev_file->pool)); + } + + return SVN_NO_ERROR; +} + +/* Data structure that describes which p2l page info shall be extracted + * from the cache and contains the fields that receive the result. + */ +typedef struct p2l_page_info_baton_t +{ + /* input variables */ + /* revision identifying the index file */ + svn_revnum_t revision; + + /* offset within the page in rev / pack file */ + apr_off_t offset; + + /* output variables */ + /* page containing OFFSET */ + apr_size_t page_no; + + /* first revision in this p2l index */ + svn_revnum_t first_revision; + + /* offset within the p2l index file describing this page */ + apr_off_t start_offset; + + /* offset within the p2l index file describing the following page */ + apr_off_t next_offset; + + /* PAGE_NO * PAGE_SIZE (if <= OFFSET) */ + apr_off_t page_start; + + /* total number of pages indexed */ + apr_size_t page_count; + + /* size of each page in pack / rev file */ + apr_uint64_t page_size; +} p2l_page_info_baton_t; + +/* From HEADER and the list of all OFFSETS, fill BATON with the page info + * requested by BATON->OFFSET. + */ +static void +p2l_page_info_copy(p2l_page_info_baton_t *baton, + const p2l_header_t *header, + const apr_off_t *offsets) +{ + /* if the requested offset is out of bounds, return info for + * a zero-sized empty page right behind the last page. + */ + if (baton->offset / header->page_size < header->page_count) + { + /* This cast is safe because the value is < header->page_count. */ + baton->page_no = (apr_size_t)(baton->offset / header->page_size); + baton->start_offset = offsets[baton->page_no]; + baton->next_offset = offsets[baton->page_no + 1]; + baton->page_size = header->page_size; + } + else + { + /* Beyond the last page. */ + baton->page_no = header->page_count; + baton->start_offset = offsets[baton->page_no]; + baton->next_offset = offsets[baton->page_no]; + baton->page_size = 0; + } + + baton->first_revision = header->first_revision; + baton->page_start = (apr_off_t)(header->page_size * baton->page_no); + baton->page_count = header->page_count; +} + +/* Implement svn_cache__partial_getter_func_t: extract the p2l page info + * requested by BATON and return it in BATON. + */ +static svn_error_t * +p2l_page_info_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + /* all the pointers to cached data we need */ + const p2l_header_t *header = data; + const apr_off_t *offsets + = svn_temp_deserializer__ptr(header, + (const void *const *)&header->offsets); + + /* copy data from cache to BATON */ + p2l_page_info_copy(baton, header, offsets); + return SVN_NO_ERROR; +} + +/* Read the header data structure of the phys-to-log index for REVISION in + * FS and return it in *HEADER, allocated in RESULT_POOL. Use REV_FILE to + * access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_p2l_header(p2l_header_t **header, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_uint64_t value; + apr_size_t i; + apr_off_t offset; + p2l_header_t *result; + svn_boolean_t is_cached = FALSE; + + /* look for the header data in our cache */ + svn_fs_x__pair_cache_key_t key; + key.revision = rev_file->start_revision; + key.second = rev_file->is_packed; + + SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->p2l_header_cache, + &key, result_pool)); + if (is_cached) + return SVN_NO_ERROR; + + /* not found -> must read it from disk. + * Open index file or position read pointer to the begin of the file */ + SVN_ERR(auto_open_p2l_index(rev_file, fs, key.revision)); + packed_stream_seek(rev_file->p2l_stream, 0); + + /* allocate result data structure */ + result = apr_pcalloc(result_pool, sizeof(*result)); + + /* Read table sizes, check them for plausibility and allocate page array. */ + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->first_revision = (svn_revnum_t)value; + if (result->first_revision != rev_file->start_revision) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index rev / pack file revision numbers do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->file_size = value; + if (result->file_size != (apr_uint64_t)rev_file->l2p_offset) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Index offset and rev / pack file size do not match")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->page_size = value; + if (!result->page_size || (result->page_size & (result->page_size - 1))) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L index page size is not a power of two")); + + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->page_count = (apr_size_t)value; + if (result->page_count != (result->file_size - 1) / result->page_size + 1) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L page count does not match rev / pack file size")); + + result->offsets + = apr_pcalloc(result_pool, (result->page_count + 1) * sizeof(*result->offsets)); + + /* read page sizes and derive page description offsets from them */ + result->offsets[0] = 0; + for (i = 0; i < result->page_count; ++i) + { + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + result->offsets[i+1] = result->offsets[i] + (apr_off_t)value; + } + + /* correct the offset values */ + offset = packed_stream_offset(rev_file->p2l_stream); + for (i = 0; i <= result->page_count; ++i) + result->offsets[i] += offset; + + /* cache the header data */ + SVN_ERR(svn_cache__set(ffd->p2l_header_cache, &key, result, scratch_pool)); + + /* return the result */ + *header = result; + + return SVN_NO_ERROR; +} + +/* Read the header data structure of the phys-to-log index for revision + * BATON->REVISION in FS. Return in *BATON all info relevant to read the + * index page for the rev / pack file offset BATON->OFFSET. Use REV_FILE + * to access on-disk data. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +get_p2l_page_info(p2l_page_info_baton_t *baton, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + p2l_header_t *header; + svn_boolean_t is_cached = FALSE; + void *dummy = NULL; + + /* look for the header data in our cache */ + svn_fs_x__pair_cache_key_t key; + key.revision = base_revision(fs, baton->revision); + key.second = svn_fs_x__is_packed_rev(fs, baton->revision); + + SVN_ERR(svn_cache__get_partial(&dummy, &is_cached, ffd->p2l_header_cache, + &key, p2l_page_info_func, baton, + scratch_pool)); + if (is_cached) + return SVN_NO_ERROR; + + SVN_ERR(get_p2l_header(&header, rev_file, fs, baton->revision, + scratch_pool, scratch_pool)); + + /* copy the requested info into *BATON */ + p2l_page_info_copy(baton, header, header->offsets); + + return SVN_NO_ERROR; +} + +/* Read a mapping entry from the phys-to-log index STREAM and append it to + * RESULT. *ITEM_INDEX contains the phys offset for the entry and will + * be moved forward by the size of entry. + */ +static svn_error_t * +read_entry(svn_fs_x__packed_number_stream_t *stream, + apr_off_t *item_offset, + svn_revnum_t revision, + apr_array_header_t *result) +{ + apr_uint64_t value; + apr_uint64_t number = 0; + apr_uint32_t sub_item; + + svn_fs_x__p2l_entry_t entry; + + entry.offset = *item_offset; + SVN_ERR(packed_stream_get(&value, stream)); + entry.size = (apr_off_t)value; + SVN_ERR(packed_stream_get(&value, stream)); + entry.type = (int)value % 16; + entry.item_count = (apr_uint32_t)(value / 16); + + /* Verify item type. */ + if (entry.type > SVN_FS_X__ITEM_TYPE_REPS_CONT) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid item type in P2L index")); + + SVN_ERR(packed_stream_get(&value, stream)); + entry.fnv1_checksum = (apr_uint32_t)value; + + /* Truncating the checksum to 32 bits may have hidden random data in the + * unused extra bits of the on-disk representation (7/8 bit representation + * uses 5 bytes on disk for the 32 bit value, leaving 3 bits unused). */ + if (value > APR_UINT32_MAX) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Invalid FNV1 checksum in P2L index")); + + /* Some of the index data for empty rev / pack file sections will not be + * used during normal operation. Thus, we have strict rules for the + * contents of those unused fields. */ + if (entry.type == SVN_FS_X__ITEM_TYPE_UNUSED) + if ( entry.fnv1_checksum != 0 + || entry.item_count != 0) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Unused regions must be empty and have checksum 0")); + + if (entry.item_count == 0) + { + entry.items = NULL; + } + else + { + entry.items + = apr_pcalloc(result->pool, entry.item_count * sizeof(*entry.items)); + + if ( entry.item_count > 1 + && entry.type < SVN_FS_X__ITEM_TYPE_CHANGES_CONT) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Only containers may have more than one sub-item")); + + for (sub_item = 0; sub_item < entry.item_count; ++sub_item) + { + SVN_ERR(packed_stream_get(&value, stream)); + revision += (svn_revnum_t)(value % 2 ? -1 - value / 2 : value / 2); + entry.items[sub_item].change_set + = svn_fs_x__change_set_by_rev(revision); + } + + for (sub_item = 0; sub_item < entry.item_count; ++sub_item) + { + SVN_ERR(packed_stream_get(&value, stream)); + number += value % 2 ? -1 - value / 2 : value / 2; + entry.items[sub_item].number = number; + + if ( ( entry.type == SVN_FS_X__ITEM_TYPE_CHANGES + || entry.type == SVN_FS_X__ITEM_TYPE_CHANGES_CONT) + && number != SVN_FS_X__ITEM_INDEX_CHANGES) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("Changed path list must have item number 1")); + } + } + + APR_ARRAY_PUSH(result, svn_fs_x__p2l_entry_t) = entry; + *item_offset += entry.size; + + return SVN_NO_ERROR; +} + +/* Read the phys-to-log mappings for the cluster beginning at rev file + * offset PAGE_START from the index for START_REVISION in FS. The data + * can be found in the index page beginning at START_OFFSET with the next + * page beginning at NEXT_OFFSET. PAGE_SIZE is the L2P index page size. + * Return the relevant index entries in *ENTRIES. Use REV_FILE to access + * on-disk data. Allocate *ENTRIES in RESULT_POOL. + */ +static svn_error_t * +get_p2l_page(apr_array_header_t **entries, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t start_revision, + apr_off_t start_offset, + apr_off_t next_offset, + apr_off_t page_start, + apr_uint64_t page_size, + apr_pool_t *result_pool) +{ + apr_uint64_t value; + apr_array_header_t *result + = apr_array_make(result_pool, 16, sizeof(svn_fs_x__p2l_entry_t)); + apr_off_t item_offset; + apr_off_t offset; + + /* open index and navigate to page start */ + SVN_ERR(auto_open_p2l_index(rev_file, fs, start_revision)); + packed_stream_seek(rev_file->p2l_stream, start_offset); + + /* read rev file offset of the first page entry (all page entries will + * only store their sizes). */ + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + item_offset = (apr_off_t)value; + + /* Special case: empty pages. */ + if (start_offset == next_offset) + { + /* Empty page. This only happens if the first entry of the next page + * also covers this page (and possibly more) completely. */ + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, start_revision, + result)); + } + else + { + /* Read non-empty page. */ + do + { + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, + start_revision, result)); + offset = packed_stream_offset(rev_file->p2l_stream); + } + while (offset < next_offset); + + /* We should now be exactly at the next offset, i.e. the numbers in + * the stream cannot overlap into the next page description. */ + if (offset != next_offset) + return svn_error_create(SVN_ERR_FS_INDEX_CORRUPTION, NULL, + _("P2L page description overlaps with next page description")); + + /* if we haven't covered the cluster end yet, we must read the first + * entry of the next page */ + if (item_offset < page_start + page_size) + { + SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); + item_offset = (apr_off_t)value; + SVN_ERR(read_entry(rev_file->p2l_stream, &item_offset, + start_revision, result)); + } + } + + *entries = result; + + return SVN_NO_ERROR; +} + +/* If it cannot be found in FS's caches, read the p2l index page selected + * by BATON->OFFSET from *STREAM. If the latter is yet to be constructed, + * do so in STREAM_POOL. Don't read the page if it precedes MIN_OFFSET. + * Set *END to TRUE if the caller should stop refeching. + * + * *BATON will be updated with the selected page's info and SCRATCH_POOL + * will be used for temporary allocations. If the data is alread in the + * cache, descrease *LEAKING_BUCKET and increase it otherwise. With that + * pattern we will still read all pages from the block even if some of + * them survived in the cached. + */ +static svn_error_t * +prefetch_p2l_page(svn_boolean_t *end, + int *leaking_bucket, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + p2l_page_info_baton_t *baton, + apr_off_t min_offset, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_boolean_t already_cached; + apr_array_header_t *page; + svn_fs_x__page_cache_key_t key = { 0 }; + + /* fetch the page info */ + *end = FALSE; + baton->revision = baton->first_revision; + SVN_ERR(get_p2l_page_info(baton, rev_file, fs, scratch_pool)); + if (baton->start_offset < min_offset) + { + /* page outside limits -> stop prefetching */ + *end = TRUE; + return SVN_NO_ERROR; + } + + /* do we have that page in our caches already? */ + assert(baton->first_revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)baton->first_revision; + key.is_packed = svn_fs_x__is_packed_rev(fs, baton->first_revision); + key.page = baton->page_no; + SVN_ERR(svn_cache__has_key(&already_cached, ffd->p2l_page_cache, + &key, scratch_pool)); + + /* yes, already cached */ + if (already_cached) + { + /* stop prefetching if most pages are already cached. */ + if (!--*leaking_bucket) + *end = TRUE; + + return SVN_NO_ERROR; + } + + ++*leaking_bucket; + + /* read from disk */ + SVN_ERR(get_p2l_page(&page, rev_file, fs, + baton->first_revision, + baton->start_offset, + baton->next_offset, + baton->page_start, + baton->page_size, + scratch_pool)); + + /* and put it into our cache */ + SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Lookup & construct the baton and key information that we will need for + * a P2L page cache lookup. We want the page covering OFFSET in the rev / + * pack file containing REVSION in FS. Return the results in *PAGE_INFO_P + * and *KEY_P. Read data through REV_FILE. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +get_p2l_keys(p2l_page_info_baton_t *page_info_p, + svn_fs_x__page_cache_key_t *key_p, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *scratch_pool) +{ + p2l_page_info_baton_t page_info; + + /* request info for the index pages that describes the pack / rev file + * contents at pack / rev file position OFFSET. */ + page_info.offset = offset; + page_info.revision = revision; + SVN_ERR(get_p2l_page_info(&page_info, rev_file, fs, scratch_pool)); + + /* if the offset refers to a non-existent page, bail out */ + if (page_info.page_count <= page_info.page_no) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Offset %s too large in revision %ld"), + apr_off_t_toa(scratch_pool, offset), revision); + + /* return results */ + if (page_info_p) + *page_info_p = page_info; + + /* construct cache key */ + if (key_p) + { + svn_fs_x__page_cache_key_t key = { 0 }; + assert(page_info.first_revision <= APR_UINT32_MAX); + key.revision = (apr_uint32_t)page_info.first_revision; + key.is_packed = svn_fs_x__is_packed_rev(fs, revision); + key.page = page_info.page_no; + + *key_p = key; + } + + return SVN_NO_ERROR; +} + +/* qsort-compatible compare function that compares the OFFSET of the + * svn_fs_x__p2l_entry_t in *LHS with the apr_off_t in *RHS. */ +static int +compare_start_p2l_entry(const void *lhs, + const void *rhs) +{ + const svn_fs_x__p2l_entry_t *entry = lhs; + apr_off_t start = *(const apr_off_t*)rhs; + apr_off_t diff = entry->offset - start; + + /* restrict result to int */ + return diff < 0 ? -1 : (diff == 0 ? 0 : 1); +} + +/* From the PAGE_ENTRIES array of svn_fs_x__p2l_entry_t, ordered + * by their OFFSET member, copy all elements overlapping the range + * [BLOCK_START, BLOCK_END) to ENTRIES. If RESOLVE_PTR is set, the ITEMS + * sub-array in each entry needs to be de-serialized. */ +static void +append_p2l_entries(apr_array_header_t *entries, + apr_array_header_t *page_entries, + apr_off_t block_start, + apr_off_t block_end, + svn_boolean_t resolve_ptr) +{ + const svn_fs_x__p2l_entry_t *entry; + int idx = svn_sort__bsearch_lower_bound(page_entries, &block_start, + compare_start_p2l_entry); + + /* start at the first entry that overlaps with BLOCK_START */ + if (idx > 0) + { + entry = &APR_ARRAY_IDX(page_entries, idx - 1, svn_fs_x__p2l_entry_t); + if (entry->offset + entry->size > block_start) + --idx; + } + + /* copy all entries covering the requested range */ + for ( ; idx < page_entries->nelts; ++idx) + { + svn_fs_x__p2l_entry_t *copy; + entry = &APR_ARRAY_IDX(page_entries, idx, svn_fs_x__p2l_entry_t); + if (entry->offset >= block_end) + break; + + /* Copy the entry record. */ + copy = apr_array_push(entries); + *copy = *entry; + + /* Copy the items of that entries. */ + if (entry->item_count) + { + const svn_fs_x__id_t *items + = resolve_ptr + ? svn_temp_deserializer__ptr(page_entries->elts, + (const void * const *)&entry->items) + : entry->items; + + copy->items = apr_pmemdup(entries->pool, items, + entry->item_count * sizeof(*items)); + } + } +} + +/* Auxilliary struct passed to p2l_entries_func selecting the relevant + * data range. */ +typedef struct p2l_entries_baton_t +{ + apr_off_t start; + apr_off_t end; +} p2l_entries_baton_t; + +/* Implement svn_cache__partial_getter_func_t: extract p2l entries from + * the page in DATA which overlap the p2l_entries_baton_t in BATON. + * The target array is already provided in *OUT. + */ +static svn_error_t * +p2l_entries_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + apr_array_header_t *entries = *(apr_array_header_t **)out; + const apr_array_header_t *raw_page = data; + p2l_entries_baton_t *block = baton; + + /* Make PAGE a readable APR array. */ + apr_array_header_t page = *raw_page; + page.elts = (void *)svn_temp_deserializer__ptr(raw_page, + (const void * const *)&raw_page->elts); + + /* append relevant information to result */ + append_p2l_entries(entries, &page, block->start, block->end, TRUE); + + return SVN_NO_ERROR; +} + + +/* Body of svn_fs_x__p2l_index_lookup. However, do a single index page + * lookup and append the result to the ENTRIES array provided by the caller. + * Use successive calls to cover larger ranges. + */ +static svn_error_t * +p2l_index_lookup(apr_array_header_t *entries, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_end, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__page_cache_key_t key; + svn_boolean_t is_cached = FALSE; + p2l_page_info_baton_t page_info; + apr_array_header_t *local_result = entries; + + /* baton selecting the relevant entries from the one page we access */ + p2l_entries_baton_t block; + block.start = block_start; + block.end = block_end; + + /* if we requested an empty range, the result would be empty */ + SVN_ERR_ASSERT(block_start < block_end); + + /* look for the fist page of the range in our cache */ + SVN_ERR(get_p2l_keys(&page_info, &key, rev_file, fs, revision, block_start, + scratch_pool)); + SVN_ERR(svn_cache__get_partial((void**)&local_result, &is_cached, + ffd->p2l_page_cache, &key, p2l_entries_func, + &block, scratch_pool)); + + if (!is_cached) + { + svn_boolean_t end; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_off_t original_page_start = page_info.page_start; + int leaking_bucket = 4; + p2l_page_info_baton_t prefetch_info = page_info; + apr_array_header_t *page_entries; + + apr_off_t max_offset + = APR_ALIGN(page_info.next_offset, ffd->block_size); + apr_off_t min_offset + = APR_ALIGN(page_info.start_offset, ffd->block_size) - ffd->block_size; + + /* Since we read index data in larger chunks, we probably got more + * page data than we requested. Parse & cache that until either we + * encounter pages already cached or reach the end of the buffer. + */ + + /* pre-fetch preceding pages */ + end = FALSE; + prefetch_info.offset = original_page_start; + while (prefetch_info.offset >= prefetch_info.page_size && !end) + { + prefetch_info.offset -= prefetch_info.page_size; + SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, rev_file, + &prefetch_info, min_offset, iterpool)); + svn_pool_clear(iterpool); + } + + /* fetch page from disk and put it into the cache */ + SVN_ERR(get_p2l_page(&page_entries, rev_file, fs, + page_info.first_revision, + page_info.start_offset, + page_info.next_offset, + page_info.page_start, + page_info.page_size, iterpool)); + + /* The last cache entry must not end beyond the range covered by + * this index. The same applies for any subset of entries. */ + if (page_entries->nelts) + { + const svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(page_entries, page_entries->nelts - 1, + svn_fs_x__p2l_entry_t); + if ( entry->offset + entry->size + > page_info.page_size * page_info.page_count) + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, + _("Last P2L index entry extends beyond " + "the last page in revision %ld."), + revision); + } + + SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page_entries, + iterpool)); + + /* append relevant information to result */ + append_p2l_entries(entries, page_entries, block_start, block_end, FALSE); + + /* pre-fetch following pages */ + end = FALSE; + leaking_bucket = 4; + prefetch_info = page_info; + prefetch_info.offset = original_page_start; + while ( prefetch_info.next_offset < max_offset + && prefetch_info.page_no + 1 < prefetch_info.page_count + && !end) + { + prefetch_info.offset += prefetch_info.page_size; + SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, rev_file, + &prefetch_info, min_offset, iterpool)); + svn_pool_clear(iterpool); + } + + svn_pool_destroy(iterpool); + } + + /* We access a valid page (otherwise, we had seen an error in the + * get_p2l_keys request). Hence, at least one entry must be found. */ + SVN_ERR_ASSERT(entries->nelts > 0); + + /* Add an "unused" entry if it extends beyond the end of the data file. + * Since the index page size might be smaller than the current data + * read block size, the trailing "unused" entry in this index may not + * fully cover the end of the last block. */ + if (page_info.page_no + 1 >= page_info.page_count) + { + svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_x__p2l_entry_t); + + apr_off_t entry_end = entry->offset + entry->size; + if (entry_end < block_end) + { + if (entry->type == SVN_FS_X__ITEM_TYPE_UNUSED) + { + /* extend the terminal filler */ + entry->size = block_end - entry->offset; + } + else + { + /* No terminal filler. Add one. */ + entry = apr_array_push(entries); + entry->offset = entry_end; + entry->size = block_end - entry_end; + entry->type = SVN_FS_X__ITEM_TYPE_UNUSED; + entry->fnv1_checksum = 0; + entry->item_count = 0; + entry->items = NULL; + } + } + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_index_lookup(apr_array_header_t **entries, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_off_t block_end = block_start + block_size; + + /* the receiving container */ + int last_count = 0; + apr_array_header_t *result = apr_array_make(result_pool, 16, + sizeof(svn_fs_x__p2l_entry_t)); + + /* Fetch entries page-by-page. Since the p2l index is supposed to cover + * every single byte in the rev / pack file - even unused sections - + * every iteration must result in some progress. */ + while (block_start < block_end) + { + svn_fs_x__p2l_entry_t *entry; + SVN_ERR(p2l_index_lookup(result, rev_file, fs, revision, block_start, + block_end, scratch_pool)); + SVN_ERR_ASSERT(result->nelts > 0); + + /* continue directly behind last item */ + entry = &APR_ARRAY_IDX(result, result->nelts-1, svn_fs_x__p2l_entry_t); + block_start = entry->offset + entry->size; + + /* Some paranoia check. Successive iterations should never return + * duplicates but if it did, we might get into trouble later on. */ + if (last_count > 0 && last_count < result->nelts) + { + entry = &APR_ARRAY_IDX(result, last_count - 1, + svn_fs_x__p2l_entry_t); + SVN_ERR_ASSERT(APR_ARRAY_IDX(result, last_count, + svn_fs_x__p2l_entry_t).offset + >= entry->offset + entry->size); + } + + last_count = result->nelts; + } + + *entries = result; + return SVN_NO_ERROR; +} + +/* compare_fn_t comparing a svn_fs_x__p2l_entry_t at LHS with an offset + * RHS. + */ +static int +compare_p2l_entry_offsets(const void *lhs, const void *rhs) +{ + const svn_fs_x__p2l_entry_t *entry = (const svn_fs_x__p2l_entry_t *)lhs; + apr_off_t offset = *(const apr_off_t *)rhs; + + return entry->offset < offset ? -1 : (entry->offset == offset ? 0 : 1); +} + +/* Cached data extraction utility. DATA is a P2L index page, e.g. an APR + * array of svn_fs_fs__p2l_entry_t elements. Return the entry for the item, + * allocated in RESULT_POOL, starting at OFFSET or NULL if that's not an + * the start offset of any item. Use SCRATCH_POOL for temporary allocations. + */ +static svn_fs_x__p2l_entry_t * +get_p2l_entry_from_cached_page(const void *data, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* resolve all pointer values of in-cache data */ + const apr_array_header_t *page = data; + apr_array_header_t *entries = apr_pmemdup(scratch_pool, page, + sizeof(*page)); + svn_fs_x__p2l_entry_t *entry; + + entries->elts = (char *)svn_temp_deserializer__ptr(page, + (const void *const *)&page->elts); + + /* search of the offset we want */ + entry = svn_sort__array_lookup(entries, &offset, NULL, + (int (*)(const void *, const void *))compare_p2l_entry_offsets); + + /* return it, if it is a perfect match */ + if (entry) + { + svn_fs_x__p2l_entry_t *result + = apr_pmemdup(result_pool, entry, sizeof(*result)); + result->items + = (svn_fs_x__id_t *)svn_temp_deserializer__ptr(entries->elts, + (const void *const *)&entry->items); + return result; + } + + return NULL; +} + +/* Implements svn_cache__partial_getter_func_t for P2L index pages, copying + * the entry for the apr_off_t at BATON into *OUT. *OUT will be NULL if + * there is no matching entry in the index page at DATA. + */ +static svn_error_t * +p2l_entry_lookup_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + svn_fs_x__p2l_entry_t *entry + = get_p2l_entry_from_cached_page(data, *(apr_off_t *)baton, result_pool, + result_pool); + + *out = entry && entry->offset == *(apr_off_t *)baton + ? svn_fs_x__p2l_entry_dup(entry, result_pool) + : NULL; + + return SVN_NO_ERROR; +} + +static svn_error_t * +p2l_entry_lookup(svn_fs_x__p2l_entry_t **entry_p, + svn_fs_x__revision_file_t *rev_file, + svn_fs_t *fs, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__page_cache_key_t key = { 0 }; + svn_boolean_t is_cached = FALSE; + p2l_page_info_baton_t page_info; + + /* look for this info in our cache */ + SVN_ERR(get_p2l_keys(&page_info, &key, rev_file, fs, revision, offset, + scratch_pool)); + SVN_ERR(svn_cache__get_partial((void**)entry_p, &is_cached, + ffd->p2l_page_cache, &key, + p2l_entry_lookup_func, &offset, + result_pool)); + if (!is_cached) + { + /* do a standard index lookup. This is will automatically prefetch + * data to speed up future lookups. */ + apr_array_header_t *entries = apr_array_make(result_pool, 1, + sizeof(**entry_p)); + SVN_ERR(p2l_index_lookup(entries, rev_file, fs, revision, offset, + offset + 1, scratch_pool)); + + /* Find the entry that we want. */ + *entry_p = svn_sort__array_lookup(entries, &offset, NULL, + (int (*)(const void *, const void *))compare_p2l_entry_offsets); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_entry_lookup(svn_fs_x__p2l_entry_t **entry_p, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* look for this info in our cache */ + SVN_ERR(p2l_entry_lookup(entry_p, rev_file, fs, revision, offset, + result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Baton structure for p2l_item_lookup_func. It describes which sub_item + * info shall be returned. + */ +typedef struct p2l_item_lookup_baton_t +{ + /* file offset to find the P2L index entry for */ + apr_off_t offset; + + /* return the sub-item at this position within that entry */ + apr_uint32_t sub_item; +} p2l_item_lookup_baton_t; + +/* Implements svn_cache__partial_getter_func_t for P2L index pages, copying + * the svn_fs_x__id_t for the item described 2l_item_lookup_baton_t + * *BATON. *OUT will be NULL if there is no matching index entry or the + * sub-item is out of range. + */ +static svn_error_t * +p2l_item_lookup_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + p2l_item_lookup_baton_t *lookup_baton = baton; + svn_fs_x__p2l_entry_t *entry + = get_p2l_entry_from_cached_page(data, lookup_baton->offset, result_pool, + result_pool); + + *out = entry + && entry->offset == lookup_baton->offset + && entry->item_count > lookup_baton->sub_item + ? apr_pmemdup(result_pool, + entry->items + lookup_baton->sub_item, + sizeof(*entry->items)) + : NULL; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_item_lookup(svn_fs_x__id_t **item, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_uint32_t sub_item, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__page_cache_key_t key = { 0 }; + svn_boolean_t is_cached = FALSE; + p2l_page_info_baton_t page_info; + p2l_item_lookup_baton_t baton; + + *item = NULL; + + /* look for this info in our cache */ + SVN_ERR(get_p2l_keys(&page_info, &key, rev_file, fs, revision, offset, + scratch_pool)); + baton.offset = offset; + baton.sub_item = sub_item; + SVN_ERR(svn_cache__get_partial((void**)item, &is_cached, + ffd->p2l_page_cache, &key, + p2l_item_lookup_func, &baton, result_pool)); + if (!is_cached) + { + /* do a standard index lookup. This is will automatically prefetch + * data to speed up future lookups. */ + svn_fs_x__p2l_entry_t *entry; + SVN_ERR(p2l_entry_lookup(&entry, rev_file, fs, revision, offset, + result_pool, scratch_pool)); + + /* return result */ + if (entry && entry->item_count > sub_item) + *item = apr_pmemdup(result_pool, entry->items + sub_item, + sizeof(**item)); + } + + return SVN_NO_ERROR; +} + +/* Implements svn_cache__partial_getter_func_t for P2L headers, setting *OUT + * to the largest the first offset not covered by this P2L index. + */ +static svn_error_t * +p2l_get_max_offset_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *result_pool) +{ + const p2l_header_t *header = data; + apr_off_t max_offset = header->file_size; + *out = apr_pmemdup(result_pool, &max_offset, sizeof(max_offset)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__p2l_get_max_offset(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + p2l_header_t *header; + svn_boolean_t is_cached = FALSE; + apr_off_t *offset_p; + + /* look for the header data in our cache */ + svn_fs_x__pair_cache_key_t key; + key.revision = base_revision(fs, revision); + key.second = svn_fs_x__is_packed_rev(fs, revision); + + SVN_ERR(svn_cache__get_partial((void **)&offset_p, &is_cached, + ffd->p2l_header_cache, &key, + p2l_get_max_offset_func, NULL, + scratch_pool)); + if (is_cached) + { + *offset = *offset_p; + return SVN_NO_ERROR; + } + + SVN_ERR(get_p2l_header(&header, rev_file, fs, revision, scratch_pool, + scratch_pool)); + *offset = header->file_size; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__item_offset(apr_off_t *absolute_position, + apr_uint32_t *sub_item, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + const svn_fs_x__id_t *item_id, + apr_pool_t *scratch_pool) +{ + if (svn_fs_x__is_txn(item_id->change_set)) + SVN_ERR(l2p_proto_index_lookup(absolute_position, sub_item, fs, + svn_fs_x__get_txn_id(item_id->change_set), + item_id->number, scratch_pool)); + else + SVN_ERR(l2p_index_lookup(absolute_position, sub_item, fs, rev_file, + svn_fs_x__get_revnum(item_id->change_set), + item_id->number, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Calculate the FNV1 checksum over the offset range in REV_FILE, covered by + * ENTRY. Store the result in ENTRY->FNV1_CHECKSUM. Use SCRATCH_POOL for + * temporary allocations. */ +static svn_error_t * +calc_fnv1(svn_fs_x__p2l_entry_t *entry, + svn_fs_x__revision_file_t *rev_file, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[4096]; + svn_checksum_t *checksum; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, scratch_pool); + apr_off_t size = entry->size; + + /* Special rules apply to unused sections / items. The data must be a + * sequence of NUL bytes (not checked here) and the checksum is fixed to 0. + */ + if (entry->type == SVN_FS_X__ITEM_TYPE_UNUSED) + { + entry->fnv1_checksum = 0; + return SVN_NO_ERROR; + } + + /* Read the block and feed it to the checksum calculator. */ + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &entry->offset, + scratch_pool)); + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(rev_file->file, buffer, to_read, NULL, + NULL, scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + } + + /* Store final checksum in ENTRY. */ + SVN_ERR(svn_checksum_final(&checksum, context, scratch_pool)); + entry->fnv1_checksum = ntohl(*(const apr_uint32_t *)checksum->digest); + + return SVN_NO_ERROR; +} + +/* + * Index (re-)creation utilities. + */ + +svn_error_t * +svn_fs_x__p2l_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *proto_index; + + /* Use a subpool for immediate temp file cleanup at the end of this + * function. */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + /* Create a proto-index file. */ + SVN_ERR(svn_io_open_unique_file3(NULL, protoname, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(svn_fs_x__p2l_proto_index_open(&proto_index, *protoname, + scratch_pool)); + + /* Write ENTRIES to proto-index file and calculate checksums as we go. */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t *); + svn_pool_clear(iterpool); + + SVN_ERR(calc_fnv1(entry, rev_file, iterpool)); + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry(proto_index, entry, + iterpool)); + } + + /* Convert proto-index into final index and move it into position. + * Note that REV_FILE contains the start revision of the shard file if it + * has been packed while REVISION may be somewhere in the middle. For + * non-packed shards, they will have identical values. */ + SVN_ERR(svn_io_file_close(proto_index, iterpool)); + + /* Temp file cleanup. */ + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Decorator for svn_fs_x__p2l_entry_t that associates it with a sorted + * variant of its ITEMS array. + */ +typedef struct sub_item_ordered_t +{ + /* ENTRY that got wrapped */ + svn_fs_x__p2l_entry_t *entry; + + /* Array of pointers into ENTRY->ITEMS, sorted by their revision member + * _descending_ order. May be NULL if ENTRY->ITEM_COUNT < 2. */ + svn_fs_x__id_t **order; +} sub_item_ordered_t; + +/* implements compare_fn_t. Place LHS before RHS, if the latter is younger. + * Used to sort sub_item_ordered_t::order + */ +static int +compare_sub_items(const svn_fs_x__id_t * const * lhs, + const svn_fs_x__id_t * const * rhs) +{ + return (*lhs)->change_set < (*rhs)->change_set + ? 1 + : ((*lhs)->change_set > (*rhs)->change_set ? -1 : 0); +} + +/* implements compare_fn_t. Place LHS before RHS, if the latter belongs to + * a newer revision. + */ +static int +compare_p2l_info_rev(const sub_item_ordered_t * lhs, + const sub_item_ordered_t * rhs) +{ + svn_fs_x__id_t *lhs_part; + svn_fs_x__id_t *rhs_part; + + assert(lhs != rhs); + if (lhs->entry->item_count == 0) + return rhs->entry->item_count == 0 ? 0 : -1; + if (rhs->entry->item_count == 0) + return 1; + + lhs_part = lhs->order ? lhs->order[lhs->entry->item_count - 1] + : &lhs->entry->items[0]; + rhs_part = rhs->order ? rhs->order[rhs->entry->item_count - 1] + : &rhs->entry->items[0]; + + if (lhs_part->change_set == rhs_part->change_set) + return 0; + + return lhs_part->change_set < rhs_part->change_set ? -1 : 1; +} + +svn_error_t * +svn_fs_x__l2p_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *proto_index; + + /* Use a subpool for immediate temp file cleanup at the end of this + * function. */ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_revnum_t prev_rev = SVN_INVALID_REVNUM; + int i; + apr_uint32_t k; + svn_priority_queue__t *queue; + apr_size_t count = 0; + apr_array_header_t *sub_item_orders; + + /* Create the temporary proto-rev file. */ + SVN_ERR(svn_io_open_unique_file3(NULL, protoname, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(svn_fs_x__l2p_proto_index_open(&proto_index, *protoname, + scratch_pool)); + + + /* wrap P2L entries such that we have access to the sub-items in revision + order. The ENTRY_COUNT member will point to the next item to read+1. */ + sub_item_orders = apr_array_make(scratch_pool, entries->nelts, + sizeof(sub_item_ordered_t)); + sub_item_orders->nelts = entries->nelts; + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t *); + sub_item_ordered_t *ordered + = &APR_ARRAY_IDX(sub_item_orders, i, sub_item_ordered_t); + + /* skip unused regions (e.g. padding) */ + if (entry->item_count == 0) + { + --sub_item_orders->nelts; + continue; + } + + assert(entry); + ordered->entry = entry; + count += entry->item_count; + + if (entry->item_count > 1) + { + ordered->order + = apr_palloc(scratch_pool, + sizeof(*ordered->order) * entry->item_count); + for (k = 0; k < entry->item_count; ++k) + ordered->order[k] = &entry->items[k]; + + qsort(ordered->order, entry->item_count, sizeof(*ordered->order), + (int (*)(const void *, const void *))compare_sub_items); + } + } + + /* we need to write the index in ascending revision order */ + queue = svn_priority_queue__create + (sub_item_orders, + (int (*)(const void *, const void *))compare_p2l_info_rev); + + /* write index entries */ + for (i = 0; i < count; ++i) + { + svn_fs_x__id_t *sub_item; + sub_item_ordered_t *ordered = svn_priority_queue__peek(queue); + + if (ordered->entry->item_count > 0) + { + /* if there is only one item, we skip the overhead of having an + extra array for the item order */ + sub_item = ordered->order + ? ordered->order[ordered->entry->item_count - 1] + : &ordered->entry->items[0]; + + /* next revision? */ + if (prev_rev != svn_fs_x__get_revnum(sub_item->change_set)) + { + prev_rev = svn_fs_x__get_revnum(sub_item->change_set); + SVN_ERR(svn_fs_x__l2p_proto_index_add_revision + (proto_index, iterpool)); + } + + /* add entry */ + SVN_ERR(svn_fs_x__l2p_proto_index_add_entry + (proto_index, ordered->entry->offset, + (apr_uint32_t)(sub_item - ordered->entry->items), + sub_item->number, iterpool)); + + /* make ITEM_COUNT point the next sub-item to use+1 */ + --ordered->entry->item_count; + } + + /* process remaining sub-items (if any) of that container later */ + if (ordered->entry->item_count) + svn_priority_queue__update(queue); + else + svn_priority_queue__pop(queue); + + /* keep memory usage in check */ + if (i % 256 == 0) + svn_pool_clear(iterpool); + } + + /* Convert proto-index into final index and move it into position. */ + SVN_ERR(svn_io_file_close(proto_index, iterpool)); + + /* Temp file cleanup. */ + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* + * Standard (de-)serialization functions + */ + +svn_error_t * +svn_fs_x__serialize_l2p_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + l2p_header_t *header = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t page_count = header->page_table_index[header->revision_count]; + apr_size_t page_table_size = page_count * sizeof(*header->page_table); + apr_size_t index_size + = (header->revision_count + 1) * sizeof(*header->page_table_index); + apr_size_t data_size = sizeof(*header) + index_size + page_table_size; + + /* serialize header and all its elements */ + context = svn_temp_serializer__init(header, + sizeof(*header), + data_size + 32, + pool); + + /* page table index array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->page_table_index, + index_size); + + /* page table array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->page_table, + page_table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_l2p_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + l2p_header_t *header = (l2p_header_t *)data; + + /* resolve the pointers in the struct */ + svn_temp_deserializer__resolve(header, (void**)&header->page_table_index); + svn_temp_deserializer__resolve(header, (void**)&header->page_table); + + /* done */ + *out = header; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_l2p_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + l2p_page_t *page = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t of_table_size = page->entry_count * sizeof(*page->offsets); + apr_size_t si_table_size = page->entry_count * sizeof(*page->sub_items); + + /* serialize struct and all its elements */ + context = svn_temp_serializer__init(page, + sizeof(*page), + of_table_size + si_table_size + + sizeof(*page) + 32, + pool); + + /* offsets and sub_items arrays */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&page->offsets, + of_table_size); + svn_temp_serializer__add_leaf(context, + (const void * const *)&page->sub_items, + si_table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_l2p_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + l2p_page_t *page = data; + + /* resolve the pointers in the struct */ + svn_temp_deserializer__resolve(page, (void**)&page->offsets); + svn_temp_deserializer__resolve(page, (void**)&page->sub_items); + + /* done */ + *out = page; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_p2l_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + p2l_header_t *header = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t table_size = (header->page_count + 1) * sizeof(*header->offsets); + + /* serialize header and all its elements */ + context = svn_temp_serializer__init(header, + sizeof(*header), + table_size + sizeof(*header) + 32, + pool); + + /* offsets array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&header->offsets, + table_size); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_p2l_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + p2l_header_t *header = data; + + /* resolve the only pointer in the struct */ + svn_temp_deserializer__resolve(header, (void**)&header->offsets); + + /* done */ + *out = header; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_p2l_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_array_header_t *page = in; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_size_t table_size = page->elt_size * page->nelts; + svn_fs_x__p2l_entry_t *entries = (svn_fs_x__p2l_entry_t *)page->elts; + int i; + + /* serialize array header and all its elements */ + context = svn_temp_serializer__init(page, + sizeof(*page), + table_size + sizeof(*page) + 32, + pool); + + /* items in the array */ + svn_temp_serializer__push(context, + (const void * const *)&page->elts, + table_size); + + for (i = 0; i < page->nelts; ++i) + svn_temp_serializer__add_leaf(context, + (const void * const *)&entries[i].items, + entries[i].item_count + * sizeof(*entries[i].items)); + + svn_temp_serializer__pop(context); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_p2l_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + apr_array_header_t *page = (apr_array_header_t *)data; + svn_fs_x__p2l_entry_t *entries; + int i; + + /* resolve the only pointer in the struct */ + svn_temp_deserializer__resolve(page, (void**)&page->elts); + + /* resolve sub-struct pointers*/ + entries = (svn_fs_x__p2l_entry_t *)page->elts; + for (i = 0; i < page->nelts; ++i) + svn_temp_deserializer__resolve(entries, (void**)&entries[i].items); + + /* patch up members */ + page->pool = pool; + page->nalloc = page->nelts; + + /* done */ + *out = page; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/index.h b/contrib/subversion/subversion/libsvn_fs_x/index.h new file mode 100644 index 000000000..4e0e1ddfc --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/index.h @@ -0,0 +1,411 @@ +/* index.h : interface to FSX indexing 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__INDEX_H +#define SVN_LIBSVN_FS__INDEX_H + +#include "fs.h" +#include "rev_file.h" + +/* Per-defined item index values. They are used to identify empty or + * mandatory items. + */ +#define SVN_FS_X__ITEM_INDEX_UNUSED 0 /* invalid / reserved value */ +#define SVN_FS_X__ITEM_INDEX_CHANGES 1 /* list of changed paths */ +#define SVN_FS_X__ITEM_INDEX_ROOT_NODE 2 /* the root noderev */ +#define SVN_FS_X__ITEM_INDEX_FIRST_USER 3 /* first noderev to be freely + assigned */ + +/* Data / item types as stored in the phys-to-log index. + */ +#define SVN_FS_X__ITEM_TYPE_UNUSED 0 /* file section not used */ +#define SVN_FS_X__ITEM_TYPE_FILE_REP 1 /* item is a file representation */ +#define SVN_FS_X__ITEM_TYPE_DIR_REP 2 /* item is a directory rep. */ +#define SVN_FS_X__ITEM_TYPE_FILE_PROPS 3 /* item is a file property rep. */ +#define SVN_FS_X__ITEM_TYPE_DIR_PROPS 4 /* item is a directory prop rep */ +#define SVN_FS_X__ITEM_TYPE_NODEREV 5 /* item is a noderev */ +#define SVN_FS_X__ITEM_TYPE_CHANGES 6 /* item is a changed paths list */ + +#define SVN_FS_X__ITEM_TYPE_ANY_REP 7 /* item is any representation. + Only used in pre-format7. */ + +#define SVN_FS_X__ITEM_TYPE_CHANGES_CONT 8 /* item is a changes container */ +#define SVN_FS_X__ITEM_TYPE_NODEREVS_CONT 9 /* item is a noderevs container */ +#define SVN_FS_X__ITEM_TYPE_REPS_CONT 10 /* item is a representations + container */ + +/* (user visible) entry in the phys-to-log index. It describes a section + * of some packed / non-packed rev file as containing a specific item. + * There must be no overlapping / conflicting entries. + */ +typedef struct svn_fs_x__p2l_entry_t +{ + /* offset of the first byte that belongs to the item */ + apr_off_t offset; + + /* length of the item in bytes */ + apr_off_t size; + + /* type of the item (see SVN_FS_X__ITEM_TYPE_*) defines */ + apr_uint32_t type; + + /* modified FNV-1a checksum. 0 if unknown checksum */ + apr_uint32_t fnv1_checksum; + + /* Number of items in this block / container. Their list can be found + * in *ITEMS. 0 for unused sections. 1 for non-container items, + * > 1 for containers. */ + apr_uint32_t item_count; + + /* List of items in that block / container */ + svn_fs_x__id_t *items; +} svn_fs_x__p2l_entry_t; + +/* Return a (deep) copy of ENTRY, allocated in RESULT_POOL. + */ +svn_fs_x__p2l_entry_t * +svn_fs_x__p2l_entry_dup(const svn_fs_x__p2l_entry_t *entry, + apr_pool_t *result_pool); + +/* Open / create a log-to-phys index file with the full file path name + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. + */ +svn_error_t * +svn_fs_x__l2p_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool); + +/* Call this function before adding entries for the next revision to the + * log-to-phys index file in PROTO_INDEX. Use SCRATCH_POOL for temporary + * allocations. + */ +svn_error_t * +svn_fs_x__l2p_proto_index_add_revision(apr_file_t *proto_index, + apr_pool_t *scratch_pool); + +/* Add a new mapping, ITEM_INDEX to the (OFFSET, SUB_ITEM) pair, to log-to- + * phys index file in PROTO_INDEX. Please note that mappings may be added + * in any order but duplicate entries for the same ITEM_INDEX, SUB_ITEM + * are not supported. Not all possible index values need to be used. + * (OFFSET, SUB_ITEM) may be (-1, 0) to mark 'invalid' item indexes but + * that is already implied for all item indexes not explicitly given a + * mapping. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__l2p_proto_index_add_entry(apr_file_t *proto_index, + apr_off_t offset, + apr_uint32_t sub_item, + apr_uint64_t item_index, + apr_pool_t *scratch_pool); + +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * log-to-phys index and append it to INDEX_FILE. The first revision will + * be REVISION, entries to the next revision will be assigned to REVISION+1 + * and so forth. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Open / create a phys-to-log index file with the full file path name + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. + */ +svn_error_t * +svn_fs_x__p2l_proto_index_open(apr_file_t **proto_index, + const char *file_name, + apr_pool_t *result_pool); + +/* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX. + * The entries must be added in ascending offset order and must not leave + * intermittent ranges uncovered. The revision value in ENTRY may be + * SVN_INVALID_REVISION. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_proto_index_add_entry(apr_file_t *proto_index, + const svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool); + +/* Set *NEXT_OFFSET to the first offset behind the last entry in the + * phys-to-log proto index file PROTO_INDEX. This will be 0 for empty + * index files. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_proto_index_next_offset(apr_off_t *next_offset, + apr_file_t *proto_index, + apr_pool_t *scratch_pool); + +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * phys-to-log index and append it to INDEX_FILE. Entries without a valid + * revision will be assigned to the REVISION given here. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, + const char *proto_file_name, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Use the phys-to-log mapping files in FS to build a list of entries + * that (at least partly) overlap with the range given by BLOCK_START + * offset and BLOCK_SIZE in the rep / pack file containing REVISION. + * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements, + * allocated in RESULT_POOL. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. + * + * Note that (only) the first and the last mapping may cross a cluster + * boundary. + */ +svn_error_t * +svn_fs_x__p2l_index_lookup(apr_array_header_t **entries, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t block_start, + apr_off_t block_size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Use the phys-to-log mapping files in FS to return the entry for the + * container or single item starting at global OFFSET in the rep file + * containing REVISION in*ENTRY, allocated in RESULT_POOL. Sets *ENTRY + * to NULL if no item starts at exactly that offset. REV_FILE determines + * whether to access single rev or pack file data. If that is not available + * anymore (neither in cache nor on disk), return an error. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_entry_lookup(svn_fs_x__p2l_entry_t **entry, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Use the phys-to-log mapping files in FS to return the svn_fs_x__id_t + * for the SUB_ITEM of the container starting at global OFFSET in the rep / + * pack file containing REVISION in *ITEM, allocated in RESULT_POOL. Sets + * *ITEM to NULL if no element starts at exactly that offset or if it + * contains no more than SUB_ITEM sub-items. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_item_lookup(svn_fs_x__id_t **item, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_off_t offset, + apr_uint32_t sub_item, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* For ITEM_ID in FS, return the position in the respective rev or pack file + * in *ABSOLUTE_POSITION and the *SUB_ITEM number within the object at that + * location. *SUB_ITEM will be 0 for non-container items. + * + * REV_FILE determines whether to access single rev or pack file data. + * If that is not available anymore (neither in cache nor on disk), re-open + * the rev / pack file and retry to open the index file. For transaction + * content, REV_FILE may be NULL. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__item_offset(apr_off_t *absolute_position, + apr_uint32_t *sub_item, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + const svn_fs_x__id_t *item_id, + apr_pool_t *scratch_pool); + +/* Use the log-to-phys indexes in FS to determine the maximum item indexes + * assigned to revision START_REV to START_REV + COUNT - 1. That is a + * close upper limit to the actual number of items in the respective revs. + * Return the results in *MAX_IDS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__l2p_get_max_ids(apr_array_header_t **max_ids, + svn_fs_t *fs, + svn_revnum_t start_rev, + apr_size_t count, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* In *OFFSET, return the first OFFSET in the pack / rev file containing + * REVISION in FS not covered by the log-to-phys index. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_get_max_offset(apr_off_t *offset, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_revnum_t revision, + apr_pool_t *scratch_pool); + +/* Index (re-)creation utilities. + */ + +/* For FS, create a new L2P auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * entries are of type svn_fs_fs__p2l_entry_t* (sic!). The ENTRIES array + * will be reordered. Give the proto index file the lifetime of RESULT_POOL + * and use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__l2p_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* For FS, create a new P2L auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * of type svn_fs_fs__p2l_entry_t*. The FVN1 checksums are not taken from + * ENTRIES but are begin calculated from the current contents of REV_FILE + * as we go. Give the proto index file the lifetime of RESULT_POOL and use + * SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Serialization and caching interface + */ + +/* We use this key type to address individual pages from both index types. + */ +typedef struct svn_fs_x__page_cache_key_t +{ + /* in l2p: this is the revision of the items being mapped + in p2l: this is the start revision identifying the pack / rev file */ + apr_uint32_t revision; + + /* if TRUE, this is the index to a pack file + */ + svn_boolean_t is_packed; + + /* in l2p: page number within the revision + * in p2l: page number with the rev / pack file + */ + apr_uint64_t page; +} svn_fs_x__page_cache_key_t; + +/* + * Implements svn_cache__serialize_func_t for l2p_header_t objects. + */ +svn_error_t * +svn_fs_x__serialize_l2p_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for l2p_header_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_l2p_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for l2p_page_t objects. + */ +svn_error_t * +svn_fs_x__serialize_l2p_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for l2p_page_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_l2p_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for p2l_header_t objects. + */ +svn_error_t * +svn_fs_x__serialize_p2l_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for p2l_header_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_p2l_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* + * Implements svn_cache__serialize_func_t for apr_array_header_t objects + * with elements of type svn_fs_x__p2l_entry_t. + */ +svn_error_t * +svn_fs_x__serialize_p2l_page(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* + * Implements svn_cache__deserialize_func_t for apr_array_header_t objects + * with elements of type svn_fs_x__p2l_entry_t. + */ +svn_error_t * +svn_fs_x__deserialize_p2l_page(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/libsvn_fs_x.pc.in b/contrib/subversion/subversion/libsvn_fs_x/libsvn_fs_x.pc.in new file mode 100644 index 000000000..46d93dceb --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/libsvn_fs_x.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs_x +Description: Subversion FSX Repository Filesystem Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr libsvn_fs_util +Libs: -L${libdir} -lsvn_fs_x +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_fs_x/lock.c b/contrib/subversion/subversion/libsvn_fs_x/lock.c new file mode 100644 index 000000000..6819f630e --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/lock.c @@ -0,0 +1,1492 @@ +/* lock.c : functions for manipulating filesystem locks. + * + * ==================================================================== + * 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_pools.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_fs.h" +#include "svn_hash.h" +#include "svn_time.h" +#include "svn_utf.h" + +#include +#include +#include + +#include "lock.h" +#include "tree.h" +#include "fs_x.h" +#include "transaction.h" +#include "util.h" +#include "../libsvn_fs/fs-loader.h" + +#include "private/svn_fs_util.h" +#include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" +#include "svn_private_config.h" + +/* Names of hash keys used to store a lock for writing to disk. */ +#define PATH_KEY "path" +#define TOKEN_KEY "token" +#define OWNER_KEY "owner" +#define CREATION_DATE_KEY "creation_date" +#define EXPIRATION_DATE_KEY "expiration_date" +#define COMMENT_KEY "comment" +#define IS_DAV_COMMENT_KEY "is_dav_comment" +#define CHILDREN_KEY "children" + +/* Number of characters from the head of a digest file name used to + calculate a subdirectory in which to drop that file. */ +#define DIGEST_SUBDIR_LEN 3 + + + +/*** Generic helper functions. ***/ + +/* Set *DIGEST to the MD5 hash of STR. */ +static svn_error_t * +make_digest(const char **digest, + const char *str, + apr_pool_t *pool) +{ + svn_checksum_t *checksum; + + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, strlen(str), pool)); + + *digest = svn_checksum_to_cstring_display(checksum, pool); + return SVN_NO_ERROR; +} + + +/* Set the value of KEY (whose size is KEY_LEN, or APR_HASH_KEY_STRING + if unknown) to an svn_string_t-ized version of VALUE (whose size is + VALUE_LEN, or APR_HASH_KEY_STRING if unknown) in HASH. The value + will be allocated in POOL; KEY will not be duped. If either KEY or VALUE + is NULL, this function will do nothing. */ +static void +hash_store(apr_hash_t *hash, + const char *key, + apr_ssize_t key_len, + const char *value, + apr_ssize_t value_len, + apr_pool_t *pool) +{ + if (! (key && value)) + return; + if (value_len == APR_HASH_KEY_STRING) + value_len = strlen(value); + apr_hash_set(hash, key, key_len, + svn_string_ncreate(value, value_len, pool)); +} + + +/* Fetch the value of KEY from HASH, returning only the cstring data + of that value (if it exists). */ +static const char * +hash_fetch(apr_hash_t *hash, + const char *key) +{ + svn_string_t *str = svn_hash_gets(hash, key); + return str ? str->data : NULL; +} + + +/* SVN_ERR_FS_CORRUPT: the lockfile for PATH in FS is corrupt. */ +static svn_error_t * +err_corrupt_lockfile(const char *fs_path, const char *path) +{ + return + svn_error_createf( + SVN_ERR_FS_CORRUPT, 0, + _("Corrupt lockfile for path '%s' in filesystem '%s'"), + path, fs_path); +} + + +/*** Digest file handling functions. ***/ + +/* Return the path of the lock/entries file for which DIGEST is the + hashed repository relative path. */ +static const char * +digest_path_from_digest(const char *fs_path, + const char *digest, + apr_pool_t *pool) +{ + return svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, + apr_pstrmemdup(pool, digest, DIGEST_SUBDIR_LEN), + digest, SVN_VA_NULL); +} + + +/* Set *DIGEST_PATH to the path to the lock/entries digest file associate + with PATH, where PATH is the path to the lock file or lock entries file + in FS. */ +static svn_error_t * +digest_path_from_path(const char **digest_path, + const char *fs_path, + const char *path, + apr_pool_t *pool) +{ + const char *digest; + SVN_ERR(make_digest(&digest, path, pool)); + *digest_path = svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, + apr_pstrmemdup(pool, digest, + DIGEST_SUBDIR_LEN), + digest, SVN_VA_NULL); + return SVN_NO_ERROR; +} + + +/* Write to DIGEST_PATH a representation of CHILDREN (which may be + empty, if the versioned path in FS represented by DIGEST_PATH has + no children) and LOCK (which may be NULL if that versioned path is + lock itself locked). Set the permissions of DIGEST_PATH to those of + PERMS_REFERENCE. Use POOL for temporary allocations. + */ +static svn_error_t * +write_digest_file(apr_hash_t *children, + svn_lock_t *lock, + const char *fs_path, + const char *digest_path, + const char *perms_reference, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = SVN_NO_ERROR; + svn_stream_t *stream; + apr_hash_index_t *hi; + apr_hash_t *hash = apr_hash_make(scratch_pool); + const char *tmp_path; + + SVN_ERR(svn_fs_x__ensure_dir_exists(svn_dirent_join(fs_path, PATH_LOCKS_DIR, + scratch_pool), + fs_path, scratch_pool)); + SVN_ERR(svn_fs_x__ensure_dir_exists(svn_dirent_dirname(digest_path, + scratch_pool), + fs_path, scratch_pool)); + + if (lock) + { + const char *creation_date = NULL, *expiration_date = NULL; + if (lock->creation_date) + creation_date = svn_time_to_cstring(lock->creation_date, + scratch_pool); + if (lock->expiration_date) + expiration_date = svn_time_to_cstring(lock->expiration_date, + scratch_pool); + + hash_store(hash, PATH_KEY, sizeof(PATH_KEY)-1, + lock->path, APR_HASH_KEY_STRING, scratch_pool); + hash_store(hash, TOKEN_KEY, sizeof(TOKEN_KEY)-1, + lock->token, APR_HASH_KEY_STRING, scratch_pool); + hash_store(hash, OWNER_KEY, sizeof(OWNER_KEY)-1, + lock->owner, APR_HASH_KEY_STRING, scratch_pool); + hash_store(hash, COMMENT_KEY, sizeof(COMMENT_KEY)-1, + lock->comment, APR_HASH_KEY_STRING, scratch_pool); + hash_store(hash, IS_DAV_COMMENT_KEY, sizeof(IS_DAV_COMMENT_KEY)-1, + lock->is_dav_comment ? "1" : "0", 1, scratch_pool); + hash_store(hash, CREATION_DATE_KEY, sizeof(CREATION_DATE_KEY)-1, + creation_date, APR_HASH_KEY_STRING, scratch_pool); + hash_store(hash, EXPIRATION_DATE_KEY, sizeof(EXPIRATION_DATE_KEY)-1, + expiration_date, APR_HASH_KEY_STRING, scratch_pool); + } + if (apr_hash_count(children)) + { + svn_stringbuf_t *children_list + = svn_stringbuf_create_empty(scratch_pool); + for (hi = apr_hash_first(scratch_pool, children); + hi; + hi = apr_hash_next(hi)) + { + svn_stringbuf_appendbytes(children_list, + apr_hash_this_key(hi), + apr_hash_this_key_len(hi)); + svn_stringbuf_appendbyte(children_list, '\n'); + } + hash_store(hash, CHILDREN_KEY, sizeof(CHILDREN_KEY)-1, + children_list->data, children_list->len, scratch_pool); + } + + SVN_ERR(svn_stream_open_unique(&stream, &tmp_path, + svn_dirent_dirname(digest_path, + scratch_pool), + svn_io_file_del_none, scratch_pool, + scratch_pool)); + if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, + scratch_pool))) + { + svn_error_clear(svn_stream_close(stream)); + return svn_error_createf(err->apr_err, + err, + _("Cannot write lock/entries hashfile '%s'"), + svn_dirent_local_style(tmp_path, + scratch_pool)); + } + + SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_io_file_rename(tmp_path, digest_path, scratch_pool)); + SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, scratch_pool)); + return SVN_NO_ERROR; +} + + +/* Parse the file at DIGEST_PATH, populating the lock LOCK_P in that + file (if it exists, and if *LOCK_P is non-NULL) and the hash of + CHILDREN_P (if any exist, and if *CHILDREN_P is non-NULL). Use POOL + for all allocations. */ +static svn_error_t * +read_digest_file(apr_hash_t **children_p, + svn_lock_t **lock_p, + const char *fs_path, + const char *digest_path, + apr_pool_t *pool) +{ + svn_error_t *err = SVN_NO_ERROR; + svn_lock_t *lock; + apr_hash_t *hash; + svn_stream_t *stream; + const char *val; + svn_node_kind_t kind; + + if (lock_p) + *lock_p = NULL; + if (children_p) + *children_p = apr_hash_make(pool); + + SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); + if (kind == svn_node_none) + return SVN_NO_ERROR; + + /* If our caller doesn't care about anything but the presence of the + file... whatever. */ + if (kind == svn_node_file && !lock_p && !children_p) + return SVN_NO_ERROR; + + SVN_ERR(svn_stream_open_readonly(&stream, digest_path, pool, pool)); + + hash = apr_hash_make(pool); + if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) + { + svn_error_clear(svn_stream_close(stream)); + return svn_error_createf(err->apr_err, + err, + _("Can't parse lock/entries hashfile '%s'"), + svn_dirent_local_style(digest_path, pool)); + } + SVN_ERR(svn_stream_close(stream)); + + /* If our caller cares, see if we have a lock path in our hash. If + so, we'll assume we have a lock here. */ + val = hash_fetch(hash, PATH_KEY); + if (val && lock_p) + { + const char *path = val; + + /* Create our lock and load it up. */ + lock = svn_lock_create(pool); + lock->path = path; + + if (! ((lock->token = hash_fetch(hash, TOKEN_KEY)))) + return svn_error_trace(err_corrupt_lockfile(fs_path, path)); + + if (! ((lock->owner = hash_fetch(hash, OWNER_KEY)))) + return svn_error_trace(err_corrupt_lockfile(fs_path, path)); + + if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY)))) + return svn_error_trace(err_corrupt_lockfile(fs_path, path)); + lock->is_dav_comment = (val[0] == '1'); + + if (! ((val = hash_fetch(hash, CREATION_DATE_KEY)))) + return svn_error_trace(err_corrupt_lockfile(fs_path, path)); + SVN_ERR(svn_time_from_cstring(&(lock->creation_date), val, pool)); + + if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY))) + SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), val, pool)); + + lock->comment = hash_fetch(hash, COMMENT_KEY); + + *lock_p = lock; + } + + /* If our caller cares, see if we have any children for this path. */ + val = hash_fetch(hash, CHILDREN_KEY); + if (val && children_p) + { + apr_array_header_t *kiddos = svn_cstring_split(val, "\n", FALSE, pool); + int i; + + for (i = 0; i < kiddos->nelts; i++) + { + svn_hash_sets(*children_p, APR_ARRAY_IDX(kiddos, i, const char *), + (void *)1); + } + } + return SVN_NO_ERROR; +} + + + +/*** Lock helper functions (path here are still FS paths, not on-disk + schema-supporting paths) ***/ + + +/* Write LOCK in FS to the actual OS filesystem. + + Use PERMS_REFERENCE for the permissions of any digest files. + */ +static svn_error_t * +set_lock(const char *fs_path, + svn_lock_t *lock, + const char *perms_reference, + apr_pool_t *scratch_pool) +{ + const char *digest_path; + apr_hash_t *children; + + SVN_ERR(digest_path_from_path(&digest_path, fs_path, lock->path, + scratch_pool)); + + /* We could get away without reading the file as children should + always come back empty. */ + SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, + scratch_pool)); + + SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, + perms_reference, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +delete_lock(const char *fs_path, + const char *path, + apr_pool_t *scratch_pool) +{ + const char *digest_path; + + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, scratch_pool)); + + SVN_ERR(svn_io_remove_file2(digest_path, TRUE, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +add_to_digest(const char *fs_path, + apr_array_header_t *paths, + const char *index_path, + const char *perms_reference, + apr_pool_t *scratch_pool) +{ + const char *index_digest_path; + apr_hash_t *children; + svn_lock_t *lock; + int i; + unsigned int original_count; + + SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, + scratch_pool)); + SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, + scratch_pool)); + + original_count = apr_hash_count(children); + + for (i = 0; i < paths->nelts; ++i) + { + const char *path = APR_ARRAY_IDX(paths, i, const char *); + const char *digest_path, *digest_file; + + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, + scratch_pool)); + digest_file = svn_dirent_basename(digest_path, NULL); + svn_hash_sets(children, digest_file, (void *)1); + } + + if (apr_hash_count(children) != original_count) + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + perms_reference, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +delete_from_digest(const char *fs_path, + apr_array_header_t *paths, + const char *index_path, + const char *perms_reference, + apr_pool_t *scratch_pool) +{ + const char *index_digest_path; + apr_hash_t *children; + svn_lock_t *lock; + int i; + + SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, + scratch_pool)); + SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, + scratch_pool)); + + for (i = 0; i < paths->nelts; ++i) + { + const char *path = APR_ARRAY_IDX(paths, i, const char *); + const char *digest_path, *digest_file; + + SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, + scratch_pool)); + digest_file = svn_dirent_basename(digest_path, NULL); + svn_hash_sets(children, digest_file, NULL); + } + + if (apr_hash_count(children) || lock) + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + perms_reference, scratch_pool)); + else + SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +unlock_single(svn_fs_t *fs, + svn_lock_t *lock, + apr_pool_t *pool); + +/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be + TRUE if the caller (or one of its callers) has taken out the + repository-wide write lock, FALSE otherwise. If MUST_EXIST is + not set, the function will simply return NULL in *LOCK_P instead + of creating an SVN_FS__ERR_NO_SUCH_LOCK error in case the lock + was not found (much faster). Use POOL for allocations. */ +static svn_error_t * +get_lock(svn_lock_t **lock_p, + svn_fs_t *fs, + const char *path, + svn_boolean_t have_write_lock, + svn_boolean_t must_exist, + apr_pool_t *pool) +{ + svn_lock_t *lock = NULL; + const char *digest_path; + svn_node_kind_t kind; + + SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); + SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); + + *lock_p = NULL; + if (kind != svn_node_none) + SVN_ERR(read_digest_file(NULL, &lock, fs->path, digest_path, pool)); + + if (! lock) + return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; + + /* Don't return an expired lock. */ + if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) + { + /* Only remove the lock if we have the write lock. + Read operations shouldn't change the filesystem. */ + if (have_write_lock) + SVN_ERR(unlock_single(fs, lock, pool)); + return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); + } + + *lock_p = lock; + return SVN_NO_ERROR; +} + + +/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be + TRUE if the caller (or one of its callers) has taken out the + repository-wide write lock, FALSE otherwise. Use POOL for + allocations. */ +static svn_error_t * +get_lock_helper(svn_fs_t *fs, + svn_lock_t **lock_p, + const char *path, + svn_boolean_t have_write_lock, + apr_pool_t *pool) +{ + svn_lock_t *lock; + svn_error_t *err; + + err = get_lock(&lock, fs, path, have_write_lock, FALSE, pool); + + /* We've deliberately decided that this function doesn't tell the + caller *why* the lock is unavailable. */ + if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK) + || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED))) + { + svn_error_clear(err); + *lock_p = NULL; + return SVN_NO_ERROR; + } + else + SVN_ERR(err); + + *lock_p = lock; + return SVN_NO_ERROR; +} + + +/* Baton for locks_walker(). */ +typedef struct walk_locks_baton_t +{ + svn_fs_get_locks_callback_t get_locks_func; + void *get_locks_baton; + svn_fs_t *fs; +} walk_locks_baton_t; + +/* Implements walk_digests_callback_t. */ +static svn_error_t * +locks_walker(void *baton, + const char *fs_path, + const char *digest_path, + svn_lock_t *lock, + svn_boolean_t have_write_lock, + apr_pool_t *pool) +{ + walk_locks_baton_t *wlb = baton; + + if (lock) + { + /* Don't report an expired lock. */ + if (lock->expiration_date == 0 + || (apr_time_now() <= lock->expiration_date)) + { + if (wlb->get_locks_func) + SVN_ERR(wlb->get_locks_func(wlb->get_locks_baton, lock, pool)); + } + else + { + /* Only remove the lock if we have the write lock. + Read operations shouldn't change the filesystem. */ + if (have_write_lock) + SVN_ERR(unlock_single(wlb->fs, lock, pool)); + } + } + + return SVN_NO_ERROR; +} + +/* Callback type for walk_digest_files(). + * + * LOCK come from a read_digest_file(digest_path) call. + */ +typedef svn_error_t *(*walk_digests_callback_t)(void *baton, + const char *fs_path, + const char *digest_path, + svn_lock_t *lock, + svn_boolean_t have_write_lock, + apr_pool_t *pool); + +/* A function that calls WALK_DIGESTS_FUNC/WALK_DIGESTS_BATON for + all lock digest files in and under PATH in FS. + HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) + has the FS write lock. */ +static svn_error_t * +walk_digest_files(const char *fs_path, + const char *digest_path, + walk_digests_callback_t walk_digests_func, + void *walk_digests_baton, + svn_boolean_t have_write_lock, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + apr_hash_t *children; + apr_pool_t *subpool; + svn_lock_t *lock; + + /* First, send up any locks in the current digest file. */ + SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool)); + + SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock, + have_write_lock, pool)); + + /* Now, report all the child entries (if any; bail otherwise). */ + if (! apr_hash_count(children)) + return SVN_NO_ERROR; + subpool = svn_pool_create(pool); + for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) + { + const char *digest = apr_hash_this_key(hi); + svn_pool_clear(subpool); + + SVN_ERR(read_digest_file + (NULL, &lock, fs_path, + digest_path_from_digest(fs_path, digest, subpool), subpool)); + + SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock, + have_write_lock, subpool)); + } + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + +/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for + all locks in and under PATH in FS. + HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) + has the FS write lock. */ +static svn_error_t * +walk_locks(svn_fs_t *fs, + const char *digest_path, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, + svn_boolean_t have_write_lock, + apr_pool_t *pool) +{ + walk_locks_baton_t wlb; + + wlb.get_locks_func = get_locks_func; + wlb.get_locks_baton = get_locks_baton; + wlb.fs = fs; + SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb, + have_write_lock, pool)); + return SVN_NO_ERROR; +} + + +/* Utility function: verify that a lock can be used. Interesting + errors returned from this function: + + SVN_ERR_FS_NO_USER: No username attached to FS. + SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner. + SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK. + */ +static svn_error_t * +verify_lock(svn_fs_t *fs, + svn_lock_t *lock) +{ + if ((! fs->access_ctx) || (! fs->access_ctx->username)) + return svn_error_createf + (SVN_ERR_FS_NO_USER, NULL, + _("Cannot verify lock on path '%s'; no username available"), + lock->path); + + else if (strcmp(fs->access_ctx->username, lock->owner) != 0) + return svn_error_createf + (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL, + _("User '%s' does not own lock on path '%s' (currently locked by '%s')"), + fs->access_ctx->username, lock->path, lock->owner); + + else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL) + return svn_error_createf + (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Cannot verify lock on path '%s'; no matching lock-token available"), + lock->path); + + return SVN_NO_ERROR; +} + + +/* This implements the svn_fs_get_locks_callback_t interface, where + BATON is just an svn_fs_t object. */ +static svn_error_t * +get_locks_callback(void *baton, + svn_lock_t *lock, + apr_pool_t *pool) +{ + return verify_lock(baton, lock); +} + + +/* The main routine for lock enforcement, used throughout libsvn_fs_x. */ +svn_error_t * +svn_fs_x__allow_locked_operation(const char *path, + svn_fs_t *fs, + svn_boolean_t recurse, + svn_boolean_t have_write_lock, + apr_pool_t *scratch_pool) +{ + path = svn_fs__canonicalize_abspath(path, scratch_pool); + if (recurse) + { + /* Discover all locks at or below the path. */ + const char *digest_path; + SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, + scratch_pool)); + SVN_ERR(walk_locks(fs, digest_path, get_locks_callback, + fs, have_write_lock, scratch_pool)); + } + else + { + /* Discover and verify any lock attached to the path. */ + svn_lock_t *lock; + SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, + scratch_pool)); + if (lock) + SVN_ERR(verify_lock(fs, lock)); + } + return SVN_NO_ERROR; +} + +/* The effective arguments for lock_body() below. */ +typedef struct lock_baton_t { + svn_fs_t *fs; + apr_array_header_t *targets; + apr_array_header_t *infos; + const char *comment; + svn_boolean_t is_dav_comment; + apr_time_t expiration_date; + svn_boolean_t steal_lock; + apr_pool_t *result_pool; +} lock_baton_t; + +static svn_error_t * +check_lock(svn_error_t **fs_err, + const char *path, + const svn_fs_lock_target_t *target, + lock_baton_t *lb, + svn_fs_root_t *root, + svn_revnum_t youngest_rev, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + svn_lock_t *existing_lock; + + *fs_err = SVN_NO_ERROR; + + SVN_ERR(svn_fs_x__check_path(&kind, root, path, pool)); + if (kind == svn_node_dir) + { + *fs_err = SVN_FS__ERR_NOT_FILE(lb->fs, path); + return SVN_NO_ERROR; + } + + /* While our locking implementation easily supports the locking of + nonexistent paths, we deliberately choose not to allow such madness. */ + if (kind == svn_node_none) + { + if (SVN_IS_VALID_REVNUM(target->current_rev)) + *fs_err = svn_error_createf( + SVN_ERR_FS_OUT_OF_DATE, NULL, + _("Path '%s' doesn't exist in HEAD revision"), + path); + else + *fs_err = svn_error_createf( + SVN_ERR_FS_NOT_FOUND, NULL, + _("Path '%s' doesn't exist in HEAD revision"), + path); + + return SVN_NO_ERROR; + } + + /* Is the caller attempting to lock an out-of-date working file? */ + if (SVN_IS_VALID_REVNUM(target->current_rev)) + { + svn_revnum_t created_rev; + + if (target->current_rev > youngest_rev) + { + *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), + target->current_rev); + return SVN_NO_ERROR; + } + + SVN_ERR(svn_fs_x__node_created_rev(&created_rev, root, path, + pool)); + + /* SVN_INVALID_REVNUM means the path doesn't exist. So + apparently somebody is trying to lock something in their + working copy, but somebody else has deleted the thing + from HEAD. That counts as being 'out of date'. */ + if (! SVN_IS_VALID_REVNUM(created_rev)) + { + *fs_err = svn_error_createf + (SVN_ERR_FS_OUT_OF_DATE, NULL, + _("Path '%s' doesn't exist in HEAD revision"), path); + + return SVN_NO_ERROR; + } + + if (target->current_rev < created_rev) + { + *fs_err = svn_error_createf + (SVN_ERR_FS_OUT_OF_DATE, NULL, + _("Lock failed: newer version of '%s' exists"), path); + + return SVN_NO_ERROR; + } + } + + /* If the caller provided a TOKEN, we *really* need to see + if a lock already exists with that token, and if so, verify that + the lock's path matches PATH. Otherwise we run the risk of + breaking the 1-to-1 mapping of lock tokens to locked paths. */ + /* ### TODO: actually do this check. This is tough, because the + schema doesn't supply a lookup-by-token mechanism. */ + + /* Is the path already locked? + + Note that this next function call will automatically ignore any + errors about {the path not existing as a key, the path's token + not existing as a key, the lock just having been expired}. And + that's totally fine. Any of these three errors are perfectly + acceptable to ignore; it means that the path is now free and + clear for locking, because the fsx funcs just cleared out both + of the tables for us. */ + SVN_ERR(get_lock_helper(lb->fs, &existing_lock, path, TRUE, pool)); + if (existing_lock) + { + if (! lb->steal_lock) + { + /* Sorry, the path is already locked. */ + *fs_err = SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock); + return SVN_NO_ERROR; + } + } + + return SVN_NO_ERROR; +} + +typedef struct lock_info_t { + const char *path; + const char *component; + svn_lock_t *lock; + svn_error_t *fs_err; +} lock_info_t; + +/* The body of svn_fs_x__lock(), which see. + + BATON is a 'lock_baton_t *' holding the effective arguments. + BATON->targets is an array of 'svn_sort__item_t' targets, sorted by + path, mapping canonical path to 'svn_fs_lock_target_t'. Set + BATON->infos to an array of 'lock_info_t' holding the results. For + the other arguments, see svn_fs_lock_many(). + + This implements the svn_fs_x__with_write_lock() 'body' callback + type, and assumes that the write lock is held. + */ +static svn_error_t * +lock_body(void *baton, apr_pool_t *pool) +{ + lock_baton_t *lb = baton; + svn_fs_root_t *root; + svn_revnum_t youngest; + const char *rev_0_path; + int i, outstanding = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + + lb->infos = apr_array_make(lb->result_pool, lb->targets->nelts, + sizeof(lock_info_t)); + + /* Until we implement directory locks someday, we only allow locks + on files or non-existent paths. */ + /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular + library dependencies, which are not portable. */ + SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); + SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); + + for (i = 0; i < lb->targets->nelts; ++i) + { + const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, + svn_sort__item_t); + const svn_fs_lock_target_t *target = item->value; + lock_info_t info; + + svn_pool_clear(iterpool); + + info.path = item->key; + SVN_ERR(check_lock(&info.fs_err, info.path, target, lb, root, + youngest, iterpool)); + info.lock = NULL; + info.component = NULL; + APR_ARRAY_PUSH(lb->infos, lock_info_t) = info; + if (!info.fs_err) + ++outstanding; + } + + rev_0_path = svn_fs_x__path_rev_absolute(lb->fs, 0, pool); + + /* Given the paths: + + /foo/bar/f + /foo/bar/g + /zig/x + + we loop through repeatedly. The first pass sees '/' on all paths + and writes the '/' index. The second pass sees '/foo' twice and + writes that index followed by '/zig' and that index. The third + pass sees '/foo/bar' twice and writes that index, and then writes + the lock for '/zig/x'. The fourth pass writes the locks for + '/foo/bar/f' and '/foo/bar/g'. + + Writing indices before locks is correct: if interrupted it leaves + indices without locks rather than locks without indices. An + index without a lock is consistent in that it always shows up as + unlocked in svn_fs_x__allow_locked_operation. A lock without an + index is inconsistent, svn_fs_x__allow_locked_operation will + show locked on the file but unlocked on the parent. */ + + + while (outstanding) + { + const char *last_path = NULL; + apr_array_header_t *paths; + + svn_pool_clear(iterpool); + paths = apr_array_make(iterpool, 1, sizeof(const char *)); + + for (i = 0; i < lb->infos->nelts; ++i) + { + lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, lock_info_t); + const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, + svn_sort__item_t); + const svn_fs_lock_target_t *target = item->value; + + if (!info->fs_err && !info->lock) + { + if (!info->component) + { + info->component = info->path; + APR_ARRAY_PUSH(paths, const char *) = info->path; + last_path = "/"; + } + else + { + info->component = strchr(info->component + 1, '/'); + if (!info->component) + { + /* The component is a path to lock, this cannot + match a previous path that need to be indexed. */ + if (paths->nelts) + { + SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, + rev_0_path, iterpool)); + apr_array_clear(paths); + last_path = NULL; + } + + info->lock = svn_lock_create(lb->result_pool); + if (target->token) + info->lock->token = target->token; + else + SVN_ERR(svn_fs_x__generate_lock_token( + &(info->lock->token), lb->fs, + lb->result_pool)); + info->lock->path = info->path; + info->lock->owner = lb->fs->access_ctx->username; + info->lock->comment = lb->comment; + info->lock->is_dav_comment = lb->is_dav_comment; + info->lock->creation_date = apr_time_now(); + info->lock->expiration_date = lb->expiration_date; + + info->fs_err = set_lock(lb->fs->path, info->lock, + rev_0_path, iterpool); + --outstanding; + } + else + { + /* The component is a path to an index. */ + apr_size_t len = info->component - info->path; + + if (last_path + && (strncmp(last_path, info->path, len) + || strlen(last_path) != len)) + { + /* No match to the previous paths to index. */ + SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, + rev_0_path, iterpool)); + apr_array_clear(paths); + last_path = NULL; + } + APR_ARRAY_PUSH(paths, const char *) = info->path; + if (!last_path) + last_path = apr_pstrndup(iterpool, info->path, len); + } + } + } + + if (last_path && i == lb->infos->nelts - 1) + SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, + rev_0_path, iterpool)); + } + } + + return SVN_NO_ERROR; +} + +/* The effective arguments for unlock_body() below. */ +typedef struct unlock_baton_t { + svn_fs_t *fs; + apr_array_header_t *targets; + apr_array_header_t *infos; + /* Set skip_check TRUE to prevent the checks that set infos[].fs_err. */ + svn_boolean_t skip_check; + svn_boolean_t break_lock; + apr_pool_t *result_pool; +} unlock_baton_t; + +static svn_error_t * +check_unlock(svn_error_t **fs_err, + const char *path, + const char *token, + unlock_baton_t *ub, + svn_fs_root_t *root, + apr_pool_t *pool) +{ + svn_lock_t *lock; + + *fs_err = get_lock(&lock, ub->fs, path, TRUE, TRUE, pool); + if (!*fs_err && !ub->break_lock) + { + if (strcmp(token, lock->token) != 0) + *fs_err = SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, path); + else if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0) + *fs_err = SVN_FS__ERR_LOCK_OWNER_MISMATCH(ub->fs, + ub->fs->access_ctx->username, + lock->owner); + } + + return SVN_NO_ERROR; +} + +typedef struct unlock_info_t { + const char *path; + const char *component; + svn_error_t *fs_err; + svn_boolean_t done; + int components; +} unlock_info_t; + +/* The body of svn_fs_x__unlock(), which see. + + BATON is a 'unlock_baton_t *' holding the effective arguments. + BATON->targets is an array of 'svn_sort__item_t' targets, sorted by + path, mapping canonical path to (const char *) token. Set + BATON->infos to an array of 'unlock_info_t' results. For the other + arguments, see svn_fs_unlock_many(). + + This implements the svn_fs_x__with_write_lock() 'body' callback + type, and assumes that the write lock is held. + */ +static svn_error_t * +unlock_body(void *baton, apr_pool_t *pool) +{ + unlock_baton_t *ub = baton; + svn_fs_root_t *root; + svn_revnum_t youngest; + const char *rev_0_path; + int i, max_components = 0, outstanding = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + + ub->infos = apr_array_make(ub->result_pool, ub->targets->nelts, + sizeof( unlock_info_t)); + + SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool)); + SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool)); + + for (i = 0; i < ub->targets->nelts; ++i) + { + const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i, + svn_sort__item_t); + const char *token = item->value; + unlock_info_t info = { 0 }; + + svn_pool_clear(iterpool); + + info.path = item->key; + if (!ub->skip_check) + SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root, + iterpool)); + if (!info.fs_err) + { + const char *s; + + info.components = 1; + info.component = info.path; + while((s = strchr(info.component + 1, '/'))) + { + info.component = s; + ++info.components; + } + + if (info.components > max_components) + max_components = info.components; + + ++outstanding; + } + APR_ARRAY_PUSH(ub->infos, unlock_info_t) = info; + } + + rev_0_path = svn_fs_x__path_rev_absolute(ub->fs, 0, pool); + + for (i = max_components; i >= 0; --i) + { + const char *last_path = NULL; + apr_array_header_t *paths; + int j; + + svn_pool_clear(iterpool); + paths = apr_array_make(pool, 1, sizeof(const char *)); + + for (j = 0; j < ub->infos->nelts; ++j) + { + unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, j, unlock_info_t); + + if (!info->fs_err && info->path) + { + + if (info->components == i) + { + SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool)); + info->done = TRUE; + } + else if (info->components > i) + { + apr_size_t len = info->component - info->path; + + if (last_path + && strcmp(last_path, "/") + && (strncmp(last_path, info->path, len) + || strlen(last_path) != len)) + { + SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path, + rev_0_path, iterpool)); + apr_array_clear(paths); + last_path = NULL; + } + APR_ARRAY_PUSH(paths, const char *) = info->path; + if (!last_path) + { + if (info->component > info->path) + last_path = apr_pstrndup(pool, info->path, len); + else + last_path = "/"; + } + + if (info->component > info->path) + { + --info->component; + while(info->component[0] != '/') + --info->component; + } + } + } + + if (last_path && j == ub->infos->nelts - 1) + SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path, + rev_0_path, iterpool)); + } + } + + return SVN_NO_ERROR; +} + +/* Unlock the lock described by LOCK->path and LOCK->token in FS. + + This assumes that the write lock is held. + */ +static svn_error_t * +unlock_single(svn_fs_t *fs, + svn_lock_t *lock, + apr_pool_t *scratch_pool) +{ + unlock_baton_t ub; + svn_sort__item_t item; + apr_array_header_t *targets = apr_array_make(scratch_pool, 1, + sizeof(svn_sort__item_t)); + item.key = lock->path; + item.klen = strlen(item.key); + item.value = (char*)lock->token; + APR_ARRAY_PUSH(targets, svn_sort__item_t) = item; + + ub.fs = fs; + ub.targets = targets; + ub.skip_check = TRUE; + ub.result_pool = scratch_pool; + + /* No ub.infos[].fs_err error because skip_check is TRUE. */ + SVN_ERR(unlock_body(&ub, scratch_pool)); + + return SVN_NO_ERROR; +} + + +/*** Public API implementations ***/ + +svn_error_t * +svn_fs_x__lock(svn_fs_t *fs, + apr_hash_t *targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + lock_baton_t lb; + apr_array_header_t *sorted_targets; + apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool; + svn_error_t *err, *cb_err = SVN_NO_ERROR; + int i; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + + /* We need to have a username attached to the fs. */ + if (!fs->access_ctx || !fs->access_ctx->username) + return SVN_FS__ERR_NO_USER(fs); + + /* The FS locking API allows both canonical and non-canonical + paths which means that the same canonical path could be + represented more than once in the TARGETS hash. We just keep + one, choosing one with a token if possible. */ + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + const svn_fs_lock_target_t *target = apr_hash_this_val(hi); + const svn_fs_lock_target_t *other; + + path = svn_fspath__canonicalize(path, result_pool); + other = svn_hash_gets(canonical_targets, path); + + if (!other || (!other->token && target->token)) + svn_hash_sets(canonical_targets, path, target); + } + + sorted_targets = svn_sort__hash(canonical_targets, + svn_sort_compare_items_as_paths, + scratch_pool); + + lb.fs = fs; + lb.targets = sorted_targets; + lb.comment = comment; + lb.is_dav_comment = is_dav_comment; + lb.expiration_date = expiration_date; + lb.steal_lock = steal_lock; + lb.result_pool = result_pool; + + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_x__with_write_lock(fs, lock_body, &lb, iterpool); + for (i = 0; i < lb.infos->nelts; ++i) + { + struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i, + struct lock_info_t); + svn_pool_clear(iterpool); + if (!cb_err && lock_callback) + { + if (!info->lock && !info->fs_err) + info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, + 0, _("Failed to lock '%s'"), + info->path); + + cb_err = lock_callback(lock_baton, info->path, info->lock, + info->fs_err, iterpool); + } + svn_error_clear(info->fs_err); + } + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; + + return svn_error_trace(err); +} + + +svn_error_t * +svn_fs_x__generate_lock_token(const char **token, + svn_fs_t *fs, + apr_pool_t *pool) +{ + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + + /* Notice that 'fs' is currently unused. But perhaps someday, we'll + want to use the fs UUID + some incremented number? For now, we + generate a URI that matches the DAV RFC. We could change this to + some other URI scheme someday, if we wish. */ + *token = apr_pstrcat(pool, "opaquelocktoken:", + svn_uuid_generate(pool), SVN_VA_NULL); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__unlock(svn_fs_t *fs, + apr_hash_t *targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + unlock_baton_t ub; + apr_array_header_t *sorted_targets; + apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool; + svn_error_t *err, *cb_err = SVN_NO_ERROR; + int i; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + + /* We need to have a username attached to the fs. */ + if (!fs->access_ctx || !fs->access_ctx->username) + return SVN_FS__ERR_NO_USER(fs); + + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); + const char *other; + + path = svn_fspath__canonicalize(path, result_pool); + other = svn_hash_gets(canonical_targets, path); + + if (!other) + svn_hash_sets(canonical_targets, path, token); + } + + sorted_targets = svn_sort__hash(canonical_targets, + svn_sort_compare_items_as_paths, + scratch_pool); + + ub.fs = fs; + ub.targets = sorted_targets; + ub.skip_check = FALSE; + ub.break_lock = break_lock; + ub.result_pool = result_pool; + + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_x__with_write_lock(fs, unlock_body, &ub, iterpool); + for (i = 0; i < ub.infos->nelts; ++i) + { + unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i, unlock_info_t); + svn_pool_clear(iterpool); + if (!cb_err && lock_callback) + { + if (!info->done && !info->fs_err) + info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, + 0, _("Failed to unlock '%s'"), + info->path); + cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err, + iterpool); + } + svn_error_clear(info->fs_err); + } + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; + + return svn_error_trace(err); +} + + +svn_error_t * +svn_fs_x__get_lock(svn_lock_t **lock_p, + svn_fs_t *fs, + const char *path, + apr_pool_t *pool) +{ + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + path = svn_fs__canonicalize_abspath(path, pool); + return get_lock_helper(fs, lock_p, path, FALSE, pool); +} + + +/* Baton for get_locks_filter_func(). */ +typedef struct get_locks_filter_baton_t +{ + const char *path; + svn_depth_t requested_depth; + svn_fs_get_locks_callback_t get_locks_func; + void *get_locks_baton; + +} get_locks_filter_baton_t; + + +/* A wrapper for the GET_LOCKS_FUNC passed to svn_fs_x__get_locks() + which filters out locks on paths that aren't within + BATON->requested_depth of BATON->path before called + BATON->get_locks_func() with BATON->get_locks_baton. + + NOTE: See issue #3660 for details about how the FSX lock + management code is inconsistent. Until that inconsistency is + resolved, we take this filtering approach rather than honoring + depth requests closer to the crawling code. In other words, once + we decide how to resolve issue #3660, there might be a more + performant way to honor the depth passed to svn_fs_x__get_locks(). */ +static svn_error_t * +get_locks_filter_func(void *baton, + svn_lock_t *lock, + apr_pool_t *pool) +{ + get_locks_filter_baton_t *b = baton; + + /* Filter out unwanted paths. Since Subversion only allows + locks on files, we can treat depth=immediates the same as + depth=files for filtering purposes. Meaning, we'll keep + this lock if: + + a) its path is the very path we queried, or + b) we've asked for a fully recursive answer, or + c) we've asked for depth=files or depth=immediates, and this + lock is on an immediate child of our query path. + */ + if ((strcmp(b->path, lock->path) == 0) + || (b->requested_depth == svn_depth_infinity)) + { + SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); + } + else if ((b->requested_depth == svn_depth_files) || + (b->requested_depth == svn_depth_immediates)) + { + const char *rel_uri = svn_fspath__skip_ancestor(b->path, lock->path); + if (rel_uri && (svn_path_component_count(rel_uri) == 1)) + SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_locks(svn_fs_t *fs, + const char *path, + svn_depth_t depth, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, + apr_pool_t *scratch_pool) +{ + const char *digest_path; + get_locks_filter_baton_t glfb; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + path = svn_fs__canonicalize_abspath(path, scratch_pool); + + glfb.path = path; + glfb.requested_depth = depth; + glfb.get_locks_func = get_locks_func; + glfb.get_locks_baton = get_locks_baton; + + /* Get the top digest path in our tree of interest, and then walk it. */ + SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, scratch_pool)); + SVN_ERR(walk_locks(fs, digest_path, get_locks_filter_func, &glfb, + FALSE, scratch_pool)); + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/lock.h b/contrib/subversion/subversion/libsvn_fs_x/lock.h new file mode 100644 index 000000000..1db5eb716 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/lock.h @@ -0,0 +1,116 @@ +/* lock.h : internal interface to lock functions + * + * ==================================================================== + * 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_FS_LOCK_H +#define SVN_LIBSVN_FS_LOCK_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* These functions implement some of the calls in the FS loader + library's fs vtables. */ + +/* See svn_fs_lock(), svn_fs_lock_many(). */ +svn_error_t * +svn_fs_x__lock(svn_fs_t *fs, + apr_hash_t *targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* See svn_fs_generate_lock_token(). */ +svn_error_t * +svn_fs_x__generate_lock_token(const char **token, + svn_fs_t *fs, + apr_pool_t *pool); + +/* See svn_fs_unlock(), svn_fs_unlock_many(). */ +svn_error_t * +svn_fs_x__unlock(svn_fs_t *fs, + apr_hash_t *targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* See svn_fs_get_lock(). */ +svn_error_t * +svn_fs_x__get_lock(svn_lock_t **lock, + svn_fs_t *fs, + const char *path, + apr_pool_t *pool); + +/* See svn_fs_get_locks2(). */ +svn_error_t * +svn_fs_x__get_locks(svn_fs_t *fs, + const char *path, + svn_depth_t depth, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, + apr_pool_t *scratch_pool); + + +/* Examine PATH for existing locks, and check whether they can be + used. Use SCRATCH_POOL for temporary allocations. + + If no locks are present, return SVN_NO_ERROR. + + If PATH is locked (or contains locks "below" it, when RECURSE is + set), then verify that: + + 1. a username has been supplied to TRAIL->fs's access-context, + else return SVN_ERR_FS_NO_USER. + + 2. for every lock discovered, the current username in the access + context of TRAIL->fs matches the "owner" of the lock, else + return SVN_ERR_FS_LOCK_OWNER_MISMATCH. + + 3. for every lock discovered, a matching lock token has been + passed into TRAIL->fs's access-context, else return + SVN_ERR_FS_BAD_LOCK_TOKEN. + + If all three conditions are met, return SVN_NO_ERROR. + + If the caller (directly or indirectly) has the FS write lock, + HAVE_WRITE_LOCK should be true. +*/ +svn_error_t * +svn_fs_x__allow_locked_operation(const char *path, + svn_fs_t *fs, + svn_boolean_t recurse, + svn_boolean_t have_write_lock, + apr_pool_t *scratch_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_LOCK_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/low_level.c b/contrib/subversion/subversion/libsvn_fs_x/low_level.c new file mode 100644 index 000000000..76f3fd2f3 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/low_level.c @@ -0,0 +1,1123 @@ +/* low_level.c --- low level r/w access to fs_x file structures + * + * ==================================================================== + * 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_sorts.h" +#include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_fspath.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "low_level.h" +#include "util.h" +#include "pack.h" +#include "cached_data.h" + +/* Headers used to describe node-revision in the revision file. */ +#define HEADER_ID "id" +#define HEADER_NODE "node" +#define HEADER_COPY "copy" +#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_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 of representation. */ +#define REP_DELTA "DELTA" + +/* An arbitrary maximum path length, so clients can't run us out of memory + * by giving us arbitrarily large paths. */ +#define FSX_MAX_PATH_LEN 4096 + +/* The 256 is an arbitrary size large enough to hold the node id and the + * various flags. */ +#define MAX_CHANGE_LINE_LEN FSX_MAX_PATH_LEN + 256 + +/* Convert the C string in *TEXT to a revision number and return it in *REV. + * Overflows, negative values other than -1 and terminating characters other + * than 0x20 or 0x0 will cause an error. Set *TEXT to the first char after + * the initial separator or to EOS. + */ +static svn_error_t * +parse_revnum(svn_revnum_t *rev, + const char **text) +{ + const char *string = *text; + if ((string[0] == '-') && (string[1] == '1')) + { + *rev = SVN_INVALID_REVNUM; + string += 2; + } + else + { + SVN_ERR(svn_revnum_parse(rev, string, &string)); + } + + if (*string == ' ') + ++string; + else if (*string != '\0') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid character in revision number")); + + *text = string; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + apr_int64_t val; + char *last_str = footer->data; + + /* Get the L2P offset. */ + const char *str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_cstring_atoi64(&val, str)); + *l2p_offset = (apr_off_t)val; + + /* Get the L2P checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, + result_pool)); + + /* Get the P2L offset. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_cstring_atoi64(&val, str)); + *p2l_offset = (apr_off_t)val; + + /* Get the P2L checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, + result_pool)); + + return SVN_NO_ERROR; +} + +svn_stringbuf_t * +svn_fs_x__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_stringbuf_createf(result_pool, + "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s", + l2p_offset, + svn_checksum_to_cstring(l2p_checksum, + scratch_pool), + p2l_offset, + svn_checksum_to_cstring(p2l_checksum, + scratch_pool)); +} + +/* 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 + RESULT_POOL. */ +static svn_error_t * +read_header_block(apr_hash_t **headers, + svn_stream_t *stream, + apr_pool_t *result_pool) +{ + *headers = svn_hash__make(result_pool); + + while (1) + { + svn_stringbuf_t *header_str; + const char *name, *value; + apr_size_t i = 0; + apr_size_t name_len; + svn_boolean_t eof; + + SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, + result_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; + name_len = i; + + /* Check if we have enough data to parse. */ + if (i + 2 > header_str->len) + { + /* Restore the original line for the error. */ + header_str->data[i] = ':'; + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Found malformed header '%s' in " + "revision file"), + header_str->data); + } + + /* Skip over the NULL byte and the space following it. */ + i += 2; + + value = header_str->data + i; + + /* header_str is safely in our pool, so we can use bits of it as + key and value. */ + apr_hash_set(*headers, name, name_len, value); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p, + svn_stringbuf_t *text, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_t *rep; + char *str; + apr_int64_t val; + char *string = text->data; + svn_checksum_t *checksum; + + rep = apr_pcalloc(result_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")); + + SVN_ERR(svn_cstring_atoi64(&rep->id.change_set, str)); + + /* while in transactions, it is legal to simply write "-1" */ + if (rep->id.change_set == -1) + return SVN_NO_ERROR; + + str = svn_cstring_tokenize(" ", &string); + if (str == NULL) + { + if (rep->id.change_set == SVN_FS_X__INVALID_CHANGE_SET) + return SVN_NO_ERROR; + + 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->id.number = (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(&checksum, svn_checksum_md5, str, + scratch_pool)); + if (checksum) + memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest)); + + /* 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(&checksum, svn_checksum_sha1, str, + scratch_pool)); + rep->has_sha1 = checksum != NULL; + if (checksum) + memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); + + 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(svn_fs_x__representation_t **rep_p, + char *string, + const svn_fs_x__id_t *noderev_id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err + = svn_fs_x__parse_representation(rep_p, + svn_stringbuf_create_wrap(string, + scratch_pool), + result_pool, + scratch_pool); + if (err) + { + const svn_string_t *id_unparsed; + const char *where; + + id_unparsed = svn_fs_x__id_unparse(noderev_id, scratch_pool); + where = apr_psprintf(scratch_pool, + _("While reading representation offsets " + "for node-revision '%s':"), + id_unparsed->data); + + return svn_error_quick_wrap(err, where); + } + + return SVN_NO_ERROR; +} + +/* If PATH needs to be escaped, return an escaped version of it, allocated + * from RESULT_POOL. Otherwise, return PATH directly. */ +static const char * +auto_escape_path(const char *path, + apr_pool_t *result_pool) +{ + apr_size_t len = strlen(path); + apr_size_t i; + const char esc = '\x1b'; + + for (i = 0; i < len; ++i) + if (path[i] < ' ') + { + svn_stringbuf_t *escaped = svn_stringbuf_create_ensure(2 * len, + result_pool); + for (i = 0; i < len; ++i) + if (path[i] < ' ') + { + svn_stringbuf_appendbyte(escaped, esc); + svn_stringbuf_appendbyte(escaped, path[i] + 'A' - 1); + } + else + { + svn_stringbuf_appendbyte(escaped, path[i]); + } + + return escaped->data; + } + + return path; +} + +/* If PATH has been escaped, return the un-escaped version of it, allocated + * from RESULT_POOL. Otherwise, return PATH directly. */ +static const char * +auto_unescape_path(const char *path, + apr_pool_t *result_pool) +{ + const char esc = '\x1b'; + if (strchr(path, esc)) + { + apr_size_t len = strlen(path); + apr_size_t i; + + svn_stringbuf_t *unescaped = svn_stringbuf_create_ensure(len, + result_pool); + for (i = 0; i < len; ++i) + if (path[i] == esc) + svn_stringbuf_appendbyte(unescaped, path[++i] + 1 - 'A'); + else + svn_stringbuf_appendbyte(unescaped, path[i]); + + return unescaped->data; + } + + return path; +} + +/* Find entry HEADER_NAME in HEADERS and parse its value into *ID. */ +static svn_error_t * +read_id_part(svn_fs_x__id_t *id, + apr_hash_t *headers, + const char *header_name) +{ + const char *value = svn_hash_gets(headers, header_name); + if (value == NULL) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Missing %s field in node-rev"), + header_name); + + SVN_ERR(svn_fs_x__id_parse(id, value)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_noderev(svn_fs_x__noderev_t **noderev_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *headers; + svn_fs_x__noderev_t *noderev; + char *value; + const char *noderev_id; + + SVN_ERR(read_header_block(&headers, stream, scratch_pool)); + SVN_ERR(svn_stream_close(stream)); + + noderev = apr_pcalloc(result_pool, sizeof(*noderev)); + + /* for error messages later */ + noderev_id = svn_hash_gets(headers, HEADER_ID); + + /* Read the node-rev id. */ + SVN_ERR(read_id_part(&noderev->noderev_id, headers, HEADER_ID)); + SVN_ERR(read_id_part(&noderev->node_id, headers, HEADER_NODE)); + SVN_ERR(read_id_part(&noderev->copy_id, headers, HEADER_COPY)); + + /* Read the type. */ + value = svn_hash_gets(headers, HEADER_TYPE); + + if ((value == NULL) || + ( strcmp(value, SVN_FS_X__KIND_FILE) + && strcmp(value, SVN_FS_X__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, SVN_FS_X__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->noderev_id, result_pool, + scratch_pool)); + } + + /* Get the data location. */ + value = svn_hash_gets(headers, HEADER_TEXT); + if (value) + { + SVN_ERR(read_rep_offsets(&noderev->data_rep, value, + &noderev->noderev_id, result_pool, + scratch_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 + { + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Non-canonical cpath field in node-rev '%s'"), + noderev_id); + + noderev->created_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); + } + + /* Get the predecessor ID. */ + value = svn_hash_gets(headers, HEADER_PRED); + if (value) + SVN_ERR(svn_fs_x__id_parse(&noderev->predecessor_id, value)); + else + svn_fs_x__id_reset(&noderev->predecessor_id); + + /* Get the copyroot. */ + value = svn_hash_gets(headers, HEADER_COPYROOT); + if (value == NULL) + { + noderev->copyroot_path = noderev->created_path; + noderev->copyroot_rev + = svn_fs_x__get_revnum(noderev->noderev_id.change_set); + } + else + { + SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value)); + + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed copyroot line in node-rev '%s'"), + noderev_id); + noderev->copyroot_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); + } + + /* Get the copyfrom. */ + value = svn_hash_gets(headers, HEADER_COPYFROM); + if (value == NULL) + { + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + } + else + { + SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value)); + + if (*value == 0) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed copyfrom line in node-rev '%s'"), + noderev_id); + noderev->copyfrom_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); + } + + /* 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; +} + +/* Return a textual representation of the DIGEST of given KIND. + * If IS_NULL is TRUE, no digest is available. + * Allocate the result in RESULT_POOL. + */ +static const char * +format_digest(const unsigned char *digest, + svn_checksum_kind_t kind, + svn_boolean_t is_null, + apr_pool_t *result_pool) +{ + svn_checksum_t checksum; + checksum.digest = digest; + checksum.kind = kind; + + if (is_null) + return "(null)"; + + return svn_checksum_to_cstring_display(&checksum, result_pool); +} + +svn_stringbuf_t * +svn_fs_x__unparse_representation(svn_fs_x__representation_t *rep, + svn_boolean_t mutable_rep_truncated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if (!rep->has_sha1) + return svn_stringbuf_createf + (result_pool, + "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT + " %" SVN_FILESIZE_T_FMT " %s", + rep->id.change_set, rep->id.number, rep->size, + rep->expanded_size, + format_digest(rep->md5_digest, svn_checksum_md5, FALSE, + scratch_pool)); + + return svn_stringbuf_createf + (result_pool, + "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT + " %" SVN_FILESIZE_T_FMT " %s %s", + rep->id.change_set, rep->id.number, rep->size, + rep->expanded_size, + format_digest(rep->md5_digest, svn_checksum_md5, + FALSE, scratch_pool), + format_digest(rep->sha1_digest, svn_checksum_sha1, + !rep->has_sha1, scratch_pool)); +} + + +svn_error_t * +svn_fs_x__write_noderev(svn_stream_t *outfile, + svn_fs_x__noderev_t *noderev, + apr_pool_t *scratch_pool) +{ + svn_string_t *str_id; + + str_id = svn_fs_x__id_unparse(&noderev->noderev_id, scratch_pool); + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", + str_id->data)); + str_id = svn_fs_x__id_unparse(&noderev->node_id, scratch_pool); + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_NODE ": %s\n", + str_id->data)); + str_id = svn_fs_x__id_unparse(&noderev->copy_id, scratch_pool); + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPY ": %s\n", + str_id->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", + (noderev->kind == svn_node_file) ? + SVN_FS_X__KIND_FILE : SVN_FS_X__KIND_DIR)); + + if (svn_fs_x__id_used(&noderev->predecessor_id)) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", + svn_fs_x__id_unparse(&noderev->predecessor_id, + scratch_pool)->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", + noderev->predecessor_count)); + + if (noderev->data_rep) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", + svn_fs_x__unparse_representation + (noderev->data_rep, + noderev->kind == svn_node_dir, + scratch_pool, scratch_pool)->data)); + + if (noderev->prop_rep) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", + svn_fs_x__unparse_representation + (noderev->prop_rep, + TRUE, scratch_pool, scratch_pool)->data)); + + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", + auto_escape_path(noderev->created_path, + scratch_pool))); + + if (noderev->copyfrom_path) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld" + " %s\n", + noderev->copyfrom_rev, + auto_escape_path(noderev->copyfrom_path, + scratch_pool))); + + if ( ( noderev->copyroot_rev + != svn_fs_x__get_revnum(noderev->noderev_id.change_set)) + || (strcmp(noderev->copyroot_path, noderev->created_path) != 0)) + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld" + " %s\n", + noderev->copyroot_rev, + auto_escape_path(noderev->copyroot_path, + scratch_pool))); + + if (noderev->mergeinfo_count > 0) + SVN_ERR(svn_stream_printf(outfile, scratch_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_x__read_rep_header(svn_fs_x__rep_header_t **header, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *buffer; + char *str, *last_str; + apr_int64_t val; + svn_boolean_t eol = FALSE; + + SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool)); + + *header = apr_pcalloc(result_pool, sizeof(**header)); + (*header)->header_size = buffer->len + 1; + if (strcmp(buffer->data, REP_DELTA) == 0) + { + /* This is a delta against the empty stream. */ + (*header)->type = svn_fs_x__rep_self_delta; + return SVN_NO_ERROR; + } + + (*header)->type = svn_fs_x__rep_delta; + + /* We have hopefully a DELTA vs. a non-empty base revision. */ + last_str = buffer->data; + str = svn_cstring_tokenize(" ", &last_str); + if (! str || (strcmp(str, REP_DELTA) != 0)) + goto error; + + SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str)); + + str = svn_cstring_tokenize(" ", &last_str); + if (! str) + goto error; + SVN_ERR(svn_cstring_atoi64(&val, str)); + (*header)->base_item_index = (apr_off_t)val; + + str = svn_cstring_tokenize(" ", &last_str); + if (! str) + goto error; + SVN_ERR(svn_cstring_atoi64(&val, str)); + (*header)->base_length = (svn_filesize_t)val; + + return SVN_NO_ERROR; + + error: + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Malformed representation header")); +} + +svn_error_t * +svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header, + svn_stream_t *stream, + apr_pool_t *scratch_pool) +{ + const char *text; + + switch (header->type) + { + case svn_fs_x__rep_self_delta: + text = REP_DELTA "\n"; + break; + + default: + text = apr_psprintf(scratch_pool, REP_DELTA " %ld %" APR_OFF_T_FMT + " %" SVN_FILESIZE_T_FMT "\n", + header->base_revision, header->base_item_index, + header->base_length); + } + + return svn_error_trace(svn_stream_puts(stream, text)); +} + +/* 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(svn_fs_x__change_t **change_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *line; + svn_boolean_t eof = TRUE; + svn_fs_x__change_t *change; + char *str, *last_str, *kind_str; + + /* Default return value. */ + *change_p = NULL; + + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); + + /* Check for a blank line. */ + if (eof || (line->len == 0)) + return SVN_NO_ERROR; + + change = apr_pcalloc(result_pool, sizeof(*change)); + last_str = line->data; + + /* 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")); + + SVN_ERR(svn_fs_x__id_parse(&change->noderev_id, str)); + + /* 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, SVN_FS_X__KIND_FILE) == 0) + change->node_kind = svn_node_file; + else if (strcmp(kind_str, SVN_FS_X__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->change_kind = svn_fs_path_change_modify; + } + else if (strcmp(str, ACTION_ADD) == 0) + { + change->change_kind = svn_fs_path_change_add; + } + else if (strcmp(str, ACTION_DELETE) == 0) + { + change->change_kind = svn_fs_path_change_delete; + } + else if (strcmp(str, ACTION_REPLACE) == 0) + { + change->change_kind = svn_fs_path_change_replace; + } + else if (strcmp(str, ACTION_RESET) == 0) + { + change->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 mergeinfo-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->mergeinfo_mod = svn_tristate_true; + } + else if (strcmp(str, FLAG_FALSE) == 0) + { + change->mergeinfo_mod = svn_tristate_false; + } + else + { + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid mergeinfo-mod flag in rev-file")); + } + + /* Get the changed path. */ + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid path in changes line")); + + change->path.data = auto_unescape_path(apr_pstrmemdup(result_pool, + last_str, + strlen(last_str)), + result_pool); + change->path.len = strlen(change->path.data); + + /* Read the next line, the copyfrom line. */ + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool)); + change->copyfrom_known = TRUE; + if (eof || line->len == 0) + { + change->copyfrom_rev = SVN_INVALID_REVNUM; + change->copyfrom_path = NULL; + } + else + { + last_str = line->data; + SVN_ERR(parse_revnum(&change->copyfrom_rev, (const char **)&last_str)); + + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid copy-from path in changes line")); + + change->copyfrom_path = auto_unescape_path(last_str, result_pool); + } + + *change_p = change; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_changes(apr_array_header_t **changes, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__change_t *change; + apr_pool_t *iterpool; + + /* Pre-allocate enough room for most change lists. + (will be auto-expanded as necessary). + + Chose the default to just below 2^N such that the doubling reallocs + will request roughly 2^M bytes from the OS without exceeding the + respective two-power by just a few bytes (leaves room array and APR + node overhead for large enough M). + */ + *changes = apr_array_make(result_pool, 63, sizeof(svn_fs_x__change_t *)); + + SVN_ERR(read_change(&change, stream, result_pool, scratch_pool)); + iterpool = svn_pool_create(scratch_pool); + while (change) + { + APR_ARRAY_PUSH(*changes, svn_fs_x__change_t*) = change; + SVN_ERR(read_change(&change, stream, result_pool, iterpool)); + svn_pool_clear(iterpool); + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_changes_incrementally(svn_stream_t *stream, + svn_fs_x__change_receiver_t + change_receiver, + void *change_receiver_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__change_t *change; + apr_pool_t *iterpool; + + iterpool = svn_pool_create(scratch_pool); + do + { + svn_pool_clear(iterpool); + + SVN_ERR(read_change(&change, stream, iterpool, iterpool)); + if (change) + SVN_ERR(change_receiver(change_receiver_baton, change, iterpool)); + } + while (change); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Write a single change entry, path PATH, change CHANGE, to STREAM. + + All temporary allocations are in SCRATCH_POOL. */ +static svn_error_t * +write_change_entry(svn_stream_t *stream, + svn_fs_x__change_t *change, + apr_pool_t *scratch_pool) +{ + const char *idstr; + const char *change_string = NULL; + const char *kind_string = ""; + svn_stringbuf_t *buf; + apr_size_t len; + + 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); + } + + idstr = svn_fs_x__id_unparse(&change->noderev_id, scratch_pool)->data; + + SVN_ERR_ASSERT(change->node_kind == svn_node_dir + || change->node_kind == svn_node_file); + kind_string = apr_psprintf(scratch_pool, "-%s", + change->node_kind == svn_node_dir + ? SVN_FS_X__KIND_DIR + : SVN_FS_X__KIND_FILE); + + buf = svn_stringbuf_createf(scratch_pool, "%s %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, + change->mergeinfo_mod == svn_tristate_true + ? FLAG_TRUE : FLAG_FALSE, + auto_escape_path(change->path.data, scratch_pool)); + + if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) + { + svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s", + change->copyfrom_rev, + auto_escape_path(change->copyfrom_path, + scratch_pool))); + } + + svn_stringbuf_appendbyte(buf, '\n'); + + /* Write all change info in one write call. */ + len = buf->len; + return svn_error_trace(svn_stream_write(stream, buf->data, &len)); +} + +svn_error_t * +svn_fs_x__write_changes(svn_stream_t *stream, + svn_fs_t *fs, + apr_hash_t *changes, + svn_boolean_t terminate_list, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_array_header_t *sorted_changed_paths; + int i; + + /* For the sake of the repository administrator sort the changes so + that the final file is deterministic and repeatable, however the + rest of the FSX code doesn't require any particular order here. + + Also, this sorting is only effective in writing all entries with + a single call as write_final_changed_path_info() does. For the + list being written incrementally during transaction, we actually + *must not* change the order of entries from different calls. + */ + sorted_changed_paths = svn_sort__hash(changes, + svn_sort_compare_items_lexically, + scratch_pool); + + /* Write all items to disk in the new order. */ + for (i = 0; i < sorted_changed_paths->nelts; ++i) + { + svn_fs_x__change_t *change; + + svn_pool_clear(iterpool); + change = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).value; + + /* Write out the new entry into the final rev-file. */ + SVN_ERR(write_change_entry(stream, change, iterpool)); + } + + if (terminate_list) + svn_stream_puts(stream, "\n"); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + diff --git a/contrib/subversion/subversion/libsvn_fs_x/low_level.h b/contrib/subversion/subversion/libsvn_fs_x/low_level.h new file mode 100644 index 000000000..e4fdf05a8 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/low_level.h @@ -0,0 +1,214 @@ +/* low_level.c --- low level r/w access to fs_x file structures + * + * ==================================================================== + * 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_FS__LOW_LEVEL_H +#define SVN_LIBSVN_FS__LOW_LEVEL_H + +#include "svn_fs.h" + +#include "fs_x.h" +#include "id.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Kinds that a node-rev can be. */ +#define SVN_FS_X__KIND_FILE "file" +#define SVN_FS_X__KIND_DIR "dir" + +/* The functions are grouped as follows: + * + * - revision footer + * - representation (as in "text:" and "props:" lines) + * - node revision + * - representation header ("DELTA" lines) + * - changed path list + */ + +/* Given the FSX revision / pack FOOTER, parse it destructively + * and return the start offsets of the index data in *L2P_OFFSET and + * *P2L_OFFSET, respectively. Also, return the expected checksums in + * in *L2P_CHECKSUM and *P2L_CHECKSUM. + * + * Note that REV is only used to construct nicer error objects that + * mention this revision. Allocate the checksums in RESULT_POOL. + */ +svn_error_t * +svn_fs_x__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Given the offset of the L2P index data in L2P_OFFSET, the content + * checksum in L2P_CHECKSUM and the offset plus checksum of the P2L + * index data in P2L_OFFSET and P2L_CHECKSUM. + * + * Return the corresponding format 7+ revision / pack file footer. + * Allocate it in RESULT_POOL and use SCRATCH_POOL for temporary. + */ +svn_stringbuf_t * +svn_fs_x__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Parse the description of a representation from TEXT and store it + into *REP_P. TEXT will be invalidated by this call. Allocate *REP_P in + RESULT_POOL and use SCRATCH_POOL for temporaries. */ +svn_error_t * +svn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p, + svn_stringbuf_t *text, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return a formatted string 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. + * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ +svn_stringbuf_t * +svn_fs_x__unparse_representation(svn_fs_x__representation_t *rep, + svn_boolean_t mutable_rep_truncated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Read a node-revision from STREAM. Set *NODEREV to the new structure, + allocated in RESULT_POOL. */ +svn_error_t * +svn_fs_x__read_noderev(svn_fs_x__noderev_t **noderev, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Write the node-revision NODEREV into the stream OUTFILE. + Temporary allocations are from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__write_noderev(svn_stream_t *outfile, + svn_fs_x__noderev_t *noderev, + apr_pool_t *scratch_pool); + +/* This type enumerates all forms of representations that we support. */ +typedef enum svn_fs_x__rep_type_t +{ + /* this is a DELTA representation with no base representation */ + svn_fs_x__rep_self_delta, + + /* this is a DELTA representation against some base representation */ + svn_fs_x__rep_delta, + + /* this is a representation in a star-delta container */ + svn_fs_x__rep_container +} svn_fs_x__rep_type_t; + +/* This structure is used to hold the information stored in a representation + * header. */ +typedef struct svn_fs_x__rep_header_t +{ + /* type of the representation, i.e. whether self-DELTA etc. */ + svn_fs_x__rep_type_t type; + + /* if this rep is a delta against some other rep, that base rep can + * be found in this revision. Should be 0 if there is no base rep. */ + svn_revnum_t base_revision; + + /* if this rep is a delta against some other rep, that base rep can + * be found at this item index within the base rep's revision. Should + * be 0 if there is no base rep. */ + apr_off_t base_item_index; + + /* if this rep is a delta against some other rep, this is the (deltified) + * size of that base rep. Should be 0 if there is no base rep. */ + svn_filesize_t base_length; + + /* length of the textual representation of the header in the rep or pack + * file, including EOL. Only valid after reading it from disk. + * Should be 0 otherwise. */ + apr_size_t header_size; +} svn_fs_x__rep_header_t; + +/* Read the next line from STREAM and parse it as a text + representation header. Return the parsed entry in *HEADER, allocated + in RESULT_POOL. Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Write the representation HEADER to STREAM. + * Use SCRATCH_POOL for allocations. */ +svn_error_t * +svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header, + svn_stream_t *stream, + apr_pool_t *scratch_pool); + +/* Read all the changes from STREAM and store them in *CHANGES, + allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__read_changes(apr_array_header_t **changes, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Callback function used by svn_fs_fs__read_changes_incrementally(), + * asking the receiver to process to process CHANGE using BATON. CHANGE + * and SCRATCH_POOL will not be valid beyond the current callback invocation. + */ +typedef svn_error_t *(*svn_fs_x__change_receiver_t)( + void *baton, + svn_fs_x__change_t *change, + apr_pool_t *scratch_pool); + +/* Read all the changes from STREAM and invoke CHANGE_RECEIVER on each change. + Do all allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__read_changes_incrementally(svn_stream_t *stream, + svn_fs_x__change_receiver_t + change_receiver, + void *change_receiver_baton, + apr_pool_t *scratch_pool); + +/* Write the changed path info from CHANGES in filesystem FS to the + output stream STREAM. You may call this function multiple time on + the same stream. If you are writing to a (proto-)revision file, + the last call must set TERMINATE_LIST to write an extra empty line + that marks the end of the changed paths list. + Perform temporary allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_x__write_changes(svn_stream_t *stream, + svn_fs_t *fs, + apr_hash_t *changes, + svn_boolean_t terminate_list, + apr_pool_t *scratch_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS__LOW_LEVEL_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/noderevs.c b/contrib/subversion/subversion/libsvn_fs_x/noderevs.c new file mode 100644 index 000000000..60c602996 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/noderevs.c @@ -0,0 +1,912 @@ +/* noderevs.h --- FSX node revision container + * + * ==================================================================== + * 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 "private/svn_dep_compat.h" +#include "private/svn_packed_data.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "noderevs.h" +#include "string_table.h" +#include "temp_serializer.h" + +/* These flags will be used with the FLAGS field in binary_noderev_t. + */ + +/* (flags & NODEREV_KIND_MASK) extracts the noderev type */ +#define NODEREV_KIND_MASK 0x00007 + +/* the noderev has merge info */ +#define NODEREV_HAS_MINFO 0x00008 + +/* the noderev has copy-from-path and revision */ +#define NODEREV_HAS_COPYFROM 0x00010 + +/* the noderev has copy-root path and revision */ +#define NODEREV_HAS_COPYROOT 0x00020 + +/* the noderev has copy-root path and revision */ +#define NODEREV_HAS_CPATH 0x00040 + +/* Our internal representation of a svn_fs_x__noderev_t. + * + * We will store path strings in a string container and reference them + * from here. Similarly, IDs and representations are being stored in + * separate containers and then also referenced here. This eliminates + * the need to store the same IDs and representations more than once. + */ +typedef struct binary_noderev_t +{ + /* node type and presence indicators */ + apr_uint32_t flags; + + /* Index+1 of the noderev-id for this node-rev. */ + int id; + + /* Index+1 of the node-id for this node-rev. */ + int node_id; + + /* Index+1 of the copy-id for this node-rev. */ + int copy_id; + + /* Index+1 of the predecessor node revision id, or 0 if there is no + predecessor for this node revision */ + int predecessor_id; + + /* number of predecessors this node revision has (recursively), or + -1 if not known (for backward compatibility). */ + int predecessor_count; + + /* If this node-rev is a copy, what revision was it copied from? */ + svn_revnum_t copyfrom_rev; + + /* Helper for history tracing, root revision of the parent tree from + whence this node-rev was copied. */ + svn_revnum_t copyroot_rev; + + /* If this node-rev is a copy, this is the string index+1 of the path + from which that copy way made. 0, otherwise. */ + apr_size_t copyfrom_path; + + /* String index+1 of the root of the parent tree from whence this node- + * rev was copied. */ + apr_size_t copyroot_path; + + /* Index+1 of the representation key for this node's properties. + May be 0 if there are no properties. */ + int prop_rep; + + /* Index+1 of the representation for this node's data. + May be 0 if there is no data. */ + int data_rep; + + /* String index+1 of the path at which this node first came into + existence. */ + apr_size_t created_path; + + /* Number of nodes with svn:mergeinfo properties that are + descendants of this node (including it itself) */ + apr_int64_t mergeinfo_count; + +} binary_noderev_t; + +/* The actual container object. Node revisions are concatenated into + * NODEREVS, referenced representations are stored in DATA_REPS / PROP_REPS + * and the ids in IDs. PATHS is the string table for all paths. + * + * During construction, BUILDER will be used instead of PATHS. IDS_DICT, + * DATA_REPS_DICT and PROP_REPS_DICT are also only used during construction + * and are NULL otherwise. + */ +struct svn_fs_x__noderevs_t +{ + /* The paths - either in 'builder' mode or finalized mode. + * The respective other pointer will be NULL. */ + string_table_builder_t *builder; + string_table_t *paths; + + /* During construction, maps a full binary_id_t to an index into IDS */ + apr_hash_t *ids_dict; + + /* During construction, maps a full binary_representation_t to an index + * into REPS. */ + apr_hash_t *reps_dict; + + /* array of binary_id_t */ + apr_array_header_t *ids; + + /* array of binary_representation_t */ + apr_array_header_t *reps; + + /* array of binary_noderev_t. */ + apr_array_header_t *noderevs; +}; + +svn_fs_x__noderevs_t * +svn_fs_x__noderevs_create(int initial_count, + apr_pool_t* result_pool) +{ + svn_fs_x__noderevs_t *noderevs + = apr_palloc(result_pool, sizeof(*noderevs)); + + noderevs->builder = svn_fs_x__string_table_builder_create(result_pool); + noderevs->ids_dict = svn_hash__make(result_pool); + noderevs->reps_dict = svn_hash__make(result_pool); + noderevs->paths = NULL; + + noderevs->ids + = apr_array_make(result_pool, 2 * initial_count, sizeof(svn_fs_x__id_t)); + noderevs->reps + = apr_array_make(result_pool, 2 * initial_count, + sizeof(svn_fs_x__representation_t)); + noderevs->noderevs + = apr_array_make(result_pool, initial_count, sizeof(binary_noderev_t)); + + return noderevs; +} + +/* Given the ID, return the index+1 into IDS that contains a binary_id + * for it. Returns 0 for NULL IDs. We use DICT to detect duplicates. + */ +static int +store_id(apr_array_header_t *ids, + apr_hash_t *dict, + const svn_fs_x__id_t *id) +{ + int idx; + void *idx_void; + + if (!svn_fs_x__id_used(id)) + return 0; + + idx_void = apr_hash_get(dict, &id, sizeof(id)); + idx = (int)(apr_uintptr_t)idx_void; + if (idx == 0) + { + APR_ARRAY_PUSH(ids, svn_fs_x__id_t) = *id; + idx = ids->nelts; + apr_hash_set(dict, ids->elts + (idx-1) * ids->elt_size, + ids->elt_size, (void*)(apr_uintptr_t)idx); + } + + return idx; +} + +/* Given the REP, return the index+1 into REPS that contains a copy of it. + * Returns 0 for NULL IDs. We use DICT to detect duplicates. + */ +static int +store_representation(apr_array_header_t *reps, + apr_hash_t *dict, + const svn_fs_x__representation_t *rep) +{ + int idx; + void *idx_void; + + if (rep == NULL) + return 0; + + idx_void = apr_hash_get(dict, rep, sizeof(*rep)); + idx = (int)(apr_uintptr_t)idx_void; + if (idx == 0) + { + APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = *rep; + idx = reps->nelts; + apr_hash_set(dict, reps->elts + (idx-1) * reps->elt_size, + reps->elt_size, (void*)(apr_uintptr_t)idx); + } + + return idx; +} + +apr_size_t +svn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container, + svn_fs_x__noderev_t *noderev) +{ + binary_noderev_t binary_noderev = { 0 }; + + binary_noderev.flags = (noderev->has_mergeinfo ? NODEREV_HAS_MINFO : 0) + | (noderev->copyfrom_path ? NODEREV_HAS_COPYFROM : 0) + | (noderev->copyroot_path ? NODEREV_HAS_COPYROOT : 0) + | (noderev->created_path ? NODEREV_HAS_CPATH : 0) + | (int)noderev->kind; + + binary_noderev.id + = store_id(container->ids, container->ids_dict, &noderev->noderev_id); + binary_noderev.node_id + = store_id(container->ids, container->ids_dict, &noderev->node_id); + binary_noderev.copy_id + = store_id(container->ids, container->ids_dict, &noderev->copy_id); + binary_noderev.predecessor_id + = store_id(container->ids, container->ids_dict, &noderev->predecessor_id); + + if (noderev->copyfrom_path) + { + binary_noderev.copyfrom_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->copyfrom_path, + 0); + binary_noderev.copyfrom_rev = noderev->copyfrom_rev; + } + + if (noderev->copyroot_path) + { + binary_noderev.copyroot_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->copyroot_path, + 0); + binary_noderev.copyroot_rev = noderev->copyroot_rev; + } + + binary_noderev.predecessor_count = noderev->predecessor_count; + binary_noderev.prop_rep = store_representation(container->reps, + container->reps_dict, + noderev->prop_rep); + binary_noderev.data_rep = store_representation(container->reps, + container->reps_dict, + noderev->data_rep); + + if (noderev->created_path) + binary_noderev.created_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->created_path, + 0); + + binary_noderev.mergeinfo_count = noderev->mergeinfo_count; + + APR_ARRAY_PUSH(container->noderevs, binary_noderev_t) = binary_noderev; + + return container->noderevs->nelts - 1; +} + +apr_size_t +svn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t *container) +{ + /* CONTAINER must be in 'builder' mode */ + if (container->builder == NULL) + return 0; + + /* string table code makes its own prediction, + * noderevs should be < 16 bytes each, + * id parts < 4 bytes each, + * data representations < 40 bytes each, + * property representations < 30 bytes each, + * some static overhead should be assumed */ + return svn_fs_x__string_table_builder_estimate_size(container->builder) + + container->noderevs->nelts * 16 + + container->ids->nelts * 4 + + container->reps->nelts * 40 + + 100; +} + +/* Set *ID to the ID part stored at index IDX in IDS. + */ +static svn_error_t * +get_id(svn_fs_x__id_t *id, + const apr_array_header_t *ids, + int idx) +{ + /* handle NULL IDs */ + if (idx == 0) + { + svn_fs_x__id_reset(id); + return SVN_NO_ERROR; + } + + /* check for corrupted data */ + if (idx < 0 || idx > ids->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("ID part index %d exceeds container size %d"), + idx, ids->nelts); + + /* Return the requested ID. */ + *id = APR_ARRAY_IDX(ids, idx - 1, svn_fs_x__id_t); + + return SVN_NO_ERROR; +} + +/* Create a svn_fs_x__representation_t in *REP, allocated in POOL based on the + * representation stored at index IDX in REPS. + */ +static svn_error_t * +get_representation(svn_fs_x__representation_t **rep, + const apr_array_header_t *reps, + int idx, + apr_pool_t *pool) +{ + /* handle NULL representations */ + if (idx == 0) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* check for corrupted data */ + if (idx < 0 || idx > reps->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("Node revision ID index %d" + " exceeds container size %d"), + idx, reps->nelts); + + /* no translation required. Just duplicate the info */ + *rep = apr_pmemdup(pool, + &APR_ARRAY_IDX(reps, idx - 1, svn_fs_x__representation_t), + sizeof(**rep)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p, + const svn_fs_x__noderevs_t *container, + apr_size_t idx, + apr_pool_t *pool) +{ + svn_fs_x__noderev_t *noderev; + binary_noderev_t *binary_noderev; + + /* CONTAINER must be in 'finalized' mode */ + SVN_ERR_ASSERT(container->builder == NULL); + SVN_ERR_ASSERT(container->paths); + + /* validate index */ + if (idx >= (apr_size_t)container->noderevs->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Node revision index %%%s" + " exceeds container size %%d"), + APR_SIZE_T_FMT), + idx, container->noderevs->nelts); + + /* allocate result struct and fill it field by field */ + noderev = apr_pcalloc(pool, sizeof(*noderev)); + binary_noderev = &APR_ARRAY_IDX(container->noderevs, idx, binary_noderev_t); + + noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK); + SVN_ERR(get_id(&noderev->noderev_id, container->ids, binary_noderev->id)); + SVN_ERR(get_id(&noderev->node_id, container->ids, + binary_noderev->node_id)); + SVN_ERR(get_id(&noderev->copy_id, container->ids, + binary_noderev->copy_id)); + SVN_ERR(get_id(&noderev->predecessor_id, container->ids, + binary_noderev->predecessor_id)); + + if (binary_noderev->flags & NODEREV_HAS_COPYFROM) + { + noderev->copyfrom_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->copyfrom_path, + NULL, + pool); + noderev->copyfrom_rev = binary_noderev->copyfrom_rev; + } + else + { + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + } + + if (binary_noderev->flags & NODEREV_HAS_COPYROOT) + { + noderev->copyroot_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->copyroot_path, + NULL, + pool); + noderev->copyroot_rev = binary_noderev->copyroot_rev; + } + else + { + noderev->copyroot_path = NULL; + noderev->copyroot_rev = 0; + } + + noderev->predecessor_count = binary_noderev->predecessor_count; + + SVN_ERR(get_representation(&noderev->prop_rep, container->reps, + binary_noderev->prop_rep, pool)); + SVN_ERR(get_representation(&noderev->data_rep, container->reps, + binary_noderev->data_rep, pool)); + + if (binary_noderev->flags & NODEREV_HAS_CPATH) + noderev->created_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->created_path, + NULL, + pool); + + noderev->mergeinfo_count = binary_noderev->mergeinfo_count; + + noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0; + *noderev_p = noderev; + + return SVN_NO_ERROR; +} + +/* Create and return a stream for representations in PARENT. + * Initialize the sub-streams for all fields, except checksums. + */ +static svn_packed__int_stream_t * +create_rep_stream(svn_packed__int_stream_t *parent) +{ + svn_packed__int_stream_t *stream + = svn_packed__create_int_substream(parent, FALSE, FALSE); + + /* sub-streams for members - except for checksums */ + /* has_sha1 */ + svn_packed__create_int_substream(stream, FALSE, FALSE); + + /* rev, item_index, size, expanded_size */ + svn_packed__create_int_substream(stream, TRUE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + + return stream; +} + +/* Serialize all representations in REP. Store checksums in DIGEST_STREAM, + * put all other fields into REP_STREAM. + */ +static void +write_reps(svn_packed__int_stream_t *rep_stream, + svn_packed__byte_stream_t *digest_stream, + apr_array_header_t *reps) +{ + int i; + for (i = 0; i < reps->nelts; ++i) + { + svn_fs_x__representation_t *rep + = &APR_ARRAY_IDX(reps, i, svn_fs_x__representation_t); + + svn_packed__add_uint(rep_stream, rep->has_sha1); + + svn_packed__add_uint(rep_stream, rep->id.change_set); + svn_packed__add_uint(rep_stream, rep->id.number); + svn_packed__add_uint(rep_stream, rep->size); + svn_packed__add_uint(rep_stream, rep->expanded_size); + + svn_packed__add_bytes(digest_stream, + (const char *)rep->md5_digest, + sizeof(rep->md5_digest)); + if (rep->has_sha1) + svn_packed__add_bytes(digest_stream, + (const char *)rep->sha1_digest, + sizeof(rep->sha1_digest)); + } +} + +svn_error_t * +svn_fs_x__write_noderevs_container(svn_stream_t *stream, + const svn_fs_x__noderevs_t *container, + apr_pool_t *scratch_pool) +{ + int i; + + string_table_t *paths = container->paths + ? container->paths + : svn_fs_x__string_table_create(container->builder, + scratch_pool); + + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + /* one common top-level stream for all arrays. One sub-stream */ + svn_packed__int_stream_t *structs_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + svn_packed__int_stream_t *ids_stream + = svn_packed__create_int_substream(structs_stream, FALSE, FALSE); + svn_packed__int_stream_t *reps_stream + = create_rep_stream(structs_stream); + svn_packed__int_stream_t *noderevs_stream + = svn_packed__create_int_substream(structs_stream, FALSE, FALSE); + svn_packed__byte_stream_t *digests_stream + = svn_packed__create_bytes_stream(root); + + /* structure the IDS_STREAM such we can extract much of the redundancy + * from the svn_fs_x__ip_part_t structs */ + for (i = 0; i < 2; ++i) + svn_packed__create_int_substream(ids_stream, TRUE, FALSE); + + /* Same storing binary_noderev_t in the NODEREVS_STREAM */ + svn_packed__create_int_substream(noderevs_stream, FALSE, FALSE); + for (i = 0; i < 13; ++i) + svn_packed__create_int_substream(noderevs_stream, TRUE, FALSE); + + /* serialize ids array */ + for (i = 0; i < container->ids->nelts; ++i) + { + svn_fs_x__id_t *id = &APR_ARRAY_IDX(container->ids, i, svn_fs_x__id_t); + + svn_packed__add_int(ids_stream, id->change_set); + svn_packed__add_uint(ids_stream, id->number); + } + + /* serialize rep arrays */ + write_reps(reps_stream, digests_stream, container->reps); + + /* serialize noderevs array */ + for (i = 0; i < container->noderevs->nelts; ++i) + { + const binary_noderev_t *noderev + = &APR_ARRAY_IDX(container->noderevs, i, binary_noderev_t); + + svn_packed__add_uint(noderevs_stream, noderev->flags); + + svn_packed__add_uint(noderevs_stream, noderev->id); + svn_packed__add_uint(noderevs_stream, noderev->node_id); + svn_packed__add_uint(noderevs_stream, noderev->copy_id); + svn_packed__add_uint(noderevs_stream, noderev->predecessor_id); + svn_packed__add_uint(noderevs_stream, noderev->predecessor_count); + + svn_packed__add_uint(noderevs_stream, noderev->copyfrom_path); + svn_packed__add_int(noderevs_stream, noderev->copyfrom_rev); + svn_packed__add_uint(noderevs_stream, noderev->copyroot_path); + svn_packed__add_int(noderevs_stream, noderev->copyroot_rev); + + svn_packed__add_uint(noderevs_stream, noderev->prop_rep); + svn_packed__add_uint(noderevs_stream, noderev->data_rep); + + svn_packed__add_uint(noderevs_stream, noderev->created_path); + svn_packed__add_uint(noderevs_stream, noderev->mergeinfo_count); + } + + /* write to disk */ + SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool)); + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Allocate a svn_fs_x__representation_t array in POOL and return it in + * REPS_P. Deserialize the data in REP_STREAM and DIGEST_STREAM and store + * the resulting representations into the *REPS_P. + */ +static svn_error_t * +read_reps(apr_array_header_t **reps_p, + svn_packed__int_stream_t *rep_stream, + svn_packed__byte_stream_t *digest_stream, + apr_pool_t *pool) +{ + apr_size_t i; + apr_size_t len; + const char *bytes; + + apr_size_t count + = svn_packed__int_count(svn_packed__first_int_substream(rep_stream)); + apr_array_header_t *reps + = apr_array_make(pool, (int)count, sizeof(svn_fs_x__representation_t)); + + for (i = 0; i < count; ++i) + { + svn_fs_x__representation_t rep; + + rep.has_sha1 = (svn_boolean_t)svn_packed__get_uint(rep_stream); + + rep.id.change_set = (svn_revnum_t)svn_packed__get_uint(rep_stream); + rep.id.number = svn_packed__get_uint(rep_stream); + rep.size = svn_packed__get_uint(rep_stream); + rep.expanded_size = svn_packed__get_uint(rep_stream); + + /* when extracting the checksums, beware of buffer under/overflows + caused by disk data corruption. */ + bytes = svn_packed__get_bytes(digest_stream, &len); + if (len != sizeof(rep.md5_digest)) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Unexpected MD5" + " digest size %%%s"), + APR_SIZE_T_FMT), + len); + + memcpy(rep.md5_digest, bytes, sizeof(rep.md5_digest)); + if (rep.has_sha1) + { + bytes = svn_packed__get_bytes(digest_stream, &len); + if (len != sizeof(rep.sha1_digest)) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Unexpected SHA1" + " digest size %%%s"), + APR_SIZE_T_FMT), + len); + + memcpy(rep.sha1_digest, bytes, sizeof(rep.sha1_digest)); + } + + APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = rep; + } + + *reps_p = reps; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i; + apr_size_t count; + + svn_fs_x__noderevs_t *noderevs + = apr_pcalloc(result_pool, sizeof(*noderevs)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *structs_stream; + svn_packed__int_stream_t *ids_stream; + svn_packed__int_stream_t *reps_stream; + svn_packed__int_stream_t *noderevs_stream; + svn_packed__byte_stream_t *digests_stream; + + /* read everything from disk */ + SVN_ERR(svn_fs_x__read_string_table(&noderevs->paths, stream, + result_pool, scratch_pool)); + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + + /* get streams */ + structs_stream = svn_packed__first_int_stream(root); + ids_stream = svn_packed__first_int_substream(structs_stream); + reps_stream = svn_packed__next_int_stream(ids_stream); + noderevs_stream = svn_packed__next_int_stream(reps_stream); + digests_stream = svn_packed__first_byte_stream(root); + + /* read ids array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(ids_stream)); + noderevs->ids + = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t)); + for (i = 0; i < count; ++i) + { + svn_fs_x__id_t id; + + id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream); + id.number = svn_packed__get_uint(ids_stream); + + APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id; + } + + /* read rep arrays */ + SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream, + result_pool)); + + /* read noderevs array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(noderevs_stream)); + noderevs->noderevs + = apr_array_make(result_pool, (int)count, sizeof(binary_noderev_t)); + for (i = 0; i < count; ++i) + { + binary_noderev_t noderev; + + noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream); + + noderev.id = (int)svn_packed__get_uint(noderevs_stream); + noderev.node_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream); + + noderev.copyfrom_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream); + noderev.copyroot_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.copyroot_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream); + + noderev.prop_rep = (int)svn_packed__get_uint(noderevs_stream); + noderev.data_rep = (int)svn_packed__get_uint(noderevs_stream); + + noderev.created_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.mergeinfo_count = svn_packed__get_uint(noderevs_stream); + + APR_ARRAY_PUSH(noderevs->noderevs, binary_noderev_t) = noderev; + } + + *container = noderevs; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_noderevs_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__noderevs_t *noderevs = in; + svn_stringbuf_t *serialized; + apr_size_t size + = noderevs->ids->elt_size * noderevs->ids->nelts + + noderevs->reps->elt_size * noderevs->reps->nelts + + noderevs->noderevs->elt_size * noderevs->noderevs->nelts + + 10 * noderevs->noderevs->elt_size + + 100; + + /* serialize array header and all its elements */ + svn_temp_serializer__context_t *context + = svn_temp_serializer__init(noderevs, sizeof(*noderevs), size, pool); + + /* serialize sub-structures */ + svn_fs_x__serialize_string_table(context, &noderevs->paths); + svn_fs_x__serialize_apr_array(context, &noderevs->ids); + svn_fs_x__serialize_apr_array(context, &noderevs->reps); + svn_fs_x__serialize_apr_array(context, &noderevs->noderevs); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_noderevs_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__noderevs_t *noderevs = (svn_fs_x__noderevs_t *)data; + + /* de-serialize sub-structures */ + svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, pool); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, pool); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, pool); + + /* done */ + *out = noderevs; + + return SVN_NO_ERROR; +} + +/* Deserialize the cache serialized APR struct at *IN in BUFFER and write + * the result to OUT. Note that this will only resolve the pointers and + * not the array elements themselves. */ +static void +resolve_apr_array_header(apr_array_header_t *out, + const void *buffer, + apr_array_header_t * const *in) +{ + const apr_array_header_t *array + = svn_temp_deserializer__ptr(buffer, (const void *const *)in); + const char *elements + = svn_temp_deserializer__ptr(array, (const void *const *)&array->elts); + + *out = *array; + out->elts = (char *)elements; + out->pool = NULL; +} + +svn_error_t * +svn_fs_x__noderevs_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + svn_fs_x__noderev_t *noderev; + binary_noderev_t *binary_noderev; + + apr_array_header_t ids; + apr_array_header_t reps; + apr_array_header_t noderevs; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__noderevs_t *container = data; + + /* Resolve all container pointers */ + const string_table_t *paths + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->paths); + + resolve_apr_array_header(&ids, container, &container->ids); + resolve_apr_array_header(&reps, container, &container->reps); + resolve_apr_array_header(&noderevs, container, &container->noderevs); + + /* allocate result struct and fill it field by field */ + noderev = apr_pcalloc(pool, sizeof(*noderev)); + binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t); + + noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK); + SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id)); + SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id)); + SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id)); + SVN_ERR(get_id(&noderev->predecessor_id, &ids, + binary_noderev->predecessor_id)); + + if (binary_noderev->flags & NODEREV_HAS_COPYFROM) + { + noderev->copyfrom_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->copyfrom_path, + NULL, + pool); + noderev->copyfrom_rev = binary_noderev->copyfrom_rev; + } + else + { + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + } + + if (binary_noderev->flags & NODEREV_HAS_COPYROOT) + { + noderev->copyroot_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->copyroot_path, + NULL, + pool); + noderev->copyroot_rev = binary_noderev->copyroot_rev; + } + else + { + noderev->copyroot_path = NULL; + noderev->copyroot_rev = 0; + } + + noderev->predecessor_count = binary_noderev->predecessor_count; + + SVN_ERR(get_representation(&noderev->prop_rep, &reps, + binary_noderev->prop_rep, pool)); + SVN_ERR(get_representation(&noderev->data_rep, &reps, + binary_noderev->data_rep, pool)); + + if (binary_noderev->flags & NODEREV_HAS_CPATH) + noderev->created_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->created_path, + NULL, + pool); + + noderev->mergeinfo_count = binary_noderev->mergeinfo_count; + + noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0; + *out = noderev; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__mergeinfo_count_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + binary_noderev_t *binary_noderev; + apr_array_header_t noderevs; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__noderevs_t *container = data; + + /* Resolve all container pointers */ + resolve_apr_array_header(&noderevs, container, &container->noderevs); + binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t); + + *(apr_int64_t *)out = binary_noderev->mergeinfo_count; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/noderevs.h b/contrib/subversion/subversion/libsvn_fs_x/noderevs.h new file mode 100644 index 000000000..f9b79dcf2 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/noderevs.h @@ -0,0 +1,142 @@ +/* noderevs.h --- FSX node revision container + * + * ==================================================================== + * 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_FS__NODEREVS_H +#define SVN_LIBSVN_FS__NODEREVS_H + +#include "svn_io.h" +#include "fs.h" + +/* A collection of related noderevs tends to be widely redundant (similar + * paths, predecessor ID matching anothers ID, shared representations etc.) + * Also, the binary representation of a noderev can be much shorter than + * the ordinary textual variant. + * + * In its serialized form, the svn_fs_x__noderevs_t container extracts + * most of that redundancy and the run-time representation is also much + * smaller than sum of the respective svn_fs_x__noderev_t objects. + * + * As with other containers, this one has two modes: 'construction', in + * which you may add data to it, and 'getter' in which there is only r/o + * access to the data. + */ + +/* An opaque collection of node revisions. + */ +typedef struct svn_fs_x__noderevs_t svn_fs_x__noderevs_t; + +/* Create and populate noderev containers. */ + +/* Create and return a new noderevs container with an initial capacity of + * INITIAL_COUNT svn_fs_x__noderev_t objects. + * Allocate the result in RESULT_POOL. + */ +svn_fs_x__noderevs_t * +svn_fs_x__noderevs_create(int initial_count, + apr_pool_t *result_pool); + +/* Add NODEREV to the CONTAINER. Return the index that identifies the new + * item in this container. + */ +apr_size_t +svn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container, + svn_fs_x__noderev_t *noderev); + +/* Return a rough estimate in bytes for the serialized representation + * of CONTAINER. + */ +apr_size_t +svn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t *container); + +/* Read from noderev containers. */ + +/* From CONTAINER, extract the noderev with the given IDX. Allocate + * the result in POOL and return it in *NODEREV_P. + */ +svn_error_t * +svn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p, + const svn_fs_x__noderevs_t *container, + apr_size_t idx, + apr_pool_t *pool); + +/* I/O interface. */ + +/* Write a serialized representation of CONTAINER to STREAM. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__write_noderevs_container(svn_stream_t *stream, + const svn_fs_x__noderevs_t *container, + apr_pool_t *scratch_pool); + +/* Read a noderev container from its serialized representation in STREAM. + * Allocate the result in RESULT_POOL and return it in *CONTAINER. Use + * SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Implements #svn_cache__serialize_func_t for svn_fs_x__noderevs_t + * objects. + */ +svn_error_t * +svn_fs_x__serialize_noderevs_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* Implements #svn_cache__deserialize_func_t for svn_fs_x__noderevs_t + * objects. + */ +svn_error_t * +svn_fs_x__deserialize_noderevs_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* Implements svn_cache__partial_getter_func_t for svn_fs_x__noderevs_t, + * setting *OUT to the svn_fs_x__noderev_t selected by the apr_uint32_t index + * passed in as *BATON. This function is similar to svn_fs_x__noderevs_get + * but operates on the cache serialized representation of the container. + */ +svn_error_t * +svn_fs_x__noderevs_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +/* Implements svn_cache__partial_getter_func_t for the mergeinfo_count in + * the stored noderevs, setting *OUT to the apr_int64_t counter value of + * the noderev selected by the apr_uint32_t index passed in as *BATON. + */ +svn_error_t * +svn_fs_x__mergeinfo_count_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/pack.c b/contrib/subversion/subversion/libsvn_fs_x/pack.c new file mode 100644 index 000000000..cdbb98029 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/pack.c @@ -0,0 +1,2324 @@ +/* pack.c --- FSX shard packing 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. + * ==================================================================== + */ +#include + +#include "svn_pools.h" +#include "svn_dirent_uri.h" +#include "svn_sorts.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "private/svn_temp_serializer.h" + +#include "fs_x.h" +#include "pack.h" +#include "util.h" +#include "revprops.h" +#include "transaction.h" +#include "index.h" +#include "low_level.h" +#include "cached_data.h" +#include "changes.h" +#include "noderevs.h" +#include "reps.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" +#include "temp_serializer.h" + +/* Packing logic: + * + * We pack files on a pack file basis (e.g. 1000 revs) without changing + * existing pack files nor the revision files outside the range to pack. + * + * First, we will scan the revision file indexes to determine the number + * of items to "place" (i.e. determine their optimal position within the + * future pack file). For each item, we will need a constant amount of + * memory to track it. A MAX_MEM parameter sets a limit to the number of + * items we may place in one go. That means, we may not be able to add + * all revisions at once. Instead, we will run the placement for a subset + * of revisions at a time. The very unlikely worst case will simply append + * all revision data with just a little reshuffling inside each revision. + * + * In a second step, we read all revisions in the selected range, build + * the item tracking information and copy the items themselves from the + * revision files to temporary files. The latter serve as buckets for a + * very coarse bucket presort: Separate change lists, file properties, + * directory properties and noderevs + representations from one another. + * + * The third step will determine an optimized placement for the items in + * each of the 4 buckets separately. The first three will simply order + * their items by revision, starting with the newest once. Placing rep + * and noderev items is a more elaborate process documented in the code. + * + * In short, we store items in the following order: + * - changed paths lists + * - node property + * - directory properties + * - directory representations corresponding noderevs, lexical path order + * with special treatment of "trunk" and "branches" + * - same for file representations + * + * Step 4 copies the items from the temporary buckets into the final + * pack file and writes the temporary index files. + * + * Finally, after the last range of revisions, create the final indexes. + */ + +/* Maximum amount of memory we allocate for placement information during + * the pack process. + */ +#define DEFAULT_MAX_MEM (64 * 1024 * 1024) + +/* Data structure describing a node change at PATH, REVISION. + * We will sort these instances by PATH and NODE_ID such that we can combine + * similar nodes in the same reps container and store containers in path + * major order. + */ +typedef struct path_order_t +{ + /* changed path */ + svn_prefix_string__t *path; + + /* node ID for this PATH in REVISION */ + svn_fs_x__id_t node_id; + + /* when this change happened */ + svn_revnum_t revision; + + /* this is a directory node */ + svn_boolean_t is_dir; + + /* length of the expanded representation content */ + apr_int64_t expanded_size; + + /* item ID of the noderev linked to the change. May be (0, 0). */ + svn_fs_x__id_t noderev_id; + + /* item ID of the representation containing the new data. May be (0, 0). */ + svn_fs_x__id_t rep_id; +} path_order_t; + +/* Represents a reference from item FROM to item TO. FROM may be a noderev + * or rep_id while TO is (currently) always a representation. We will sort + * them by TO which allows us to collect all dependent items. + */ +typedef struct reference_t +{ + svn_fs_x__id_t to; + svn_fs_x__id_t from; +} reference_t; + +/* This structure keeps track of all the temporary data and status that + * needs to be kept around during the creation of one pack file. After + * each revision range (in case we can't process all revs at once due to + * memory restrictions), parts of the data will get re-initialized. + */ +typedef struct pack_context_t +{ + /* file system that we operate on */ + svn_fs_t *fs; + + /* cancel function to invoke at regular intervals. May be NULL */ + svn_cancel_func_t cancel_func; + + /* baton to pass to CANCEL_FUNC */ + void *cancel_baton; + + /* first revision in the shard (and future pack file) */ + svn_revnum_t shard_rev; + + /* first revision in the range to process (>= SHARD_REV) */ + svn_revnum_t start_rev; + + /* first revision after the range to process (<= SHARD_END_REV) */ + svn_revnum_t end_rev; + + /* first revision after the current shard */ + svn_revnum_t shard_end_rev; + + /* log-to-phys proto index for the whole pack file */ + apr_file_t *proto_l2p_index; + + /* phys-to-log proto index for the whole pack file */ + apr_file_t *proto_p2l_index; + + /* full shard directory path (containing the unpacked revisions) */ + const char *shard_dir; + + /* full packed shard directory path (containing the pack file + indexes) */ + const char *pack_file_dir; + + /* full pack file path (including PACK_FILE_DIR) */ + const char *pack_file_path; + + /* current write position (i.e. file length) in the pack file */ + apr_off_t pack_offset; + + /* the pack file to ultimately write all data to */ + apr_file_t *pack_file; + + /* array of svn_fs_x__p2l_entry_t *, all referring to change lists. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *changes; + + /* temp file receiving all change list items (referenced by CHANGES). + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_file_t *changes_file; + + /* array of svn_fs_x__p2l_entry_t *, all referring to file properties. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *file_props; + + /* temp file receiving all file prop items (referenced by FILE_PROPS). + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *file_props_file; + + /* array of svn_fs_x__p2l_entry_t *, all referring to directory properties. + * Will be filled in phase 2 and be cleared after each revision range. */ + apr_array_header_t *dir_props; + + /* temp file receiving all directory prop items (referenced by DIR_PROPS). + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *dir_props_file; + + /* container for all PATH members in PATH_ORDER. */ + svn_prefix_tree__t *paths; + + /* array of path_order_t *. Will be filled in phase 2 and be cleared + * after each revision range. Sorted by PATH, NODE_ID. */ + apr_array_header_t *path_order; + + /* array of reference_t* linking representations to their delta bases. + * Will be filled in phase 2 and be cleared after each revision range. + * It will be sorted by the FROM members (for rep->base rep lookup). */ + apr_array_header_t *references; + + /* array of svn_fs_x__p2l_entry_t*. Will be filled in phase 2 and be + * cleared after each revision range. During phase 3, we will set items + * to NULL that we already processed. */ + apr_array_header_t *reps; + + /* array of int, marking for each revision, the which offset their items + * begin in REPS. Will be filled in phase 2 and be cleared after + * each revision range. */ + apr_array_header_t *rev_offsets; + + /* temp file receiving all items referenced by REPS. + * Will be filled in phase 2 and be cleared after each revision range.*/ + apr_file_t *reps_file; + + /* pool used for temporary data structures that will be cleaned up when + * the next range of revisions is being processed */ + apr_pool_t *info_pool; +} pack_context_t; + +/* Create and initialize a new pack context for packing shard SHARD_REV in + * SHARD_DIR into PACK_FILE_DIR within filesystem FS. Allocate it in POOL + * and return the structure in *CONTEXT. + * + * Limit the number of items being copied per iteration to MAX_ITEMS. + * Set CANCEL_FUNC and CANCEL_BATON as well. + */ +static svn_error_t * +initialize_pack_context(pack_context_t *context, + svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_dir, + svn_revnum_t shard_rev, + int max_items, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + const char *temp_dir; + int max_revs = MIN(ffd->max_files_per_dir, max_items); + + SVN_ERR_ASSERT(shard_rev % ffd->max_files_per_dir == 0); + + /* where we will place our various temp files */ + SVN_ERR(svn_io_temp_dir(&temp_dir, pool)); + + /* store parameters */ + context->fs = fs; + context->cancel_func = cancel_func; + context->cancel_baton = cancel_baton; + + context->shard_rev = shard_rev; + context->start_rev = shard_rev; + context->end_rev = shard_rev; + context->shard_end_rev = shard_rev + ffd->max_files_per_dir; + + /* Create the new directory and pack file. */ + context->shard_dir = shard_dir; + context->pack_file_dir = pack_file_dir; + context->pack_file_path + = svn_dirent_join(pack_file_dir, PATH_PACKED, pool); + SVN_ERR(svn_io_file_open(&context->pack_file, context->pack_file_path, + APR_WRITE | APR_BUFFERED | APR_BINARY | APR_EXCL + | APR_CREATE, APR_OS_DEFAULT, pool)); + + /* Proto index files */ + SVN_ERR(svn_fs_x__l2p_proto_index_open( + &context->proto_l2p_index, + svn_dirent_join(pack_file_dir, + PATH_INDEX PATH_EXT_L2P_INDEX, + pool), + pool)); + SVN_ERR(svn_fs_x__p2l_proto_index_open( + &context->proto_p2l_index, + svn_dirent_join(pack_file_dir, + PATH_INDEX PATH_EXT_P2L_INDEX, + pool), + pool)); + + /* item buckets: one item info array and one temp file per bucket */ + context->changes = apr_array_make(pool, max_items, + sizeof(svn_fs_x__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->changes_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + context->file_props = apr_array_make(pool, max_items, + sizeof(svn_fs_x__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->file_props_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + context->dir_props = apr_array_make(pool, max_items, + sizeof(svn_fs_x__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->dir_props_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + + /* noderev and representation item bucket */ + context->rev_offsets = apr_array_make(pool, max_revs, sizeof(int)); + context->path_order = apr_array_make(pool, max_items, + sizeof(path_order_t *)); + context->references = apr_array_make(pool, max_items, + sizeof(reference_t *)); + context->reps = apr_array_make(pool, max_items, + sizeof(svn_fs_x__p2l_entry_t *)); + SVN_ERR(svn_io_open_unique_file3(&context->reps_file, NULL, temp_dir, + svn_io_file_del_on_close, pool, pool)); + + /* the pool used for temp structures */ + context->info_pool = svn_pool_create(pool); + context->paths = svn_prefix_tree__create(context->info_pool); + + return SVN_NO_ERROR; +} + +/* Clean up / free all revision range specific data and files in CONTEXT. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +reset_pack_context(pack_context_t *context, + apr_pool_t *scratch_pool) +{ + apr_array_clear(context->changes); + SVN_ERR(svn_io_file_trunc(context->changes_file, 0, scratch_pool)); + apr_array_clear(context->file_props); + SVN_ERR(svn_io_file_trunc(context->file_props_file, 0, scratch_pool)); + apr_array_clear(context->dir_props); + SVN_ERR(svn_io_file_trunc(context->dir_props_file, 0, scratch_pool)); + + apr_array_clear(context->rev_offsets); + apr_array_clear(context->path_order); + apr_array_clear(context->references); + apr_array_clear(context->reps); + SVN_ERR(svn_io_file_trunc(context->reps_file, 0, scratch_pool)); + + svn_pool_clear(context->info_pool); + + return SVN_NO_ERROR; +} + +/* Call this after the last revision range. It will finalize all index files + * for CONTEXT and close any open files. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +close_pack_context(pack_context_t *context, + apr_pool_t *scratch_pool) +{ + const char *proto_l2p_index_path; + const char *proto_p2l_index_path; + + /* need the file names for the actual index creation call further down */ + SVN_ERR(svn_io_file_name_get(&proto_l2p_index_path, + context->proto_l2p_index, scratch_pool)); + SVN_ERR(svn_io_file_name_get(&proto_p2l_index_path, + context->proto_p2l_index, scratch_pool)); + + /* finalize proto index files */ + SVN_ERR(svn_io_file_close(context->proto_l2p_index, scratch_pool)); + SVN_ERR(svn_io_file_close(context->proto_p2l_index, scratch_pool)); + + /* Append the actual index data to the pack file. */ + SVN_ERR(svn_fs_x__add_index_data(context->fs, context->pack_file, + proto_l2p_index_path, + proto_p2l_index_path, + context->shard_rev, + scratch_pool)); + + /* remove proto index files */ + SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, scratch_pool)); + SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, scratch_pool)); + + SVN_ERR(svn_io_file_close(context->pack_file, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Efficiently copy SIZE bytes from SOURCE to DEST. Invoke the CANCEL_FUNC + * from CONTEXT at regular intervals. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +copy_file_data(pack_context_t *context, + apr_file_t *dest, + apr_file_t *source, + apr_off_t size, + apr_pool_t *scratch_pool) +{ + /* most non-representation items will be small. Minimize the buffer + * and infrastructure overhead in that case. */ + enum { STACK_BUFFER_SIZE = 1024 }; + + if (size < STACK_BUFFER_SIZE) + { + /* copy small data using a fixed-size buffer on stack */ + char buffer[STACK_BUFFER_SIZE]; + SVN_ERR(svn_io_file_read_full2(source, buffer, (apr_size_t)size, + NULL, NULL, scratch_pool)); + SVN_ERR(svn_io_file_write_full(dest, buffer, (apr_size_t)size, + NULL, scratch_pool)); + } + else + { + /* use streaming copies for larger data blocks. That may require + * the allocation of larger buffers and we should make sure that + * this extra memory is released asap. */ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + apr_pool_t *copypool = svn_pool_create(scratch_pool); + char *buffer = apr_palloc(copypool, ffd->block_size); + + while (size) + { + apr_size_t to_copy = (apr_size_t)(MIN(size, ffd->block_size)); + if (context->cancel_func) + SVN_ERR(context->cancel_func(context->cancel_baton)); + + SVN_ERR(svn_io_file_read_full2(source, buffer, to_copy, + NULL, NULL, scratch_pool)); + SVN_ERR(svn_io_file_write_full(dest, buffer, to_copy, + NULL, scratch_pool)); + + size -= to_copy; + } + + svn_pool_destroy(copypool); + } + + return SVN_NO_ERROR; +} + +/* Writes SIZE bytes, all 0, to DEST. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_null_bytes(apr_file_t *dest, + apr_off_t size, + apr_pool_t *scratch_pool) +{ + /* Have a collection of high-quality, easy to access NUL bytes handy. */ + enum { BUFFER_SIZE = 1024 }; + static const char buffer[BUFFER_SIZE] = { 0 }; + + /* copy SIZE of them into the file's buffer */ + while (size) + { + apr_size_t to_write = MIN(size, BUFFER_SIZE); + SVN_ERR(svn_io_file_write_full(dest, buffer, to_write, NULL, + scratch_pool)); + size -= to_write; + } + + return SVN_NO_ERROR; +} + +/* Copy the "simple" item (changed paths list or property representation) + * from the current position in REV_FILE to TEMP_FILE using CONTEXT. Add + * a copy of ENTRY to ENTRIES but with an updated offset value that points + * to the copy destination in TEMP_FILE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +copy_item_to_temp(pack_context_t *context, + apr_array_header_t *entries, + apr_file_t *temp_file, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + svn_fs_x__p2l_entry_t *new_entry + = svn_fs_x__p2l_entry_dup(entry, context->info_pool); + + SVN_ERR(svn_fs_x__get_file_offset(&new_entry->offset, temp_file, + scratch_pool)); + APR_ARRAY_PUSH(entries, svn_fs_x__p2l_entry_t *) = new_entry; + + SVN_ERR(copy_file_data(context, temp_file, rev_file->file, entry->size, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Return the offset within CONTEXT->REPS that corresponds to item + * ITEM_INDEX in REVISION. + */ +static int +get_item_array_index(pack_context_t *context, + svn_revnum_t revision, + apr_int64_t item_index) +{ + assert(revision >= context->start_rev); + return (int)item_index + APR_ARRAY_IDX(context->rev_offsets, + revision - context->start_rev, + int); +} + +/* Write INFO to the correct position in CONTEXT->REPS. The latter may + * need auto-expanding. Overwriting an array element is not allowed. + */ +static void +add_item_rep_mapping(pack_context_t *context, + svn_fs_x__p2l_entry_t *entry) +{ + int idx; + assert(entry->item_count == 1); + + /* index of INFO */ + idx = get_item_array_index(context, + entry->items[0].change_set, + entry->items[0].number); + + /* make sure the index exists in the array */ + while (context->reps->nelts <= idx) + APR_ARRAY_PUSH(context->reps, void *) = NULL; + + /* set the element. If there is already an entry, there are probably + * two items claiming to be the same -> bail out */ + assert(!APR_ARRAY_IDX(context->reps, idx, void *)); + APR_ARRAY_IDX(context->reps, idx, void *) = entry; +} + +/* Return the P2L entry from CONTEXT->REPS for the given ID. If there is + * none (or not anymore), return NULL. If RESET has been specified, set + * the array entry to NULL after returning the entry. + */ +static svn_fs_x__p2l_entry_t * +get_item(pack_context_t *context, + const svn_fs_x__id_t *id, + svn_boolean_t reset) +{ + svn_fs_x__p2l_entry_t *result = NULL; + svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set); + if (id->number && revision >= context->start_rev) + { + int idx = get_item_array_index(context, revision, id->number); + if (context->reps->nelts > idx) + { + result = APR_ARRAY_IDX(context->reps, idx, void *); + if (result && reset) + APR_ARRAY_IDX(context->reps, idx, void *) = NULL; + } + } + + return result; +} + +/* Copy representation item identified by ENTRY from the current position + * in REV_FILE into CONTEXT->REPS_FILE. Add all tracking into needed by + * our placement algorithm to CONTEXT. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +copy_rep_to_temp(pack_context_t *context, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + svn_fs_x__rep_header_t *rep_header; + apr_off_t source_offset = entry->offset; + + /* create a copy of ENTRY, make it point to the copy destination and + * store it in CONTEXT */ + entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool); + SVN_ERR(svn_fs_x__get_file_offset(&entry->offset, context->reps_file, + scratch_pool)); + add_item_rep_mapping(context, entry); + + /* read & parse the representation header */ + SVN_ERR(svn_fs_x__read_rep_header(&rep_header, rev_file->stream, + scratch_pool, scratch_pool)); + + /* if the representation is a delta against some other rep, link the two */ + if ( rep_header->type == svn_fs_x__rep_delta + && rep_header->base_revision >= context->start_rev) + { + reference_t *reference = apr_pcalloc(context->info_pool, + sizeof(*reference)); + reference->from = entry->items[0]; + reference->to.change_set + = svn_fs_x__change_set_by_rev(rep_header->base_revision); + reference->to.number = rep_header->base_item_index; + APR_ARRAY_PUSH(context->references, reference_t *) = reference; + } + + /* copy the whole rep (including header!) to our temp file */ + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &source_offset, + scratch_pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file->file, + entry->size, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Directories first, dirs / files sorted by name in reverse lexical order. + * This maximizes the chance of two items being located close to one another + * in *all* pack files independent of their change order. It also groups + * multi-project repos nicely according to their sub-projects. The reverse + * order aspect gives "trunk" preference over "tags" and "branches", so + * trunk-related items are more likely to be contiguous. + */ +static int +compare_dir_entries(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value; + const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value; + + if (lhs->kind != rhs->kind) + return lhs->kind == svn_node_dir ? -1 : 1; + + return strcmp(lhs->name, rhs->name); +} + +apr_array_header_t * +svn_fs_x__order_dir_entries(svn_fs_t *fs, + apr_hash_t *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *ordered + = svn_sort__hash(directory, compare_dir_entries, scratch_pool); + + apr_array_header_t *result + = apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *)); + + int i; + for (i = 0; i < ordered->nelts; ++i) + APR_ARRAY_PUSH(result, svn_fs_dirent_t *) + = APR_ARRAY_IDX(ordered, i, svn_sort__item_t).value; + + return result; +} + +/* Return a duplicate of the the ORIGINAL path and with special sub-strins + * (e.g. "trunk") modified in such a way that have a lower lexicographic + * value than any other "normal" file name. + */ +static const char * +tweak_path_for_ordering(const char *original, + apr_pool_t *pool) +{ + /* We may add further special cases as needed. */ + enum {SPECIAL_COUNT = 2}; + static const char *special[SPECIAL_COUNT] = {"trunk", "branch"}; + char *pos; + char *path = apr_pstrdup(pool, original); + int i; + + /* Replace the first char of any "special" sub-string we find by + * a control char, i.e. '\1' .. '\31'. In the rare event that this + * would clash with existing paths, no data will be lost but merely + * the node ordering will be sub-optimal. + */ + for (i = 0; i < SPECIAL_COUNT; ++i) + for (pos = strstr(path, special[i]); + pos; + pos = strstr(pos + 1, special[i])) + { + *pos = (char)(i + '\1'); + } + + return path; +} + +/* Copy node revision item identified by ENTRY from the current position + * in REV_FILE into CONTEXT->REPS_FILE. Add all tracking into needed by + * our placement algorithm to CONTEXT. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +copy_node_to_temp(pack_context_t *context, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + path_order_t *path_order = apr_pcalloc(context->info_pool, + sizeof(*path_order)); + svn_fs_x__noderev_t *noderev; + const char *sort_path; + apr_off_t source_offset = entry->offset; + + /* read & parse noderev */ + SVN_ERR(svn_fs_x__read_noderev(&noderev, rev_file->stream, scratch_pool, + scratch_pool)); + + /* create a copy of ENTRY, make it point to the copy destination and + * store it in CONTEXT */ + entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool); + SVN_ERR(svn_fs_x__get_file_offset(&entry->offset, context->reps_file, + scratch_pool)); + add_item_rep_mapping(context, entry); + + /* copy the noderev to our temp file */ + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &source_offset, + scratch_pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file->file, + entry->size, scratch_pool)); + + /* if the node has a data representation, make that the node's "base". + * This will (often) cause the noderev to be placed right in front of + * its data representation. */ + + if (noderev->data_rep + && svn_fs_x__get_revnum(noderev->data_rep->id.change_set) + >= context->start_rev) + { + reference_t *reference = apr_pcalloc(context->info_pool, + sizeof(*reference)); + reference->from = entry->items[0]; + reference->to.change_set = noderev->data_rep->id.change_set; + reference->to.number = noderev->data_rep->id.number; + APR_ARRAY_PUSH(context->references, reference_t *) = reference; + + path_order->rep_id = reference->to; + path_order->expanded_size = noderev->data_rep->expanded_size; + } + + /* Sort path is the key used for ordering noderevs and associated reps. + * It will not be stored in the final pack file. */ + sort_path = tweak_path_for_ordering(noderev->created_path, scratch_pool); + path_order->path = svn_prefix_string__create(context->paths, sort_path); + path_order->node_id = noderev->node_id; + path_order->revision = svn_fs_x__get_revnum(noderev->noderev_id.change_set); + path_order->is_dir = noderev->kind == svn_node_dir; + path_order->noderev_id = noderev->noderev_id; + APR_ARRAY_PUSH(context->path_order, path_order_t *) = path_order; + + return SVN_NO_ERROR; +} + +/* implements compare_fn_t. Place LHS before RHS, if the latter is older. + */ +static int +compare_p2l_info(const svn_fs_x__p2l_entry_t * const * lhs, + const svn_fs_x__p2l_entry_t * const * rhs) +{ + assert(*lhs != *rhs); + if ((*lhs)->item_count == 0) + return (*lhs)->item_count == 0 ? 0 : -1; + if ((*lhs)->item_count == 0) + return 1; + + if ((*lhs)->items[0].change_set == (*rhs)->items[0].change_set) + return (*lhs)->items[0].number > (*rhs)->items[0].number ? -1 : 1; + + return (*lhs)->items[0].change_set > (*rhs)->items[0].change_set ? -1 : 1; +} + +/* Sort svn_fs_x__p2l_entry_t * array ENTRIES by age. Place the latest + * items first. + */ +static void +sort_items(apr_array_header_t *entries) +{ + svn_sort__array(entries, + (int (*)(const void *, const void *))compare_p2l_info); +} + +/* implements compare_fn_t. Sort descending by PATH, NODE_ID and REVISION. + */ +static int +compare_path_order(const path_order_t * const * lhs_p, + const path_order_t * const * rhs_p) +{ + const path_order_t * lhs = *lhs_p; + const path_order_t * rhs = *rhs_p; + + /* cluster all directories */ + int diff = rhs->is_dir - lhs->is_dir; + if (diff) + return diff; + + /* lexicographic order on path and node (i.e. latest first) */ + diff = svn_prefix_string__compare(lhs->path, rhs->path); + if (diff) + return diff; + + /* reverse order on node (i.e. latest first) */ + diff = svn_fs_x__id_compare(&rhs->node_id, &lhs->node_id); + if (diff) + return diff; + + /* reverse order on revision (i.e. latest first) */ + if (lhs->revision != rhs->revision) + return lhs->revision < rhs->revision ? 1 : -1; + + return 0; +} + +/* implements compare_fn_t. Sort ascending by TO, FROM. + */ +static int +compare_references(const reference_t * const * lhs_p, + const reference_t * const * rhs_p) +{ + const reference_t * lhs = *lhs_p; + const reference_t * rhs = *rhs_p; + + int diff = svn_fs_x__id_compare(&lhs->to, &rhs->to); + return diff ? diff : svn_fs_x__id_compare(&lhs->from, &rhs->from); +} + +/* Order the data collected in CONTEXT such that we can place them in the + * desired order. + */ +static void +sort_reps(pack_context_t *context) +{ + svn_sort__array(context->path_order, + (int (*)(const void *, const void *))compare_path_order); + svn_sort__array(context->references, + (int (*)(const void *, const void *))compare_references); +} + +/* Return the remaining unused bytes in the current block in CONTEXT's + * pack file. + */ +static apr_ssize_t +get_block_left(pack_context_t *context) +{ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + return ffd->block_size - (context->pack_offset % ffd->block_size); +} + +/* To prevent items from overlapping a block boundary, we will usually + * put them into the next block and top up the old one with NUL bytes. + * Pad CONTEXT's pack file to the end of the current block, if that padding + * is short enough. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +auto_pad_block(pack_context_t *context, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + + /* This is the maximum number of bytes "wasted" that way per block. + * Larger items will cross the block boundaries. */ + const apr_off_t max_padding = MAX(ffd->block_size / 50, 512); + + /* Is wasted space small enough to align the current item to the next + * block? */ + apr_off_t padding = get_block_left(context); + + if (padding < max_padding) + { + /* Yes. To up with NUL bytes and don't forget to create + * an P2L index entry marking this section as unused. */ + svn_fs_x__p2l_entry_t null_entry; + + null_entry.offset = context->pack_offset; + null_entry.size = padding; + null_entry.type = SVN_FS_X__ITEM_TYPE_UNUSED; + null_entry.fnv1_checksum = 0; + null_entry.item_count = 0; + null_entry.items = NULL; + + SVN_ERR(write_null_bytes(context->pack_file, padding, scratch_pool)); + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, &null_entry, scratch_pool)); + context->pack_offset += padding; + } + + return SVN_NO_ERROR; +} + +/* Return the index of the first entry in CONTEXT->REFERENCES that + * references ITEM->ITEMS[0] if such entries exist. All matching items + * will be consecutive. + */ +static int +find_first_reference(pack_context_t *context, + svn_fs_x__p2l_entry_t *item) +{ + int lower = 0; + int upper = context->references->nelts - 1; + + while (lower <= upper) + { + int current = lower + (upper - lower) / 2; + reference_t *reference + = APR_ARRAY_IDX(context->references, current, reference_t *); + + if (svn_fs_x__id_compare(&reference->to, item->items) < 0) + lower = current + 1; + else + upper = current - 1; + } + + return lower; +} + +/* Check whether entry number IDX in CONTEXT->REFERENCES references ITEM. + */ +static svn_boolean_t +is_reference_match(pack_context_t *context, + int idx, + svn_fs_x__p2l_entry_t *item) +{ + reference_t *reference; + if (context->references->nelts <= idx) + return FALSE; + + reference = APR_ARRAY_IDX(context->references, idx, reference_t *); + return svn_fs_x__id_eq(&reference->to, item->items); +} + +/* Starting at IDX in CONTEXT->PATH_ORDER, select all representations and + * noderevs that should be placed into the same container, respectively. + * Append the path_order_t * elements encountered in SELECTED, the + * svn_fs_x__p2l_entry_t * of the representations that should be placed + * into the same reps container will be appended to REP_PARTS and the + * svn_fs_x__p2l_entry_t * of the noderevs referencing those reps will + * be appended to NODE_PARTS. + * + * Remove all returned items from the CONTEXT->REPS container and prevent + * them from being placed a second time later on. That also means that the + * caller has to place all items returned. + */ +static svn_error_t * +select_reps(pack_context_t *context, + int idx, + apr_array_header_t *selected, + apr_array_header_t *node_parts, + apr_array_header_t *rep_parts) +{ + apr_array_header_t *path_order = context->path_order; + path_order_t *start_path = APR_ARRAY_IDX(path_order, idx, path_order_t *); + + svn_fs_x__p2l_entry_t *node_part; + svn_fs_x__p2l_entry_t *rep_part; + svn_fs_x__p2l_entry_t *depending; + int i, k; + + /* collect all path_order records as well as rep and noderev items + * that occupy the same path with the same node. */ + for (; idx < path_order->nelts; ++idx) + { + path_order_t *current_path + = APR_ARRAY_IDX(path_order, idx, path_order_t *); + + if (!svn_fs_x__id_eq(&start_path->node_id, ¤t_path->node_id)) + break; + + APR_ARRAY_IDX(path_order, idx, path_order_t *) = NULL; + node_part = get_item(context, ¤t_path->noderev_id, TRUE); + rep_part = get_item(context, ¤t_path->rep_id, TRUE); + + if (node_part && rep_part) + APR_ARRAY_PUSH(selected, path_order_t *) = current_path; + + if (node_part) + APR_ARRAY_PUSH(node_parts, svn_fs_x__p2l_entry_t *) = node_part; + if (rep_part) + APR_ARRAY_PUSH(rep_parts, svn_fs_x__p2l_entry_t *) = rep_part; + } + + /* collect depending reps and noderevs that reference any of the collected + * reps */ + for (i = 0; i < rep_parts->nelts; ++i) + { + rep_part = APR_ARRAY_IDX(rep_parts, i, svn_fs_x__p2l_entry_t*); + for (k = find_first_reference(context, rep_part); + is_reference_match(context, k, rep_part); + ++k) + { + reference_t *reference + = APR_ARRAY_IDX(context->references, k, reference_t *); + + depending = get_item(context, &reference->from, TRUE); + if (!depending) + continue; + + if (depending->type == SVN_FS_X__ITEM_TYPE_NODEREV) + APR_ARRAY_PUSH(node_parts, svn_fs_x__p2l_entry_t *) = depending; + else + APR_ARRAY_PUSH(rep_parts, svn_fs_x__p2l_entry_t *) = depending; + } + } + + return SVN_NO_ERROR; +} + +/* Return TRUE, if all path_order_t * in SELECTED reference contents that is + * not longer than LIMIT. + */ +static svn_boolean_t +reps_fit_into_containers(apr_array_header_t *selected, + apr_uint64_t limit) +{ + int i; + for (i = 0; i < selected->nelts; ++i) + if (APR_ARRAY_IDX(selected, i, path_order_t *)->expanded_size > limit) + return FALSE; + + return TRUE; +} + +/* Write the *CONTAINER containing the noderevs described by the + * svn_fs_x__p2l_entry_t * in ITEMS to the pack file on CONTEXT. + * Append a P2L entry for the container to CONTAINER->REPS. + * Afterwards, clear ITEMS and re-allocate *CONTAINER in CONTAINER_POOL + * so the caller may fill them again. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_nodes_container(pack_context_t *context, + svn_fs_x__noderevs_t **container, + apr_array_header_t *items, + apr_pool_t *container_pool, + apr_pool_t *scratch_pool) +{ + int i; + apr_off_t offset = 0; + svn_fs_x__p2l_entry_t *container_entry; + svn_stream_t *pack_stream; + + if (items->nelts == 0) + return SVN_NO_ERROR; + + /* serialize container */ + container_entry = apr_palloc(context->info_pool, sizeof(*container_entry)); + pack_stream = svn_checksum__wrap_write_stream_fnv1a_32x4 + (&container_entry->fnv1_checksum, + svn_stream_from_aprfile2(context->pack_file, + TRUE, scratch_pool), + scratch_pool); + SVN_ERR(svn_fs_x__write_noderevs_container(pack_stream, *container, + scratch_pool)); + SVN_ERR(svn_stream_close(pack_stream)); + SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, + scratch_pool)); + + /* replace first noderev item in ENTRIES with the container + and set all others to NULL */ + container_entry->offset = context->pack_offset; + container_entry->size = offset - container_entry->offset; + container_entry->type = SVN_FS_X__ITEM_TYPE_NODEREVS_CONT; + container_entry->item_count = items->nelts; + container_entry->items = apr_palloc(context->info_pool, + sizeof(svn_fs_x__id_t) * container_entry->item_count); + + for (i = 0; i < items->nelts; ++i) + container_entry->items[i] + = APR_ARRAY_IDX(items, i, svn_fs_x__p2l_entry_t *)->items[0]; + + context->pack_offset = offset; + APR_ARRAY_PUSH(context->reps, svn_fs_x__p2l_entry_t *) + = container_entry; + + /* Write P2L index for copied items, i.e. the 1 container */ + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, container_entry, scratch_pool)); + + svn_pool_clear(container_pool); + *container = svn_fs_x__noderevs_create(16, container_pool); + apr_array_clear(items); + + return SVN_NO_ERROR; +} + +/* Read the noderevs given by the svn_fs_x__p2l_entry_t * in NODE_PARTS + * from TEMP_FILE and add them to *CONTAINER and NODES_IN_CONTAINER. + * Whenever the container grows bigger than the current block in CONTEXT, + * write the data to disk and continue in the next block. + * + * Use CONTAINER_POOL to re-allocate the *CONTAINER as necessary and + * SCRATCH_POOL to temporary allocations. + */ +static svn_error_t * +store_nodes(pack_context_t *context, + apr_file_t *temp_file, + apr_array_header_t *node_parts, + svn_fs_x__noderevs_t **container, + apr_array_header_t *nodes_in_container, + apr_pool_t *container_pool, + apr_pool_t *scratch_pool) +{ + int i; + + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_stream_t *stream + = svn_stream_from_aprfile2(temp_file, TRUE, scratch_pool); + + /* number of bytes in the current block not being spent on fixed-size + items (i.e. those not put into the container). */ + apr_size_t capacity_left = get_block_left(context); + + /* Estimated noderev container size */ + apr_size_t last_container_size = 0, container_size = 0; + + /* Estimate extra capacity we will gain from container compression. */ + apr_size_t pack_savings = 0; + for (i = 0; i < node_parts->nelts; ++i) + { + svn_fs_x__noderev_t *noderev; + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(node_parts, i, svn_fs_x__p2l_entry_t *); + + /* if we reached the limit, check whether we saved some space + through the container. */ + if (capacity_left + pack_savings < container_size + entry->size) + container_size = svn_fs_x__noderevs_estimate_size(*container); + + /* If necessary and the container is large enough, try harder + by actually serializing the container and determine current + savings due to compression. */ + if ( capacity_left + pack_savings < container_size + entry->size + && container_size > last_container_size + 2000) + { + svn_stringbuf_t *serialized + = svn_stringbuf_create_ensure(container_size, iterpool); + svn_stream_t *temp_stream + = svn_stream_from_stringbuf(serialized, iterpool); + + SVN_ERR(svn_fs_x__write_noderevs_container(temp_stream, *container, + iterpool)); + SVN_ERR(svn_stream_close(temp_stream)); + + last_container_size = container_size; + pack_savings = container_size - serialized->len; + } + + /* still doesn't fit? -> block is full. Flush */ + if ( capacity_left + pack_savings < container_size + entry->size + && nodes_in_container->nelts < 2) + { + SVN_ERR(auto_pad_block(context, iterpool)); + capacity_left = get_block_left(context); + } + + /* still doesn't fit? -> block is full. Flush */ + if (capacity_left + pack_savings < container_size + entry->size) + { + SVN_ERR(write_nodes_container(context, container, + nodes_in_container, container_pool, + iterpool)); + + capacity_left = get_block_left(context); + pack_savings = 0; + container_size = 0; + } + + /* item will fit into the block. */ + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); + SVN_ERR(svn_fs_x__read_noderev(&noderev, stream, iterpool, iterpool)); + svn_fs_x__noderevs_add(*container, noderev); + + container_size += entry->size; + APR_ARRAY_PUSH(nodes_in_container, svn_fs_x__p2l_entry_t *) = entry; + + svn_pool_clear(iterpool); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* Finalize CONTAINER and write it to CONTEXT's pack file. + * Append an P2L entry containing the given SUB_ITEMS to NEW_ENTRIES. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_reps_container(pack_context_t *context, + svn_fs_x__reps_builder_t *container, + apr_array_header_t *sub_items, + apr_array_header_t *new_entries, + apr_pool_t *scratch_pool) +{ + apr_off_t offset = 0; + svn_fs_x__p2l_entry_t container_entry; + + svn_stream_t *pack_stream + = svn_checksum__wrap_write_stream_fnv1a_32x4 + (&container_entry.fnv1_checksum, + svn_stream_from_aprfile2(context->pack_file, + TRUE, scratch_pool), + scratch_pool); + + SVN_ERR(svn_fs_x__write_reps_container(pack_stream, container, + scratch_pool)); + SVN_ERR(svn_stream_close(pack_stream)); + SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, + scratch_pool)); + + container_entry.offset = context->pack_offset; + container_entry.size = offset - container_entry.offset; + container_entry.type = SVN_FS_X__ITEM_TYPE_REPS_CONT; + container_entry.item_count = sub_items->nelts; + container_entry.items = (svn_fs_x__id_t *)sub_items->elts; + + context->pack_offset = offset; + APR_ARRAY_PUSH(new_entries, svn_fs_x__p2l_entry_t *) + = svn_fs_x__p2l_entry_dup(&container_entry, context->info_pool); + + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, &container_entry, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the (property) representations identified by svn_fs_x__p2l_entry_t + * elements in ENTRIES from TEMP_FILE, aggregate them and write them into + * CONTEXT->PACK_FILE. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_reps_containers(pack_context_t *context, + apr_array_header_t *entries, + apr_file_t *temp_file, + apr_array_header_t *new_entries, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *container_pool = svn_pool_create(scratch_pool); + int i; + + apr_ssize_t block_left = get_block_left(context); + + svn_fs_x__reps_builder_t *container + = svn_fs_x__reps_builder_create(context->fs, container_pool); + apr_array_header_t *sub_items + = apr_array_make(scratch_pool, 64, sizeof(svn_fs_x__id_t)); + svn_fs_x__revision_file_t *file; + + SVN_ERR(svn_fs_x__wrap_temp_rev_file(&file, context->fs, temp_file, + scratch_pool)); + + /* copy all items in strict order */ + for (i = entries->nelts-1; i >= 0; --i) + { + svn_fs_x__representation_t representation = { 0 }; + svn_stringbuf_t *contents; + svn_stream_t *stream; + apr_size_t list_index; + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t *); + + if ((block_left < entry->size) && sub_items->nelts) + { + block_left = get_block_left(context) + - svn_fs_x__reps_estimate_size(container); + } + + if ((block_left < entry->size) && sub_items->nelts) + { + SVN_ERR(write_reps_container(context, container, sub_items, + new_entries, iterpool)); + + apr_array_clear(sub_items); + svn_pool_clear(container_pool); + container = svn_fs_x__reps_builder_create(context->fs, + container_pool); + block_left = get_block_left(context); + } + + /* still enough space in current block? */ + if (block_left < entry->size) + { + SVN_ERR(auto_pad_block(context, iterpool)); + block_left = get_block_left(context); + } + + assert(entry->item_count == 1); + representation.id = entry->items[0]; + + /* select the change list in the source file, parse it and add it to + * the container */ + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, + iterpool)); + SVN_ERR(svn_fs_x__get_representation_length(&representation.size, + &representation.expanded_size, + context->fs, file, + entry, iterpool)); + SVN_ERR(svn_fs_x__get_contents(&stream, context->fs, &representation, + FALSE, iterpool)); + contents = svn_stringbuf_create_ensure(representation.expanded_size, + iterpool); + contents->len = representation.expanded_size; + + /* The representation is immutable. Read it normally. */ + SVN_ERR(svn_stream_read_full(stream, contents->data, &contents->len)); + SVN_ERR(svn_stream_close(stream)); + + SVN_ERR(svn_fs_x__reps_add(&list_index, container, + svn_stringbuf__morph_into_string(contents))); + SVN_ERR_ASSERT(list_index == sub_items->nelts); + block_left -= entry->size; + + APR_ARRAY_PUSH(sub_items, svn_fs_x__id_t) = entry->items[0]; + + svn_pool_clear(iterpool); + } + + if (sub_items->nelts) + SVN_ERR(write_reps_container(context, container, sub_items, + new_entries, iterpool)); + + svn_pool_destroy(iterpool); + svn_pool_destroy(container_pool); + + return SVN_NO_ERROR; +} + +/* Return TRUE if the estimated size of the NODES_IN_CONTAINER plus the + * representations given as svn_fs_x__p2l_entry_t * in ENTRIES may exceed + * the space left in the current block. + */ +static svn_boolean_t +should_flush_nodes_container(pack_context_t *context, + svn_fs_x__noderevs_t *nodes_container, + apr_array_header_t *entries) +{ + apr_ssize_t block_left = get_block_left(context); + apr_ssize_t rep_sum = 0; + apr_ssize_t container_size + = svn_fs_x__noderevs_estimate_size(nodes_container); + + int i; + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t *); + rep_sum += entry->size; + } + + return block_left < rep_sum + container_size; +} + +/* Read the contents of the first COUNT non-NULL, non-empty items in ITEMS + * from TEMP_FILE and write them to CONTEXT->PACK_FILE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +store_items(pack_context_t *context, + apr_file_t *temp_file, + apr_array_header_t *items, + int count, + apr_pool_t *scratch_pool) +{ + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* copy all items in strict order */ + for (i = 0; i < count; ++i) + { + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(items, i, svn_fs_x__p2l_entry_t *); + if (!entry + || entry->type == SVN_FS_X__ITEM_TYPE_UNUSED + || entry->item_count == 0) + continue; + + /* select the item in the source file and copy it into the target + * pack file */ + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, + iterpool)); + SVN_ERR(copy_file_data(context, context->pack_file, temp_file, + entry->size, iterpool)); + + /* write index entry and update current position */ + entry->offset = context->pack_offset; + context->pack_offset += entry->size; + + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, entry, iterpool)); + + APR_ARRAY_PUSH(context->reps, svn_fs_x__p2l_entry_t *) = entry; + svn_pool_clear(iterpool); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Copy (append) the items identified by svn_fs_x__p2l_entry_t * elements + * in ENTRIES strictly in order from TEMP_FILE into CONTEXT->PACK_FILE. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +copy_reps_from_temp(pack_context_t *context, + apr_file_t *temp_file, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *container_pool = svn_pool_create(scratch_pool); + apr_array_header_t *path_order = context->path_order; + apr_array_header_t *reps = context->reps; + apr_array_header_t *selected = apr_array_make(scratch_pool, 16, + path_order->elt_size); + apr_array_header_t *node_parts = apr_array_make(scratch_pool, 16, + reps->elt_size); + apr_array_header_t *rep_parts = apr_array_make(scratch_pool, 16, + reps->elt_size); + apr_array_header_t *nodes_in_container = apr_array_make(scratch_pool, 16, + reps->elt_size); + int i, k; + int initial_reps_count = reps->nelts; + + /* 1 container for all noderevs in the current block. We will try to + * not write it to disk until the current block fills up, i.e. aim for + * a single noderevs container per block. */ + svn_fs_x__noderevs_t *nodes_container + = svn_fs_x__noderevs_create(16, container_pool); + + /* copy items in path order. Create block-sized containers. */ + for (i = 0; i < path_order->nelts; ++i) + { + if (APR_ARRAY_IDX(path_order, i, path_order_t *) == NULL) + continue; + + /* Collect reps to combine and all noderevs referencing them */ + SVN_ERR(select_reps(context, i, selected, node_parts, rep_parts)); + + /* store the noderevs container in front of the reps */ + SVN_ERR(store_nodes(context, temp_file, node_parts, &nodes_container, + nodes_in_container, container_pool, iterpool)); + + /* actually flush the noderevs to disk if the reps container is likely + * to fill the block, i.e. no further noderevs will be added to the + * nodes container. */ + if (should_flush_nodes_container(context, nodes_container, node_parts)) + SVN_ERR(write_nodes_container(context, &nodes_container, + nodes_in_container, container_pool, + iterpool)); + + /* if all reps are short enough put them into one container. + * Otherwise, just store all containers here. */ + if (reps_fit_into_containers(selected, 2 * ffd->block_size)) + SVN_ERR(write_reps_containers(context, rep_parts, temp_file, + context->reps, iterpool)); + else + SVN_ERR(store_items(context, temp_file, rep_parts, rep_parts->nelts, + iterpool)); + + /* processed all items */ + apr_array_clear(selected); + apr_array_clear(node_parts); + apr_array_clear(rep_parts); + + svn_pool_clear(iterpool); + } + + /* flush noderevs container to disk */ + if (nodes_in_container->nelts) + SVN_ERR(write_nodes_container(context, &nodes_container, + nodes_in_container, container_pool, + iterpool)); + + /* copy all items in strict order */ + SVN_ERR(store_items(context, temp_file, reps, initial_reps_count, + scratch_pool)); + + /* vaccum ENTRIES array: eliminate NULL entries */ + for (i = 0, k = 0; i < reps->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(reps, i, svn_fs_x__p2l_entry_t *); + if (entry) + { + APR_ARRAY_IDX(reps, k, svn_fs_x__p2l_entry_t *) = entry; + ++k; + } + } + reps->nelts = k; + + svn_pool_destroy(iterpool); + svn_pool_destroy(container_pool); + + return SVN_NO_ERROR; +} + +/* Finalize CONTAINER and write it to CONTEXT's pack file. + * Append an P2L entry containing the given SUB_ITEMS to NEW_ENTRIES. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_changes_container(pack_context_t *context, + svn_fs_x__changes_t *container, + apr_array_header_t *sub_items, + apr_array_header_t *new_entries, + apr_pool_t *scratch_pool) +{ + apr_off_t offset = 0; + svn_fs_x__p2l_entry_t container_entry; + + svn_stream_t *pack_stream + = svn_checksum__wrap_write_stream_fnv1a_32x4 + (&container_entry.fnv1_checksum, + svn_stream_from_aprfile2(context->pack_file, + TRUE, scratch_pool), + scratch_pool); + + SVN_ERR(svn_fs_x__write_changes_container(pack_stream, + container, + scratch_pool)); + SVN_ERR(svn_stream_close(pack_stream)); + SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, + scratch_pool)); + + container_entry.offset = context->pack_offset; + container_entry.size = offset - container_entry.offset; + container_entry.type = SVN_FS_X__ITEM_TYPE_CHANGES_CONT; + container_entry.item_count = sub_items->nelts; + container_entry.items = (svn_fs_x__id_t *)sub_items->elts; + + context->pack_offset = offset; + APR_ARRAY_PUSH(new_entries, svn_fs_x__p2l_entry_t *) + = svn_fs_x__p2l_entry_dup(&container_entry, context->info_pool); + + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, &container_entry, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the change lists identified by svn_fs_x__p2l_entry_t * elements + * in ENTRIES strictly in from TEMP_FILE, aggregate them and write them + * into CONTEXT->PACK_FILE. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_changes_containers(pack_context_t *context, + apr_array_header_t *entries, + apr_file_t *temp_file, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *container_pool = svn_pool_create(scratch_pool); + int i; + + apr_ssize_t block_left = get_block_left(context); + apr_ssize_t estimated_addition = 0; + + svn_fs_x__changes_t *container + = svn_fs_x__changes_create(1000, container_pool); + apr_array_header_t *sub_items + = apr_array_make(scratch_pool, 64, sizeof(svn_fs_x__id_t)); + apr_array_header_t *new_entries + = apr_array_make(context->info_pool, 16, entries->elt_size); + svn_stream_t *temp_stream + = svn_stream_from_aprfile2(temp_file, TRUE, scratch_pool); + + /* copy all items in strict order */ + for (i = entries->nelts-1; i >= 0; --i) + { + apr_array_header_t *changes; + apr_size_t list_index; + svn_fs_x__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t *); + + /* zip compression alone will significantly reduce the size of large + * change lists. So, we will probably need even less than this estimate. + */ + apr_ssize_t estimated_size = (entry->size / 5) + 250; + + /* If necessary and enough data has been added to the container since + * the last test, try harder by actually serializing the container and + * determine current savings due to compression. */ + if (block_left < estimated_size && estimated_addition > 2000) + { + svn_stringbuf_t *serialized + = svn_stringbuf_create_ensure(get_block_left(context), iterpool); + svn_stream_t *memory_stream + = svn_stream_from_stringbuf(serialized, iterpool); + + SVN_ERR(svn_fs_x__write_changes_container(memory_stream, + container, iterpool)); + SVN_ERR(svn_stream_close(temp_stream)); + + block_left = get_block_left(context) - serialized->len; + estimated_addition = 0; + } + + if ((block_left < estimated_size) && sub_items->nelts) + { + SVN_ERR(write_changes_container(context, container, sub_items, + new_entries, iterpool)); + + apr_array_clear(sub_items); + svn_pool_clear(container_pool); + container = svn_fs_x__changes_create(1000, container_pool); + block_left = get_block_left(context); + estimated_addition = 0; + } + + /* still enough space in current block? */ + if (block_left < estimated_size) + { + SVN_ERR(auto_pad_block(context, iterpool)); + block_left = get_block_left(context); + } + + /* select the change list in the source file, parse it and add it to + * the container */ + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, + iterpool)); + SVN_ERR(svn_fs_x__read_changes(&changes, temp_stream, scratch_pool, + iterpool)); + SVN_ERR(svn_fs_x__changes_append_list(&list_index, container, changes)); + SVN_ERR_ASSERT(list_index == sub_items->nelts); + block_left -= estimated_size; + estimated_addition += estimated_size; + + APR_ARRAY_PUSH(sub_items, svn_fs_x__id_t) = entry->items[0]; + + svn_pool_clear(iterpool); + } + + if (sub_items->nelts) + SVN_ERR(write_changes_container(context, container, sub_items, + new_entries, iterpool)); + + *entries = *new_entries; + svn_pool_destroy(iterpool); + svn_pool_destroy(container_pool); + + return SVN_NO_ERROR; +} + +/* Read the (property) representations identified by svn_fs_x__p2l_entry_t + * elements in ENTRIES from TEMP_FILE, aggregate them and write them into + * CONTEXT->PACK_FILE. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +write_property_containers(pack_context_t *context, + apr_array_header_t *entries, + apr_file_t *temp_file, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *new_entries + = apr_array_make(context->info_pool, 16, entries->elt_size); + + SVN_ERR(write_reps_containers(context, entries, temp_file, new_entries, + scratch_pool)); + + *entries = *new_entries; + + return SVN_NO_ERROR; +} + +/* Append all entries of svn_fs_x__p2l_entry_t * array TO_APPEND to + * svn_fs_x__p2l_entry_t * array DEST. + */ +static void +append_entries(apr_array_header_t *dest, + apr_array_header_t *to_append) +{ + int i; + for (i = 0; i < to_append->nelts; ++i) + APR_ARRAY_PUSH(dest, svn_fs_x__p2l_entry_t *) + = APR_ARRAY_IDX(to_append, i, svn_fs_x__p2l_entry_t *); +} + +/* Write the log-to-phys proto index file for CONTEXT and use POOL for + * temporary allocations. All items in all buckets must have been placed + * by now. + */ +static svn_error_t * +write_l2p_index(pack_context_t *context, + apr_pool_t *pool) +{ + apr_pool_t *scratch_pool = svn_pool_create(pool); + const char *temp_name; + const char *proto_index; + apr_off_t offset = 0; + + /* lump all items into one bucket. As target, use the bucket that + * probably has the most entries already. */ + append_entries(context->reps, context->changes); + append_entries(context->reps, context->file_props); + append_entries(context->reps, context->dir_props); + + /* Let the index code do the expensive L2P -> P2L transformation. */ + SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&temp_name, + context->fs, + context->reps, + pool, scratch_pool)); + + /* Append newly written segment to exisiting proto index file. */ + SVN_ERR(svn_io_file_name_get(&proto_index, context->proto_l2p_index, + scratch_pool)); + + SVN_ERR(svn_io_file_flush(context->proto_l2p_index, scratch_pool)); + SVN_ERR(svn_io_append_file(temp_name, proto_index, scratch_pool)); + SVN_ERR(svn_io_remove_file2(temp_name, FALSE, scratch_pool)); + SVN_ERR(svn_io_file_seek(context->proto_l2p_index, APR_END, &offset, + scratch_pool)); + + /* Done. */ + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +/* Pack the current revision range of CONTEXT, i.e. this covers phases 2 + * to 4. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +pack_range(pack_context_t *context, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + apr_pool_t *revpool = svn_pool_create(scratch_pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Phase 2: Copy items into various buckets and build tracking info */ + svn_revnum_t revision; + for (revision = context->start_rev; revision < context->end_rev; ++revision) + { + apr_off_t offset = 0; + svn_fs_x__revision_file_t *rev_file; + + /* Get the rev file dimensions (mainly index locations). */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, context->fs, + revision, revpool, iterpool)); + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); + + /* store the indirect array index */ + APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts; + + /* read the phys-to-log index file until we covered the whole rev file. + * That index contains enough info to build both target indexes from it. */ + while (offset < rev_file->l2p_offset) + { + /* read one cluster */ + int i; + apr_array_header_t *entries; + svn_pool_clear(iterpool); + + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, context->fs, + rev_file, revision, offset, + ffd->p2l_page_size, iterpool, + iterpool)); + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t); + + /* skip first entry if that was duplicated due crossing a + cluster boundary */ + if (offset > entry->offset) + continue; + + /* process entry while inside the rev file */ + offset = entry->offset; + if (offset < rev_file->l2p_offset) + { + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, + iterpool)); + + if (entry->type == SVN_FS_X__ITEM_TYPE_CHANGES) + SVN_ERR(copy_item_to_temp(context, + context->changes, + context->changes_file, + rev_file, entry, iterpool)); + else if (entry->type == SVN_FS_X__ITEM_TYPE_FILE_PROPS) + SVN_ERR(copy_item_to_temp(context, + context->file_props, + context->file_props_file, + rev_file, entry, iterpool)); + else if (entry->type == SVN_FS_X__ITEM_TYPE_DIR_PROPS) + SVN_ERR(copy_item_to_temp(context, + context->dir_props, + context->dir_props_file, + rev_file, entry, iterpool)); + else if ( entry->type == SVN_FS_X__ITEM_TYPE_FILE_REP + || entry->type == SVN_FS_X__ITEM_TYPE_DIR_REP) + SVN_ERR(copy_rep_to_temp(context, rev_file, entry, + iterpool)); + else if (entry->type == SVN_FS_X__ITEM_TYPE_NODEREV) + SVN_ERR(copy_node_to_temp(context, rev_file, entry, + iterpool)); + else + SVN_ERR_ASSERT(entry->type == SVN_FS_X__ITEM_TYPE_UNUSED); + + offset += entry->size; + } + } + + if (context->cancel_func) + SVN_ERR(context->cancel_func(context->cancel_baton)); + } + + svn_pool_clear(revpool); + } + + svn_pool_destroy(iterpool); + + /* phase 3: placement. + * Use "newest first" placement for simple items. */ + sort_items(context->changes); + sort_items(context->file_props); + sort_items(context->dir_props); + + /* follow dependencies recursively for noderevs and data representations */ + sort_reps(context); + + /* phase 4: copy bucket data to pack file. Write P2L index. */ + SVN_ERR(write_changes_containers(context, context->changes, + context->changes_file, revpool)); + svn_pool_clear(revpool); + SVN_ERR(write_property_containers(context, context->file_props, + context->file_props_file, revpool)); + svn_pool_clear(revpool); + SVN_ERR(write_property_containers(context, context->dir_props, + context->dir_props_file, revpool)); + svn_pool_clear(revpool); + SVN_ERR(copy_reps_from_temp(context, context->reps_file, revpool)); + svn_pool_clear(revpool); + + /* write L2P index as well (now that we know all target offsets) */ + SVN_ERR(write_l2p_index(context, revpool)); + + svn_pool_destroy(revpool); + + return SVN_NO_ERROR; +} + +/* Append CONTEXT->START_REV to the context's pack file with no re-ordering. + * This function will only be used for very large revisions (>>100k changes). + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +append_revision(pack_context_t *context, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = context->fs->fsap_data; + apr_off_t offset = 0; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_fs_x__revision_file_t *rev_file; + apr_finfo_t finfo; + + /* Get the size of the file. */ + const char *path = svn_dirent_join(context->shard_dir, + apr_psprintf(iterpool, "%ld", + context->start_rev), + scratch_pool); + SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, scratch_pool)); + + /* Copy all the bits from the rev file to the end of the pack file. */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, context->fs, + context->start_rev, scratch_pool, + iterpool)); + SVN_ERR(copy_file_data(context, context->pack_file, rev_file->file, + finfo.size, iterpool)); + + /* mark the start of a new revision */ + SVN_ERR(svn_fs_x__l2p_proto_index_add_revision(context->proto_l2p_index, + scratch_pool)); + + /* read the phys-to-log index file until we covered the whole rev file. + * That index contains enough info to build both target indexes from it. */ + while (offset < finfo.size) + { + /* read one cluster */ + int i; + apr_array_header_t *entries; + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, context->fs, rev_file, + context->start_rev, offset, + ffd->p2l_page_size, iterpool, + iterpool)); + + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t); + + /* skip first entry if that was duplicated due crossing a + cluster boundary */ + if (offset > entry->offset) + continue; + + /* process entry while inside the rev file */ + offset = entry->offset; + if (offset < finfo.size) + { + /* there should be true containers */ + SVN_ERR_ASSERT(entry->item_count == 1); + + entry->offset += context->pack_offset; + offset += entry->size; + SVN_ERR(svn_fs_x__l2p_proto_index_add_entry + (context->proto_l2p_index, entry->offset, 0, + entry->items[0].number, iterpool)); + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry + (context->proto_p2l_index, entry, iterpool)); + } + } + + svn_pool_clear(iterpool); + } + + svn_pool_destroy(iterpool); + context->pack_offset += finfo.size; + + return SVN_NO_ERROR; +} + +/* Format 7 packing logic. + * + * Pack the revision shard starting at SHARD_REV in filesystem FS from + * SHARD_DIR into the PACK_FILE_DIR, using SCRATCH_POOL for temporary + * allocations. Limit the extra memory consumption to MAX_MEM bytes. + * CANCEL_FUNC and CANCEL_BATON are what you think they are. + */ +static svn_error_t * +pack_log_addressed(svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_dir, + svn_revnum_t shard_rev, + apr_size_t max_mem, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + enum + { + /* estimated amount of memory used to represent one item in memory + * during rev file packing */ + PER_ITEM_MEM = APR_ALIGN_DEFAULT(sizeof(path_order_t)) + + APR_ALIGN_DEFAULT(2 *sizeof(void*)) + + APR_ALIGN_DEFAULT(sizeof(reference_t)) + + APR_ALIGN_DEFAULT(sizeof(svn_fs_x__p2l_entry_t)) + + 6 * sizeof(void*) + }; + + int max_items = max_mem / PER_ITEM_MEM > INT_MAX + ? INT_MAX + : (int)(max_mem / PER_ITEM_MEM); + apr_array_header_t *max_ids; + pack_context_t context = { 0 }; + int i; + apr_size_t item_count = 0; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* set up a pack context */ + SVN_ERR(initialize_pack_context(&context, fs, pack_file_dir, shard_dir, + shard_rev, max_items, cancel_func, + cancel_baton, scratch_pool)); + + /* phase 1: determine the size of the revisions to pack */ + SVN_ERR(svn_fs_x__l2p_get_max_ids(&max_ids, fs, shard_rev, + context.shard_end_rev - shard_rev, + scratch_pool, scratch_pool)); + + /* pack revisions in ranges that don't exceed MAX_MEM */ + for (i = 0; i < max_ids->nelts; ++i) + if (APR_ARRAY_IDX(max_ids, i, apr_uint64_t) + item_count <= max_items) + { + context.end_rev++; + } + else + { + /* some unpacked revisions before this one? */ + if (context.start_rev < context.end_rev) + { + /* pack them intelligently (might be just 1 rev but still ...) */ + SVN_ERR(pack_range(&context, iterpool)); + SVN_ERR(reset_pack_context(&context, iterpool)); + item_count = 0; + } + + /* next revision range is to start with the current revision */ + context.start_rev = i + context.shard_rev; + context.end_rev = context.start_rev + 1; + + /* if this is a very large revision, we must place it as is */ + if (APR_ARRAY_IDX(max_ids, i, apr_uint64_t) > max_items) + { + SVN_ERR(append_revision(&context, iterpool)); + context.start_rev++; + } + else + item_count += (apr_size_t)APR_ARRAY_IDX(max_ids, i, apr_uint64_t); + + svn_pool_clear(iterpool); + } + + /* non-empty revision range at the end? */ + if (context.start_rev < context.end_rev) + SVN_ERR(pack_range(&context, iterpool)); + + /* last phase: finalize indexes and clean up */ + SVN_ERR(reset_pack_context(&context, iterpool)); + SVN_ERR(close_pack_context(&context, iterpool)); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__get_packed_offset(apr_off_t *rev_offset, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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_x__get_sharded_offset, &shard_pos, + scratch_pool)); + + if (is_cached) + return SVN_NO_ERROR; + + /* Open the manifest file. */ + SVN_ERR(svn_stream_open_readonly(&manifest_stream, + svn_fs_x__path_rev_packed(fs, rev, PATH_MANIFEST, + scratch_pool), + scratch_pool, scratch_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(scratch_pool); + manifest = apr_array_make(scratch_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(svn_fs_x__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, + scratch_pool); +} + +/* In filesystem FS, pack the revision SHARD containing exactly + * MAX_FILES_PER_DIR revisions from SHARD_PATH into the PACK_FILE_DIR, + * using SCRATCH_POOL for temporary allocations. Try to limit the amount of + * temporary memory needed to MAX_MEM bytes. 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. + * + * The actual packing will be done in a format-specific sub-function. + */ +static svn_error_t * +pack_rev_shard(svn_fs_t *fs, + const char *pack_file_dir, + const char *shard_path, + apr_int64_t shard, + int max_files_per_dir, + apr_size_t max_mem, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + const char *pack_file_path; + svn_revnum_t shard_rev = (svn_revnum_t) (shard * max_files_per_dir); + + /* Some useful paths. */ + pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, 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 pack file. */ + SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, scratch_pool)); + + /* Index information files */ + SVN_ERR(pack_log_addressed(fs, pack_file_dir, shard_path, shard_rev, + max_mem, cancel_func, cancel_baton, + scratch_pool)); + + SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, scratch_pool)); + SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, 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 + * SCRATCH_POOL temporary 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, + svn_fs_t *fs, + 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 *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + 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, + scratch_pool)); + + /* Some useful paths. */ + rev_pack_file_dir = svn_dirent_join(revs_dir, + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD, + shard), + scratch_pool); + rev_shard_path = svn_dirent_join(revs_dir, + apr_psprintf(scratch_pool, "%" APR_INT64_T_FMT, shard), + scratch_pool); + + /* pack the revision content */ + SVN_ERR(pack_rev_shard(fs, rev_pack_file_dir, rev_shard_path, + shard, max_files_per_dir, DEFAULT_MAX_MEM, + cancel_func, cancel_baton, scratch_pool)); + + /* if enabled, pack the revprops in an equivalent way */ + if (revsprops_dir) + { + revprops_pack_file_dir = svn_dirent_join(revsprops_dir, + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD, + shard), + scratch_pool); + revprops_shard_path = svn_dirent_join(revsprops_dir, + apr_psprintf(scratch_pool, "%" APR_INT64_T_FMT, shard), + scratch_pool); + + SVN_ERR(svn_fs_x__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, + scratch_pool)); + } + + /* Update the min-unpacked-rev file to reflect our newly packed shard. */ + SVN_ERR(svn_fs_x__write_min_unpacked_rev(fs, + (svn_revnum_t)((shard + 1) * max_files_per_dir), + scratch_pool)); + ffd->min_unpacked_rev = (svn_revnum_t)((shard + 1) * max_files_per_dir); + + /* Finally, remove the existing shard directories. + * For revprops, clean up older obsolete shards as well as they might + * have been left over from an interrupted FS upgrade. */ + SVN_ERR(svn_io_remove_dir2(rev_shard_path, TRUE, + cancel_func, cancel_baton, scratch_pool)); + if (revsprops_dir) + { + svn_node_kind_t kind = svn_node_dir; + apr_int64_t to_cleanup = shard; + do + { + SVN_ERR(svn_fs_x__delete_revprops_shard(revprops_shard_path, + to_cleanup, + max_files_per_dir, + cancel_func, cancel_baton, + scratch_pool)); + + /* If the previous shard exists, clean it up as well. + Don't try to clean up shard 0 as it we can't tell quickly + whether it actually needs cleaning up. */ + revprops_shard_path = svn_dirent_join(revsprops_dir, + apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + --to_cleanup), + scratch_pool); + SVN_ERR(svn_io_check_path(revprops_shard_path, &kind, scratch_pool)); + } + while (kind == svn_node_dir && to_cleanup > 0); + } + + /* Notify caller we're starting to pack this shard. */ + if (notify_func) + SVN_ERR(notify_func(notify_baton, shard, svn_fs_pack_notify_end, + scratch_pool)); + + return SVN_NO_ERROR; +} + +typedef struct pack_baton_t +{ + svn_fs_t *fs; + svn_fs_pack_notify_t notify_func; + void *notify_baton; + svn_cancel_func_t cancel_func; + void *cancel_baton; +} pack_baton_t; + + +/* The work-horse for svn_fs_x__pack, called with the FS write lock. + This implements the svn_fs_x__with_write_lock() 'body' callback + type. BATON is a 'pack_baton_t *'. + + 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_x__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 *scratch_pool) +{ + pack_baton_t *pb = baton; + svn_fs_x__data_t *ffd = pb->fs->fsap_data; + 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; + + /* If we aren't using sharding, we can't do any packing, so quit. */ + SVN_ERR(svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, pb->fs, + scratch_pool)); + + SVN_ERR(svn_fs_x__youngest_rev(&youngest, pb->fs, scratch_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, scratch_pool); + revprops_data_path = svn_dirent_join(pb->fs->path, PATH_REVPROPS_DIR, + scratch_pool); + + iterpool = svn_pool_create(scratch_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, i, ffd->max_files_per_dir, + ffd->revprop_pack_size, + ffd->compress_packed_revprops + ? SVN__COMPRESSION_ZLIB_DEFAULT + : SVN__COMPRESSION_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_x__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 *scratch_pool) +{ + pack_baton_t 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_x__with_pack_lock(fs, pack_body, &pb, scratch_pool); +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/pack.h b/contrib/subversion/subversion/libsvn_fs_x/pack.h new file mode 100644 index 000000000..55416190f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/pack.h @@ -0,0 +1,65 @@ +/* pack.h : interface FSX pack 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__PACK_H +#define SVN_LIBSVN_FS__PACK_H + +#include "fs.h" + +/* Possibly pack the repository at PATH. This just take full shards, and + combines all the revision files into a single one, with a manifest header. + Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. + Use SCRATCH_POOL for temporary allocations. + + Existing filesystem references need not change. */ +svn_error_t * +svn_fs_x__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 *scratch_pool); + +/** + * For the packed revision REV in FS, determine the offset within the + * revision pack file and return it in REV_OFFSET. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__get_packed_offset(apr_off_t *rev_offset, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *scratch_pool); + +/* Return the svn_dir_entry_t* objects of DIRECTORY in an APR array + * allocated in RESULT_POOL with entries added in storage (on-disk) order. + * FS' format will be used to pick the optimal ordering strategy. Use + * SCRATCH_POOL for temporary allocations. + */ +apr_array_header_t * +svn_fs_x__order_dir_entries(svn_fs_t *fs, + apr_hash_t *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/recovery.c b/contrib/subversion/subversion/libsvn_fs_x/recovery.c new file mode 100644 index 000000000..984b74023 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/recovery.c @@ -0,0 +1,263 @@ +/* recovery.c --- FSX recovery 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. + * ==================================================================== + */ + +#include "recovery.h" + +#include "svn_hash.h" +#include "svn_pools.h" +#include "private/svn_string_private.h" + +#include "low_level.h" +#include "rep-cache.h" +#include "revprops.h" +#include "transaction.h" +#include "util.h" +#include "cached_data.h" +#include "index.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* Part of the recovery procedure. Return the largest revision *REV in + filesystem FS. Use SCRATCH_POOL for temporary allocation. */ +static svn_error_t * +recover_get_largest_revision(svn_fs_t *fs, + svn_revnum_t *rev, + apr_pool_t *scratch_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(scratch_pool); + /* Keep doubling right, until we find a revision that doesn't exist. */ + while (1) + { + svn_error_t *err; + svn_fs_x__revision_file_t *file; + svn_pool_clear(iterpool); + + err = svn_fs_x__open_pack_or_rev_file(&file, fs, right, iterpool, + 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; + svn_fs_x__revision_file_t *file; + svn_pool_clear(iterpool); + + err = svn_fs_x__open_pack_or_rev_file(&file, fs, probe, iterpool, + 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; +} + +/* Baton used for recover_body below. */ +typedef struct recover_baton_t { + svn_fs_t *fs; + svn_cancel_func_t cancel_func; + void *cancel_baton; +} recover_baton_t; + +/* The work-horse for svn_fs_x__recover, called with the FS + write lock. This implements the svn_fs_x__with_write_lock() + 'body' callback type. BATON is a 'recover_baton_t *'. */ +static svn_error_t * +recover_body(void *baton, + apr_pool_t *scratch_pool) +{ + recover_baton_t *b = baton; + svn_fs_t *fs = b->fs; + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_revnum_t max_rev; + svn_revnum_t youngest_rev; + svn_boolean_t revprop_missing = TRUE; + svn_boolean_t revprop_accessible = FALSE; + + /* Lose potentially corrupted data in temp files */ + SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, scratch_pool)); + + /* The admin may have created a plain copy of this repo before attempting + to recover it (hotcopy may or may not work with corrupted repos). + Bump the instance ID. */ + SVN_ERR(svn_fs_x__set_uuid(fs, fs->uuid, NULL, scratch_pool)); + + /* We need to know the largest revision in the filesystem. */ + SVN_ERR(recover_get_largest_revision(fs, &max_rev, scratch_pool)); + + /* Get the expected youngest revision */ + SVN_ERR(svn_fs_x__youngest_rev(&youngest_rev, fs, scratch_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 FSX 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); + + /* Before setting current, verify that there is a revprops file + for the youngest revision. (Issue #2992) */ + if (svn_fs_x__is_packed_revprop(fs, max_rev)) + { + revprop_accessible + = svn_fs_x__packed_revprop_available(&revprop_missing, fs, max_rev, + scratch_pool); + } + else + { + svn_node_kind_t youngest_revprops_kind; + SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, max_rev, + scratch_pool), + &youngest_revprops_kind, scratch_pool)); + + if (youngest_revprops_kind == svn_node_file) + { + revprop_missing = FALSE; + revprop_accessible = TRUE; + } + else if (youngest_revprops_kind != svn_node_none) + { + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Revision %ld has a non-file where its " + "revprops file should be"), + max_rev); + } + } + + if (!revprop_accessible) + { + if (revprop_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); + } + } + + /* 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_x__exists_rep_cache(&rep_cache_exists, fs, + scratch_pool)); + if (rep_cache_exists) + SVN_ERR(svn_fs_x__del_rep_reference(fs, max_rev, scratch_pool)); + } + + /* Now store the discovered youngest revision, and the next IDs if + relevant, in a new 'current' file. */ + return svn_fs_x__write_current(fs, max_rev, scratch_pool); +} + +/* This implements the fs_library_vtable_t.recover() API. */ +svn_error_t * +svn_fs_x__recover(svn_fs_t *fs, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + recover_baton_t b; + + /* We have no way to take out an exclusive lock in FSX, 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_x__with_all_locks(fs, recover_body, &b, scratch_pool); +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/recovery.h b/contrib/subversion/subversion/libsvn_fs_x/recovery.h new file mode 100644 index 000000000..4fe0a072a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/recovery.h @@ -0,0 +1,37 @@ +/* recovery.h : interface to the FSX recovery 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. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_FS__RECOVERY_H +#define SVN_LIBSVN_FS__RECOVERY_H + +#include "fs.h" + +/* Recover the fsx associated with filesystem FS. + Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__recover(svn_fs_t *fs, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.h b/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.h new file mode 100644 index 000000000..21d56f141 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.h @@ -0,0 +1,92 @@ +/* This file is automatically generated from rep-cache-db.sql and .dist_sandbox/subversion-1.9.4/subversion/libsvn_fs_x/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 \ + "PRAGMA PAGE_SIZE = 4096; " \ + "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 STMT_UNLOCK_REP 7 +#define STMT_7_INFO {"STMT_UNLOCK_REP", NULL} +#define STMT_7 \ + "ROLLBACK TRANSACTION; " \ + "" + +#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, \ + STMT_7, \ + 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, \ + STMT_7_INFO, \ + {NULL, NULL} \ + } diff --git a/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.sql b/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.sql new file mode 100644 index 000000000..7ad402a5a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rep-cache-db.sql @@ -0,0 +1,70 @@ +/* rep-cache-db.sql -- schema for use in rep-caching + * 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. + * ==================================================================== + */ + +-- STMT_CREATE_SCHEMA +PRAGMA PAGE_SIZE = 4096; + +/* A table mapping representation hashes to locations in a rev file. */ +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; + + +-- STMT_GET_REP +SELECT revision, offset, size, expanded_size +FROM rep_cache +WHERE hash = ?1 + +-- STMT_SET_REP +INSERT OR FAIL INTO rep_cache (hash, revision, offset, size, expanded_size) +VALUES (?1, ?2, ?3, ?4, ?5) + +-- STMT_GET_REPS_FOR_RANGE +SELECT hash, revision, offset, size, expanded_size +FROM rep_cache +WHERE revision >= ?1 AND revision <= ?2 + +-- STMT_GET_MAX_REV +SELECT MAX(revision) +FROM rep_cache + +-- STMT_DEL_REPS_YOUNGER_THAN_REV +DELETE FROM rep_cache +WHERE revision > ?1 + +/* An INSERT takes an SQLite reserved lock that prevents other writes + but doesn't block reads. The incomplete transaction means that no + permanent change is made to the database and the transaction is + removed when the database is closed. */ +-- STMT_LOCK_REP +BEGIN TRANSACTION; +INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0) + +-- STMT_UNLOCK_REP +ROLLBACK TRANSACTION; diff --git a/contrib/subversion/subversion/libsvn_fs_x/rep-cache.c b/contrib/subversion/subversion/libsvn_fs_x/rep-cache.c new file mode 100644 index 000000000..85e62a46f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rep-cache.c @@ -0,0 +1,416 @@ +/* rep-sharing.c --- the rep-sharing cache for fsx + * + * ==================================================================== + * 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_pools.h" + +#include "svn_private_config.h" + +#include "fs_x.h" +#include "fs.h" +#include "rep-cache.h" +#include "util.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_path.h" + +#include "private/svn_sqlite.h" + +#include "rep-cache-db.h" + +/* A few magic values */ +#define REP_CACHE_SCHEMA_FORMAT 1 + +REP_CACHE_DB_SQL_DECLARE_STATEMENTS(statements); + + + +/** Helper functions. **/ +static APR_INLINE const char * +path_rep_cache_db(const char *fs_path, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs_path, REP_CACHE_DB_NAME, result_pool); +} + + +/** Library-private API's. **/ + +/* Body of svn_fs_x__open_rep_cache(). + Implements svn_atomic__init_once().init_func. + */ +static svn_error_t * +open_rep_cache(void *baton, + apr_pool_t *scratch_pool) +{ + svn_fs_t *fs = baton; + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_sqlite__db_t *sdb; + const char *db_path; + int version; + + /* Open (or create) the sqlite database. It will be automatically + closed when fs->pool is destroyed. */ + db_path = path_rep_cache_db(fs->path, scratch_pool); +#ifndef WIN32 + { + /* We want to extend the permissions that apply to the repository + as a whole when creating a new rep cache and not simply default + to umask. */ + svn_boolean_t exists; + + SVN_ERR(svn_fs_x__exists_rep_cache(&exists, fs, scratch_pool)); + if (!exists) + { + const char *current = svn_fs_x__path_current(fs, scratch_pool); + svn_error_t *err = svn_io_file_create_empty(db_path, scratch_pool); + + if (err && !APR_STATUS_IS_EEXIST(err->apr_err)) + /* A real error. */ + return svn_error_trace(err); + else if (err) + /* Some other thread/process created the file. */ + svn_error_clear(err); + else + /* We created the file. */ + SVN_ERR(svn_io_copy_perms(current, db_path, scratch_pool)); + } + } +#endif + SVN_ERR(svn_sqlite__open(&sdb, db_path, + svn_sqlite__mode_rwcreate, statements, + 0, NULL, 0, + fs->pool, scratch_pool)); + + SVN_ERR(svn_sqlite__read_schema_version(&version, sdb, scratch_pool)); + if (version < REP_CACHE_SCHEMA_FORMAT) + { + /* Must be 0 -- an uninitialized (no schema) database. Create + the schema. Results in schema version of 1. */ + SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_SCHEMA)); + } + + /* This is used as a flag that the database is available so don't + set it earlier. */ + ffd->rep_cache_db = sdb; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__open_rep_cache(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_error_t *err = svn_atomic__init_once(&ffd->rep_cache_db_opened, + open_rep_cache, fs, scratch_pool); + return svn_error_quick_wrap(err, _("Couldn't open rep-cache database")); +} + +svn_error_t * +svn_fs_x__exists_rep_cache(svn_boolean_t *exists, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + + SVN_ERR(svn_io_check_path(path_rep_cache_db(fs->path, scratch_pool), + &kind, scratch_pool)); + + *exists = (kind != svn_node_none); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__walk_rep_reference(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_error_t *(*walker)(svn_fs_x__representation_t *, + void *, + svn_fs_t *, + apr_pool_t *), + void *walker_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + int iterations = 0; + + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + if (! ffd->rep_cache_db) + SVN_ERR(svn_fs_x__open_rep_cache(fs, scratch_pool)); + + /* Check global invariants. */ + if (start == 0) + { + svn_revnum_t max; + + SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, + STMT_GET_MAX_REV)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + max = svn_sqlite__column_revnum(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + if (SVN_IS_VALID_REVNUM(max)) /* The rep-cache could be empty. */ + SVN_ERR(svn_fs_x__ensure_revision_exists(max, fs, iterpool)); + } + + SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, + STMT_GET_REPS_FOR_RANGE)); + SVN_ERR(svn_sqlite__bindf(stmt, "rr", + start, end)); + + /* Walk the cache entries. */ + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + svn_fs_x__representation_t *rep; + const char *sha1_digest; + svn_error_t *err; + svn_checksum_t *checksum; + + /* Clear ITERPOOL occasionally. */ + if (iterations++ % 16 == 0) + svn_pool_clear(iterpool); + + /* Check for cancellation. */ + if (cancel_func) + { + err = cancel_func(cancel_baton); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + + /* Construct a svn_fs_x__representation_t. */ + rep = apr_pcalloc(iterpool, sizeof(*rep)); + sha1_digest = svn_sqlite__column_text(stmt, 0, iterpool); + err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1, + sha1_digest, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + rep->has_sha1 = TRUE; + memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); + rep->id.change_set = svn_sqlite__column_revnum(stmt, 1); + rep->id.number = svn_sqlite__column_int64(stmt, 2); + rep->size = svn_sqlite__column_int64(stmt, 3); + rep->expanded_size = svn_sqlite__column_int64(stmt, 4); + + /* Walk. */ + err = walker(rep, walker_baton, fs, 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_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + + +/* This function's caller ignores most errors it returns. + If you extend this function, check the callsite to see if you have + to make it not-ignore additional error codes. */ +svn_error_t * +svn_fs_x__get_rep_reference(svn_fs_x__representation_t **rep, + svn_fs_t *fs, + svn_checksum_t *checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + SVN_ERR_ASSERT(ffd->rep_sharing_allowed); + if (! ffd->rep_cache_db) + SVN_ERR(svn_fs_x__open_rep_cache(fs, scratch_pool)); + + /* We only allow SHA1 checksums in this table. */ + if (checksum->kind != svn_checksum_sha1) + return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, + _("Only SHA1 checksums can be used as keys in the " + "rep_cache table.\n")); + + SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_GET_REP)); + SVN_ERR(svn_sqlite__bindf(stmt, "s", + svn_checksum_to_cstring(checksum, scratch_pool))); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + { + *rep = apr_pcalloc(result_pool, sizeof(**rep)); + memcpy((*rep)->sha1_digest, checksum->digest, + sizeof((*rep)->sha1_digest)); + (*rep)->has_sha1 = TRUE; + (*rep)->id.change_set = svn_sqlite__column_revnum(stmt, 0); + (*rep)->id.number = svn_sqlite__column_int64(stmt, 1); + (*rep)->size = svn_sqlite__column_int64(stmt, 2); + (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3); + } + else + *rep = NULL; + + SVN_ERR(svn_sqlite__reset(stmt)); + + if (*rep) + { + /* Check that REP refers to a revision that exists in FS. */ + svn_revnum_t revision = svn_fs_x__get_revnum((*rep)->id.change_set); + svn_error_t *err = svn_fs_x__ensure_revision_exists(revision, fs, + scratch_pool); + if (err) + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + "Checksum '%s' in rep-cache is beyond HEAD", + svn_checksum_to_cstring_display(checksum, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__set_rep_reference(svn_fs_t *fs, + svn_fs_x__representation_t *rep, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_sqlite__stmt_t *stmt; + svn_error_t *err; + svn_checksum_t checksum; + checksum.kind = svn_checksum_sha1; + checksum.digest = rep->sha1_digest; + + SVN_ERR_ASSERT(ffd->rep_sharing_allowed); + if (! ffd->rep_cache_db) + SVN_ERR(svn_fs_x__open_rep_cache(fs, scratch_pool)); + + /* We only allow SHA1 checksums in this table. */ + if (! rep->has_sha1) + return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, + _("Only SHA1 checksums can be used as keys in the " + "rep_cache table.\n")); + + SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP)); + SVN_ERR(svn_sqlite__bindf(stmt, "siiii", + svn_checksum_to_cstring(&checksum, scratch_pool), + (apr_int64_t) rep->id.change_set, + (apr_int64_t) rep->id.number, + (apr_int64_t) rep->size, + (apr_int64_t) rep->expanded_size)); + + err = svn_sqlite__insert(NULL, stmt); + if (err) + { + svn_fs_x__representation_t *old_rep; + + if (err->apr_err != SVN_ERR_SQLITE_CONSTRAINT) + return svn_error_trace(err); + + svn_error_clear(err); + + /* Constraint failed so the mapping for SHA1_CHECKSUM->REP + should exist. If so that's cool -- just do nothing. If not, + that's a red flag! */ + SVN_ERR(svn_fs_x__get_rep_reference(&old_rep, fs, &checksum, + scratch_pool, scratch_pool)); + + if (!old_rep) + { + /* Something really odd at this point, we failed to insert the + checksum AND failed to read an existing checksum. Do we need + to flag this? */ + } + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__del_rep_reference(svn_fs_t *fs, + svn_revnum_t youngest, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_sqlite__stmt_t *stmt; + + if (! ffd->rep_cache_db) + SVN_ERR(svn_fs_x__open_rep_cache(fs, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, + STMT_DEL_REPS_YOUNGER_THAN_REV)); + SVN_ERR(svn_sqlite__bindf(stmt, "r", youngest)); + SVN_ERR(svn_sqlite__step_done(stmt)); + + return SVN_NO_ERROR; +} + +/* Start a transaction to take an SQLite reserved lock that prevents + other writes. + + See unlock_rep_cache(). */ +static svn_error_t * +lock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + if (! ffd->rep_cache_db) + SVN_ERR(svn_fs_x__open_rep_cache(fs, pool)); + + SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_LOCK_REP)); + + return SVN_NO_ERROR; +} + +/* End the transaction started by lock_rep_cache(). */ +static svn_error_t * +unlock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + SVN_ERR_ASSERT(ffd->rep_cache_db); /* was opened by lock_rep_cache() */ + + SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_UNLOCK_REP)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *, + apr_pool_t *), + void *baton, + apr_pool_t *pool) +{ + svn_error_t *err; + + SVN_ERR(lock_rep_cache(fs, pool)); + err = body(baton, pool); + return svn_error_compose_create(err, unlock_rep_cache(fs, pool)); +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/rep-cache.h b/contrib/subversion/subversion/libsvn_fs_x/rep-cache.h new file mode 100644 index 000000000..1fe26da9b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rep-cache.h @@ -0,0 +1,105 @@ +/* rep-cache.h : interface to rep cache db functions + * + * ==================================================================== + * 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_FS_X_REP_CACHE_H +#define SVN_LIBSVN_FS_X_REP_CACHE_H + +#include "svn_error.h" + +#include "fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define REP_CACHE_DB_NAME "rep-cache.db" + +/* Open and create, if needed, the rep cache database associated with FS. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__open_rep_cache(svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Set *EXISTS to TRUE iff the rep-cache DB file exists. */ +svn_error_t * +svn_fs_x__exists_rep_cache(svn_boolean_t *exists, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Iterate all representations currently in FS's cache. */ +svn_error_t * +svn_fs_x__walk_rep_reference(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_error_t *(*walker)(svn_fs_x__representation_t *rep, + void *walker_baton, + svn_fs_t *fs, + apr_pool_t *scratch_pool), + void *walker_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* Return the representation REP in FS which has fulltext CHECKSUM. + REP is allocated in RESULT_POOL. If the rep cache database has not been + opened, just set *REP to NULL. Returns SVN_ERR_FS_CORRUPT if a reference + beyond HEAD is detected. Uses SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__get_rep_reference(svn_fs_x__representation_t **rep, + svn_fs_t *fs, + svn_checksum_t *checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set the representation REP in FS, using REP->CHECKSUM. + Use SCRATCH_POOL for temporary allocations. Returns SVN_ERR_FS_CORRUPT + if an existing reference beyond HEAD is detected. + + If the rep cache database has not been opened, this may be a no op. */ +svn_error_t * +svn_fs_x__set_rep_reference(svn_fs_t *fs, + svn_fs_x__representation_t *rep, + apr_pool_t *scratch_pool); + +/* Delete from the cache all reps corresponding to revisions younger + than YOUNGEST. */ +svn_error_t * +svn_fs_x__del_rep_reference(svn_fs_t *fs, + svn_revnum_t youngest, + apr_pool_t *scratch_pool); + + +/* Start a transaction to take an SQLite reserved lock that prevents + other writes, call BODY, end the transaction, and return what BODY returned. + */ +svn_error_t * +svn_fs_x__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_X_REP_CACHE_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/reps.c b/contrib/subversion/subversion/libsvn_fs_x/reps.c new file mode 100644 index 000000000..85a5269a7 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/reps.c @@ -0,0 +1,948 @@ +/* reps.c --- FSX representation container + * + * ==================================================================== + * 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 "reps.h" + +#include "svn_sorts.h" +#include "private/svn_string_private.h" +#include "private/svn_packed_data.h" +#include "private/svn_temp_serializer.h" + +#include "svn_private_config.h" + +#include "cached_data.h" + +/* Length of the text chunks we hash and match. The algorithm will find + * most matches with a length of 2 * MATCH_BLOCKSIZE and only specific + * ones that are shorter than MATCH_BLOCKSIZE. + * + * This should be a power of two and must be a multiple of 8. + * Good choices are 32, 64 and 128. + */ +#define MATCH_BLOCKSIZE 64 + +/* Limit the total text body within a container to 16MB. Larger values + * of up to 2GB are possible but become increasingly impractical as the + * container has to be loaded in its entirety before any of it can be read. + */ +#define MAX_TEXT_BODY 0x1000000 + +/* Limit the size of the instructions stream. This should not exceed the + * text body size limit. */ +#define MAX_INSTRUCTIONS (MAX_TEXT_BODY / 8) + +/* value of unused hash buckets */ +#define NO_OFFSET ((apr_uint32_t)(-1)) + +/* Byte strings are described by a series of copy instructions that each + * do one of the following + * + * - copy a given number of bytes from the text corpus starting at a + * given offset + * - reference other instruction and specify how many of instructions of + * that sequence shall be executed (i.e. a sub-sequence) + * - copy a number of bytes from the base representation buffer starting + * at a given offset + */ + +/* The contents of a fulltext / representation is defined by its first + * instruction and the number of instructions to execute. + */ +typedef struct rep_t +{ + apr_uint32_t first_instruction; + apr_uint32_t instruction_count; +} rep_t; + +/* A single instruction. The instruction type is being encoded in OFFSET. + */ +typedef struct instruction_t +{ + /* Instruction type and offset. + * - offset < 0 + * reference to instruction sub-sequence starting with + * container->instructions[-offset]. + * - 0 <= offset < container->base_text_len + * reference to the base text corpus; + * start copy at offset + * - offset >= container->base_text_len + * reference to the text corpus; + * start copy at offset-container->base_text_len + */ + apr_int32_t offset; + + /* Number of bytes to copy / instructions to execute + */ + apr_uint32_t count; +} instruction_t; + +/* Describe a base fulltext. + */ +typedef struct base_t +{ + /* Revision */ + svn_revnum_t revision; + + /* Item within that revision */ + apr_uint64_t item_index; + + /* Priority with which to use this base over others */ + int priority; + + /* Index into builder->representations that identifies the copy + * instructions for this base. */ + apr_uint32_t rep; +} base_t; + +/* Yet another hash data structure. This one tries to be more cache + * friendly by putting the first byte of each hashed sequence in a + * common array. This array will often fit into L1 or L2 at least and + * give a 99% accurate test for a match without giving false negatives. + */ +typedef struct hash_t +{ + /* for used entries i, prefixes[i] == text[offsets[i]]; 0 otherwise. + * This allows for a quick check without resolving the double + * indirection. */ + char *prefixes; + + /* for used entries i, offsets[i] is start offset in the text corpus; + * NO_OFFSET otherwise. + */ + apr_uint32_t *offsets; + + /* to be used later for optimizations. */ + apr_uint32_t *last_matches; + + /* number of buckets in this hash, i.e. elements in each array above. + * Must be 1 << (8 * sizeof(hash_key_t) - shift) */ + apr_size_t size; + + /* number of buckets actually in use. Must be <= size. */ + apr_size_t used; + + /* number of bits to shift right to map a hash_key_t to a bucket index */ + apr_size_t shift; + + /* pool to use when growing the hash */ + apr_pool_t *pool; +} hash_t; + +/* Hash key type. 32 bits for pseudo-Adler32 hash sums. + */ +typedef apr_uint32_t hash_key_t; + +/* Constructor data structure. + */ +struct svn_fs_x__reps_builder_t +{ + /* file system to read base representations from */ + svn_fs_t *fs; + + /* text corpus */ + svn_stringbuf_t *text; + + /* text block hash */ + hash_t hash; + + /* array of base_t objects describing all bases defined so far */ + apr_array_header_t *bases; + + /* array of rep_t objects describing all fulltexts (including bases) + * added so far */ + apr_array_header_t *reps; + + /* array of instruction_t objects describing all instructions */ + apr_array_header_t *instructions; + + /* number of bytes in the text corpus that belongs to bases */ + apr_size_t base_text_len; +}; + +/* R/o container. + */ +struct svn_fs_x__reps_t +{ + /* text corpus */ + const char *text; + + /* length of the text corpus in bytes */ + apr_size_t text_len; + + /* bases used */ + const base_t *bases; + + /* number of bases used */ + apr_size_t base_count; + + /* fulltext i can be reconstructed by executing instructions + * first_instructions[i] .. first_instructions[i+1]-1 + * (this array has one extra element at the end) + */ + const apr_uint32_t *first_instructions; + + /* number of fulltexts (no bases) */ + apr_size_t rep_count; + + /* instructions */ + const instruction_t *instructions; + + /* total number of instructions */ + apr_size_t instruction_count; + + /* offsets > 0 but smaller that this are considered base references */ + apr_size_t base_text_len; +}; + +/* describe a section in the extractor's result string that is not filled + * yet (but already exists). + */ +typedef struct missing_t +{ + /* start offset within the result string */ + apr_uint32_t start; + + /* number of bytes to write */ + apr_uint32_t count; + + /* index into extractor->bases selecting the base representation to + * copy from */ + apr_uint32_t base; + + /* copy source offset within that base representation */ + apr_uint32_t offset; +} missing_t; + +/* Fulltext extractor data structure. + */ +struct svn_fs_x__rep_extractor_t +{ + /* filesystem to read the bases from */ + svn_fs_t *fs; + + /* fulltext being constructed */ + svn_stringbuf_t *result; + + /* bases (base_t) yet to process (not used ATM) */ + apr_array_header_t *bases; + + /* missing sections (missing_t) in result->data that need to be filled, + * yet */ + apr_array_header_t *missing; + + /* pool to use for allocating the above arrays */ + apr_pool_t *pool; +}; + +/* Given the ADLER32 checksum for a certain range of MATCH_BLOCKSIZE + * bytes, return the checksum for the range excluding the first byte + * C_OUT and appending C_IN. + */ +static hash_key_t +hash_key_replace(hash_key_t adler32, const char c_out, const char c_in) +{ + adler32 -= (MATCH_BLOCKSIZE * 0x10000u * ((unsigned char) c_out)); + + adler32 -= (unsigned char)c_out; + adler32 += (unsigned char)c_in; + + return adler32 + adler32 * 0x10000; +} + +/* Calculate an pseudo-adler32 checksum for MATCH_BLOCKSIZE bytes starting + at DATA. Return the checksum value. */ +static hash_key_t +hash_key(const char *data) +{ + const unsigned char *input = (const unsigned char *)data; + const unsigned char *last = input + MATCH_BLOCKSIZE; + + hash_key_t s1 = 0; + hash_key_t s2 = 0; + + for (; input < last; input += 8) + { + s1 += input[0]; s2 += s1; + s1 += input[1]; s2 += s1; + s1 += input[2]; s2 += s1; + s1 += input[3]; s2 += s1; + s1 += input[4]; s2 += s1; + s1 += input[5]; s2 += s1; + s1 += input[6]; s2 += s1; + s1 += input[7]; s2 += s1; + } + + return s2 * 0x10000 + s1; +} + +/* Map the ADLER32 key to a bucket index in HASH and return that index. + */ +static apr_size_t +hash_to_index(hash_t *hash, hash_key_t adler32) +{ + return (adler32 * 0xd1f3da69) >> hash->shift; +} + +/* Allocate and initialized SIZE buckets in RESULT_POOL. + * Assign them to HASH. + */ +static void +allocate_hash_members(hash_t *hash, + apr_size_t size, + apr_pool_t *result_pool) +{ + apr_size_t i; + + hash->pool = result_pool; + hash->size = size; + + hash->prefixes = apr_pcalloc(result_pool, size); + hash->last_matches = apr_pcalloc(result_pool, + sizeof(*hash->last_matches) * size); + hash->offsets = apr_palloc(result_pool, sizeof(*hash->offsets) * size); + + for (i = 0; i < size; ++i) + hash->offsets[i] = NO_OFFSET; +} + +/* Initialize the HASH data structure with 2**TWOPOWER buckets allocated + * in RESULT_POOL. + */ +static void +init_hash(hash_t *hash, + apr_size_t twoPower, + apr_pool_t *result_pool) +{ + hash->used = 0; + hash->shift = sizeof(hash_key_t) * 8 - twoPower; + + allocate_hash_members(hash, 1 << twoPower, result_pool); +} + +/* Make HASH have at least MIN_SIZE buckets but at least double the number + * of buckets in HASH by rehashing it based TEXT. + */ +static void +grow_hash(hash_t *hash, + svn_stringbuf_t *text, + apr_size_t min_size) +{ + hash_t copy; + apr_size_t i; + + /* determine the new hash size */ + apr_size_t new_size = hash->size * 2; + apr_size_t new_shift = hash->shift - 1; + while (new_size < min_size) + { + new_size *= 2; + --new_shift; + } + + /* allocate new hash */ + allocate_hash_members(©, new_size, hash->pool); + copy.used = 0; + copy.shift = new_shift; + + /* copy / translate data */ + for (i = 0; i < hash->size; ++i) + { + apr_uint32_t offset = hash->offsets[i]; + if (offset != NO_OFFSET) + { + hash_key_t key = hash_key(text->data + offset); + size_t idx = hash_to_index(©, key); + + if (copy.offsets[idx] == NO_OFFSET) + copy.used++; + + copy.prefixes[idx] = hash->prefixes[i]; + copy.offsets[idx] = offset; + copy.last_matches[idx] = hash->last_matches[i]; + } + } + + *hash = copy; +} + +svn_fs_x__reps_builder_t * +svn_fs_x__reps_builder_create(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + svn_fs_x__reps_builder_t *result = apr_pcalloc(result_pool, + sizeof(*result)); + + result->fs = fs; + result->text = svn_stringbuf_create_empty(result_pool); + init_hash(&result->hash, 4, result_pool); + + result->bases = apr_array_make(result_pool, 0, sizeof(base_t)); + result->reps = apr_array_make(result_pool, 0, sizeof(rep_t)); + result->instructions = apr_array_make(result_pool, 0, + sizeof(instruction_t)); + + return result; +} + +svn_error_t * +svn_fs_x__reps_add_base(svn_fs_x__reps_builder_t *builder, + svn_fs_x__representation_t *rep, + int priority, + apr_pool_t *scratch_pool) +{ + base_t base; + apr_size_t text_start_offset = builder->text->len; + + svn_stream_t *stream; + svn_string_t *contents; + apr_size_t idx; + SVN_ERR(svn_fs_x__get_contents(&stream, builder->fs, rep, FALSE, + scratch_pool)); + SVN_ERR(svn_string_from_stream(&contents, stream, scratch_pool, + scratch_pool)); + SVN_ERR(svn_fs_x__reps_add(&idx, builder, contents)); + + base.revision = svn_fs_x__get_revnum(rep->id.change_set); + base.item_index = rep->id.number; + base.priority = priority; + base.rep = (apr_uint32_t)idx; + + APR_ARRAY_PUSH(builder->bases, base_t) = base; + builder->base_text_len += builder->text->len - text_start_offset; + + return SVN_NO_ERROR; +} + +/* Add LEN bytes from DATA to BUILDER's text corpus. Also, add a copy + * operation for that text fragment. + */ +static void +add_new_text(svn_fs_x__reps_builder_t *builder, + const char *data, + apr_size_t len) +{ + instruction_t instruction; + apr_size_t offset; + apr_size_t buckets_required; + + if (len == 0) + return; + + /* new instruction */ + instruction.offset = (apr_int32_t)builder->text->len; + instruction.count = (apr_uint32_t)len; + APR_ARRAY_PUSH(builder->instructions, instruction_t) = instruction; + + /* add to text corpus */ + svn_stringbuf_appendbytes(builder->text, data, len); + + /* expand the hash upfront to minimize the chances of collisions */ + buckets_required = builder->hash.used + len / MATCH_BLOCKSIZE; + if (buckets_required * 3 >= builder->hash.size * 2) + grow_hash(&builder->hash, builder->text, 2 * buckets_required); + + /* add hash entries for the new sequence */ + for (offset = instruction.offset; + offset + MATCH_BLOCKSIZE <= builder->text->len; + offset += MATCH_BLOCKSIZE) + { + hash_key_t key = hash_key(builder->text->data + offset); + size_t idx = hash_to_index(&builder->hash, key); + + /* Don't replace hash entries that stem from the current text. + * This makes early matches more likely. */ + if (builder->hash.offsets[idx] == NO_OFFSET) + ++builder->hash.used; + else if (builder->hash.offsets[idx] >= instruction.offset) + continue; + + builder->hash.offsets[idx] = (apr_uint32_t)offset; + builder->hash.prefixes[idx] = builder->text->data[offset]; + } +} + +svn_error_t * +svn_fs_x__reps_add(apr_size_t *rep_idx, + svn_fs_x__reps_builder_t *builder, + const svn_string_t *contents) +{ + rep_t rep; + const char *current = contents->data; + const char *processed = current; + const char *end = current + contents->len; + const char *last_to_test = end - MATCH_BLOCKSIZE - 1; + + if (builder->text->len + contents->len > MAX_TEXT_BODY) + return svn_error_create(SVN_ERR_FS_CONTAINER_SIZE, NULL, + _("Text body exceeds star delta container capacity")); + + if ( builder->instructions->nelts + 2 * contents->len / MATCH_BLOCKSIZE + > MAX_INSTRUCTIONS) + return svn_error_create(SVN_ERR_FS_CONTAINER_SIZE, NULL, + _("Instruction count exceeds star delta container capacity")); + + rep.first_instruction = (apr_uint32_t)builder->instructions->nelts; + while (current < last_to_test) + { + hash_key_t key = hash_key(current); + size_t offset; + size_t idx; + + /* search for the next matching sequence */ + + for (; current < last_to_test; ++current) + { + idx = hash_to_index(&builder->hash, key); + if (builder->hash.prefixes[idx] == current[0]) + { + offset = builder->hash.offsets[idx]; + if ( (offset != NO_OFFSET) + && (memcmp(&builder->text->data[offset], current, + MATCH_BLOCKSIZE) == 0)) + break; + } + key = hash_key_replace(key, current[0], current[MATCH_BLOCKSIZE]); + } + + /* found it? */ + + if (current < last_to_test) + { + instruction_t instruction; + + /* extend the match */ + + size_t prefix_match + = svn_cstring__reverse_match_length(current, + builder->text->data + offset, + MIN(offset, current - processed)); + size_t postfix_match + = svn_cstring__match_length(current + MATCH_BLOCKSIZE, + builder->text->data + offset + MATCH_BLOCKSIZE, + MIN(builder->text->len - offset - MATCH_BLOCKSIZE, + end - current - MATCH_BLOCKSIZE)); + + /* non-matched section */ + + size_t new_copy = (current - processed) - prefix_match; + if (new_copy) + add_new_text(builder, processed, new_copy); + + /* add instruction for matching section */ + + instruction.offset = (apr_int32_t)(offset - prefix_match); + instruction.count = (apr_uint32_t)(prefix_match + postfix_match + + MATCH_BLOCKSIZE); + APR_ARRAY_PUSH(builder->instructions, instruction_t) = instruction; + + processed = current + MATCH_BLOCKSIZE + postfix_match; + current = processed; + } + } + + add_new_text(builder, processed, end - processed); + rep.instruction_count = (apr_uint32_t)builder->instructions->nelts + - rep.first_instruction; + APR_ARRAY_PUSH(builder->reps, rep_t) = rep; + + *rep_idx = (apr_size_t)(builder->reps->nelts - 1); + return SVN_NO_ERROR; +} + +apr_size_t +svn_fs_x__reps_estimate_size(const svn_fs_x__reps_builder_t *builder) +{ + /* approx: size of the text exclusive to us @ 50% compression rate + * + 2 bytes per instruction + * + 2 bytes per representation + * + 8 bytes per base representation + * + 1:8 inefficiency in using the base representations + * + 100 bytes static overhead + */ + return (builder->text->len - builder->base_text_len) / 2 + + builder->instructions->nelts * 2 + + builder->reps->nelts * 2 + + builder->bases->nelts * 8 + + builder->base_text_len / 8 + + 100; +} + +/* Execute COUNT instructions starting at INSTRUCTION_IDX in CONTAINER + * and fill the parts of EXTRACTOR->RESULT that we can from this container. + * Record the remainder in EXTRACTOR->MISSING. + * + * This function will recurse for instructions that reference other + * instruction sequences. COUNT refers to the top-level instructions only. + */ +static void +get_text(svn_fs_x__rep_extractor_t *extractor, + const svn_fs_x__reps_t *container, + apr_size_t instruction_idx, + apr_size_t count) +{ + const instruction_t *instruction; + const char *offset_0 = container->text - container->base_text_len; + + for (instruction = container->instructions + instruction_idx; + instruction < container->instructions + instruction_idx + count; + instruction++) + if (instruction->offset < 0) + { + /* instruction sub-sequence */ + get_text(extractor, container, -instruction->offset, + instruction->count); + } + else if (instruction->offset >= container->base_text_len) + { + /* direct copy instruction */ + svn_stringbuf_appendbytes(extractor->result, + offset_0 + instruction->offset, + instruction->count); + } + else + { + /* a section that we need to fill from some external base rep. */ + missing_t missing; + missing.base = 0; + missing.start = (apr_uint32_t)extractor->result->len; + missing.count = instruction->count; + missing.offset = instruction->offset; + svn_stringbuf_appendfill(extractor->result, 0, instruction->count); + + if (extractor->missing == NULL) + extractor->missing = apr_array_make(extractor->pool, 1, + sizeof(missing)); + + APR_ARRAY_PUSH(extractor->missing, missing_t) = missing; + } +} + +svn_error_t * +svn_fs_x__reps_get(svn_fs_x__rep_extractor_t **extractor, + svn_fs_t *fs, + const svn_fs_x__reps_t *container, + apr_size_t idx, + apr_pool_t *pool) +{ + apr_uint32_t first = container->first_instructions[idx]; + apr_uint32_t last = container->first_instructions[idx + 1]; + + /* create the extractor object */ + svn_fs_x__rep_extractor_t *result = apr_pcalloc(pool, sizeof(*result)); + result->fs = fs; + result->result = svn_stringbuf_create_empty(pool); + result->pool = pool; + + /* fill all the bits of the result that we can, i.e. all but bits coming + * from base representations */ + get_text(result, container, first, last - first); + *extractor = result; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__extractor_drive(svn_stringbuf_t **contents, + svn_fs_x__rep_extractor_t *extractor, + apr_size_t start_offset, + apr_size_t size, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + /* we don't support base reps right now */ + SVN_ERR_ASSERT(extractor->missing == NULL); + + if (size == 0) + { + *contents = svn_stringbuf_dup(extractor->result, result_pool); + } + else + { + /* clip the selected range */ + if (start_offset > extractor->result->len) + start_offset = extractor->result->len; + + if (size > extractor->result->len - start_offset) + size = extractor->result->len - start_offset; + + *contents = svn_stringbuf_ncreate(extractor->result->data + start_offset, + size, result_pool); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__write_reps_container(svn_stream_t *stream, + const svn_fs_x__reps_builder_t *builder, + apr_pool_t *scratch_pool) +{ + int i; + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + /* one top-level stream for each array */ + svn_packed__int_stream_t *bases_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + svn_packed__int_stream_t *reps_stream + = svn_packed__create_int_stream(root, TRUE, FALSE); + svn_packed__int_stream_t *instructions_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + + /* for misc stuff */ + svn_packed__int_stream_t *misc_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + + /* TEXT will be just a single string */ + svn_packed__byte_stream_t *text_stream + = svn_packed__create_bytes_stream(root); + + /* structure the struct streams such we can extract much of the redundancy + */ + svn_packed__create_int_substream(bases_stream, TRUE, TRUE); + svn_packed__create_int_substream(bases_stream, TRUE, FALSE); + svn_packed__create_int_substream(bases_stream, TRUE, FALSE); + svn_packed__create_int_substream(bases_stream, TRUE, FALSE); + + svn_packed__create_int_substream(instructions_stream, TRUE, TRUE); + svn_packed__create_int_substream(instructions_stream, FALSE, FALSE); + + /* text */ + svn_packed__add_bytes(text_stream, builder->text->data, builder->text->len); + + /* serialize bases */ + for (i = 0; i < builder->bases->nelts; ++i) + { + const base_t *base = &APR_ARRAY_IDX(builder->bases, i, base_t); + svn_packed__add_int(bases_stream, base->revision); + svn_packed__add_uint(bases_stream, base->item_index); + svn_packed__add_uint(bases_stream, base->priority); + svn_packed__add_uint(bases_stream, base->rep); + } + + /* serialize reps */ + for (i = 0; i < builder->reps->nelts; ++i) + { + const rep_t *rep = &APR_ARRAY_IDX(builder->reps, i, rep_t); + svn_packed__add_uint(reps_stream, rep->first_instruction); + } + + svn_packed__add_uint(reps_stream, builder->instructions->nelts); + + /* serialize instructions */ + for (i = 0; i < builder->instructions->nelts; ++i) + { + const instruction_t *instruction + = &APR_ARRAY_IDX(builder->instructions, i, instruction_t); + svn_packed__add_int(instructions_stream, instruction->offset); + svn_packed__add_uint(instructions_stream, instruction->count); + } + + /* other elements */ + svn_packed__add_uint(misc_stream, 0); + + /* write to stream */ + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_reps_container(svn_fs_x__reps_t **container, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i; + + base_t *bases; + apr_uint32_t *first_instructions; + instruction_t *instructions; + + svn_fs_x__reps_t *reps = apr_pcalloc(result_pool, sizeof(*reps)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *bases_stream; + svn_packed__int_stream_t *reps_stream; + svn_packed__int_stream_t *instructions_stream; + svn_packed__int_stream_t *misc_stream; + svn_packed__byte_stream_t *text_stream; + + /* read from disk */ + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + + bases_stream = svn_packed__first_int_stream(root); + reps_stream = svn_packed__next_int_stream(bases_stream); + instructions_stream = svn_packed__next_int_stream(reps_stream); + misc_stream = svn_packed__next_int_stream(instructions_stream); + text_stream = svn_packed__first_byte_stream(root); + + /* text */ + reps->text = svn_packed__get_bytes(text_stream, &reps->text_len); + reps->text = apr_pmemdup(result_pool, reps->text, reps->text_len); + + /* de-serialize bases */ + reps->base_count + = svn_packed__int_count(svn_packed__first_int_substream(bases_stream)); + bases = apr_palloc(result_pool, reps->base_count * sizeof(*bases)); + reps->bases = bases; + + for (i = 0; i < reps->base_count; ++i) + { + base_t *base = bases + i; + base->revision = (svn_revnum_t)svn_packed__get_int(bases_stream); + base->item_index = svn_packed__get_uint(bases_stream); + base->priority = (int)svn_packed__get_uint(bases_stream); + base->rep = (apr_uint32_t)svn_packed__get_uint(bases_stream); + } + + /* de-serialize instructions */ + reps->instruction_count + = svn_packed__int_count + (svn_packed__first_int_substream(instructions_stream)); + instructions + = apr_palloc(result_pool, + reps->instruction_count * sizeof(*instructions)); + reps->instructions = instructions; + + for (i = 0; i < reps->instruction_count; ++i) + { + instruction_t *instruction = instructions + i; + instruction->offset + = (apr_int32_t)svn_packed__get_int(instructions_stream); + instruction->count + = (apr_uint32_t)svn_packed__get_uint(instructions_stream); + } + + /* de-serialize reps */ + reps->rep_count = svn_packed__int_count(reps_stream); + first_instructions + = apr_palloc(result_pool, + (reps->rep_count + 1) * sizeof(*first_instructions)); + reps->first_instructions = first_instructions; + + for (i = 0; i < reps->rep_count; ++i) + first_instructions[i] + = (apr_uint32_t)svn_packed__get_uint(reps_stream); + first_instructions[reps->rep_count] = (apr_uint32_t)reps->instruction_count; + + /* other elements */ + reps->base_text_len = (apr_size_t)svn_packed__get_uint(misc_stream); + + /* return result */ + *container = reps; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_reps_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__reps_t *reps = in; + svn_stringbuf_t *serialized; + + /* make a guesstimate on the size of the serialized data. Erring on the + * low side will cause the serializer to re-alloc its buffer. */ + apr_size_t size + = reps->text_len + + reps->base_count * sizeof(*reps->bases) + + reps->rep_count * sizeof(*reps->first_instructions) + + reps->instruction_count * sizeof(*reps->instructions) + + 100; + + /* serialize array header and all its elements */ + svn_temp_serializer__context_t *context + = svn_temp_serializer__init(reps, sizeof(*reps), size, pool); + + /* serialize sub-structures */ + svn_temp_serializer__add_leaf(context, (const void **)&reps->text, + reps->text_len); + svn_temp_serializer__add_leaf(context, (const void **)&reps->bases, + reps->base_count * sizeof(*reps->bases)); + svn_temp_serializer__add_leaf(context, + (const void **)&reps->first_instructions, + reps->rep_count * + sizeof(*reps->first_instructions)); + svn_temp_serializer__add_leaf(context, (const void **)&reps->instructions, + reps->instruction_count * + sizeof(*reps->instructions)); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_reps_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__reps_t *reps = (svn_fs_x__reps_t *)data; + + /* de-serialize sub-structures */ + svn_temp_deserializer__resolve(reps, (void **)&reps->text); + svn_temp_deserializer__resolve(reps, (void **)&reps->bases); + svn_temp_deserializer__resolve(reps, (void **)&reps->first_instructions); + svn_temp_deserializer__resolve(reps, (void **)&reps->instructions); + + /* done */ + *out = reps; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__reps_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + svn_fs_x__reps_baton_t *reps_baton = baton; + + /* get a usable reps structure */ + const svn_fs_x__reps_t *cached = data; + svn_fs_x__reps_t *reps = apr_pmemdup(pool, cached, sizeof(*reps)); + + reps->text + = svn_temp_deserializer__ptr(cached, (const void **)&cached->text); + reps->bases + = svn_temp_deserializer__ptr(cached, (const void **)&cached->bases); + reps->first_instructions + = svn_temp_deserializer__ptr(cached, + (const void **)&cached->first_instructions); + reps->instructions + = svn_temp_deserializer__ptr(cached, + (const void **)&cached->instructions); + + /* return an extractor for the selected item */ + SVN_ERR(svn_fs_x__reps_get((svn_fs_x__rep_extractor_t **)out, + reps_baton->fs, reps, reps_baton->idx, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/reps.h b/contrib/subversion/subversion/libsvn_fs_x/reps.h new file mode 100644 index 000000000..720bfbfb4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/reps.h @@ -0,0 +1,190 @@ +/* reps.h --- FSX representation container + * + * ==================================================================== + * 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_FS__REPS_H +#define SVN_LIBSVN_FS__REPS_H + +#include "svn_io.h" +#include "fs.h" + +/* This container type implements the start-delta (aka pick lists) data + * structure plus functions to create it and read data from it. The key + * point is to identify common sub-strings within a whole set of fulltexts + * instead of only two as in the classic txdelta code. + * + * Because it is relatively expensive to optimize the final in-memory + * layout, representation containers cannot be updated. A builder object + * will do most of the space saving when adding fulltexts but the final + * data will only be created immediately before serializing everything to + * disk. So, builders are write only and representation containers are + * read-only. + * + * Extracting data from a representation container is O(length) but it + * may require multiple iterations if base representations outside the + * container were used. Therefore, you will first create an extractor + * object (this may happen while holding a cache lock) and the you need + * to "drive" the extractor outside any cache context. + */ + +/* A write-only constructor object for representation containers. + */ +typedef struct svn_fs_x__reps_builder_t svn_fs_x__reps_builder_t; + +/* A read-only representation container - + * an opaque collection of fulltexts, i.e. byte strings. + */ +typedef struct svn_fs_x__reps_t svn_fs_x__reps_t; + +/* The fulltext extractor utility object. + */ +typedef struct svn_fs_x__rep_extractor_t svn_fs_x__rep_extractor_t; + +/* Baton type to be passed to svn_fs_x__reps_get_func. + */ +typedef struct svn_fs_x__reps_baton_t +{ + /* filesystem the resulting extractor shall operate on */ + svn_fs_t *fs; + + /* element index of the item to extract from the container */ + apr_size_t idx; +} svn_fs_x__reps_baton_t; + +/* Create and populate noderev containers. */ + +/* Create and return a new builder object, allocated in RESULT_POOL. + */ +svn_fs_x__reps_builder_t * +svn_fs_x__reps_builder_create(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* To BUILDER, add reference to the fulltext currently stored in + * representation REP. Substrings matching with any of the base reps + * in BUILDER can be removed from the text base and be replaced by + * references to those base representations. + * + * The PRIORITY is a mere hint on which base representations should + * preferred in case we could re-use the same contents from multiple bases. + * Higher numerical value means higher priority / likelihood of being + * selected over others. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__reps_add_base(svn_fs_x__reps_builder_t *builder, + svn_fs_x__representation_t *rep, + int priority, + apr_pool_t *scratch_pool); + +/* Add the byte string CONTENTS to BUILDER. Return the item index under + * which the fulltext can be retrieved from the final container in *REP_IDX. + */ +svn_error_t * +svn_fs_x__reps_add(apr_size_t *rep_idx, + svn_fs_x__reps_builder_t *builder, + const svn_string_t *contents); + +/* Return a rough estimate in bytes for the serialized representation + * of BUILDER. + */ +apr_size_t +svn_fs_x__reps_estimate_size(const svn_fs_x__reps_builder_t *builder); + +/* Read from representation containers. */ + +/* For fulltext IDX in CONTAINER in filesystem FS, create an extract object + * allocated in POOL and return it in *EXTRACTOR. + */ +svn_error_t * +svn_fs_x__reps_get(svn_fs_x__rep_extractor_t **extractor, + svn_fs_t *fs, + const svn_fs_x__reps_t *container, + apr_size_t idx, + apr_pool_t *pool); + +/* Let the EXTRACTOR object fetch all parts of the desired fulltext and + * return the latter in *CONTENTS. If SIZE is not 0, return SIZE bytes + * starting at offset START_OFFSET of the full contents. If that range + * lies partly or completely outside the content, clip it accordingly. + * Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporary + * allocations. + * + * Note, you may not run this inside a cache access function. + */ +svn_error_t * +svn_fs_x__extractor_drive(svn_stringbuf_t** contents, + svn_fs_x__rep_extractor_t* extractor, + apr_size_t start_offset, + apr_size_t size, + apr_pool_t* result_pool, + apr_pool_t* scratch_pool); + +/* I/O interface. */ + +/* Write a serialized representation of the final container described by + * BUILDER to STREAM. Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__write_reps_container(svn_stream_t *stream, + const svn_fs_x__reps_builder_t *builder, + apr_pool_t *scratch_pool); + +/* Read a representations container from its serialized representation in + * STREAM. Allocate the result in RESULT_POOL and return it in *CONTAINER. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__read_reps_container(svn_fs_x__reps_t **container, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Implements #svn_cache__serialize_func_t for svn_fs_x__reps_t objects. + */ +svn_error_t * +svn_fs_x__serialize_reps_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/* Implements #svn_cache__deserialize_func_t for svn_fs_x__reps_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_reps_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/* Implements svn_cache__partial_getter_func_t for svn_fs_x__reps_t, + * setting *OUT to an svn_fs_x__rep_extractor_t object defined by the + * svn_fs_x__reps_baton_t passed in as *BATON. This function is similar + * to svn_fs_x__reps_get but operates on the cache serialized + * representation of the container. + */ +svn_error_t * +svn_fs_x__reps_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/rev_file.c b/contrib/subversion/subversion/libsvn_fs_x/rev_file.c new file mode 100644 index 000000000..445d45b55 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rev_file.c @@ -0,0 +1,316 @@ +/* rev_file.c --- revision file and index access functions + * + * ==================================================================== + * 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 "rev_file.h" +#include "fs_x.h" +#include "index.h" +#include "low_level.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "private/svn_io_private.h" +#include "svn_private_config.h" + +/* Return a new revision file instance, allocated in RESULT_POOL, for + * filesystem FS. Set its pool member to the provided RESULT_POOL. */ +static svn_fs_x__revision_file_t * +create_revision_file(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__revision_file_t *file = apr_palloc(result_pool, sizeof(*file)); + + file->is_packed = FALSE; + file->start_revision = SVN_INVALID_REVNUM; + + file->file = NULL; + file->stream = NULL; + file->p2l_stream = NULL; + file->l2p_stream = NULL; + file->block_size = ffd->block_size; + file->l2p_offset = -1; + file->l2p_checksum = NULL; + file->p2l_offset = -1; + file->p2l_checksum = NULL; + file->footer_offset = -1; + file->pool = result_pool; + + return file; +} + +/* Return a new revision file instance, allocated in RESULT_POOL, for + * REVISION in filesystem FS. Set its pool member to the provided + * RESULT_POOL. */ +static svn_fs_x__revision_file_t * +init_revision_file(svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *result_pool) +{ + svn_fs_x__revision_file_t *file = create_revision_file(fs, result_pool); + + file->is_packed = svn_fs_x__is_packed_rev(fs, revision); + file->start_revision = svn_fs_x__packed_base_rev(fs, revision); + + return file; +} + +/* Baton type for set_read_only() */ +typedef struct set_read_only_baton_t +{ + /* File to set to read-only. */ + const char *file_path; + + /* Scratch pool sufficient life time. + * Ideally the pool that we registered the cleanup on. */ + apr_pool_t *pool; +} set_read_only_baton_t; + +/* APR pool cleanup callback taking a set_read_only_baton_t baton and then + * (trying to) set the specified file to r/o mode. */ +static apr_status_t +set_read_only(void *baton) +{ + set_read_only_baton_t *ro_baton = baton; + apr_status_t status = APR_SUCCESS; + svn_error_t *err; + + err = svn_io_set_file_read_only(ro_baton->file_path, TRUE, ro_baton->pool); + if (err) + { + status = err->apr_err; + svn_error_clear(err); + } + + return status; +} + +/* If the file at PATH is read-only, attempt to make it writable. The + * original state will be restored with RESULT_POOL gets cleaned up. + * SCRATCH_POOL is for temporary allocations. */ +static svn_error_t * +auto_make_writable(const char *path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t is_read_only; + apr_finfo_t finfo; + + SVN_ERR(svn_io_stat(&finfo, path, SVN__APR_FINFO_READONLY, scratch_pool)); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, scratch_pool)); + + if (is_read_only) + { + /* Tell the pool to restore the r/o state upon cleanup + (assuming the file will still exist, failing silently otherwise). */ + set_read_only_baton_t *baton = apr_pcalloc(result_pool, + sizeof(*baton)); + baton->pool = result_pool; + baton->file_path = apr_pstrdup(result_pool, path); + apr_pool_cleanup_register(result_pool, baton, + set_read_only, apr_pool_cleanup_null); + + /* Finally, allow write access (undoing it has already been scheduled + and is idempotent). */ + SVN_ERR(svn_io_set_file_read_write(path, FALSE, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Core implementation of svn_fs_fs__open_pack_or_rev_file working on an + * existing, initialized FILE structure. If WRITABLE is TRUE, give write + * access to the file - temporarily resetting the r/o state if necessary. + */ +static svn_error_t * +open_pack_or_rev_file(svn_fs_x__revision_file_t *file, + svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t writable, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_boolean_t retry = FALSE; + + do + { + const char *path = svn_fs_x__path_rev_absolute(fs, rev, scratch_pool); + apr_file_t *apr_file; + apr_int32_t flags = writable + ? APR_READ | APR_WRITE | APR_BUFFERED + : APR_READ | APR_BUFFERED; + + /* We may have to *temporarily* enable write access. */ + err = writable ? auto_make_writable(path, result_pool, scratch_pool) + : SVN_NO_ERROR; + + /* open the revision file in buffered r/o or r/w mode */ + if (!err) + err = svn_io_file_open(&apr_file, path, flags, APR_OS_DEFAULT, + result_pool); + + if (!err) + { + file->file = apr_file; + file->stream = svn_stream_from_aprfile2(apr_file, TRUE, + result_pool); + + return SVN_NO_ERROR; + } + + if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + /* 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(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool)); + file->start_revision = svn_fs_x__packed_base_rev(fs, rev); + + retry = TRUE; + } + else + { + retry = FALSE; + } + } + while (retry); + + return svn_error_trace(err); +} + +svn_error_t * +svn_fs_x__open_pack_or_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *file = init_revision_file(fs, rev, result_pool); + return svn_error_trace(open_pack_or_rev_file(*file, fs, rev, FALSE, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_fs_x__open_pack_or_rev_file_writable(svn_fs_x__revision_file_t** file, + svn_fs_t* fs, + svn_revnum_t rev, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool) +{ + *file = init_revision_file(fs, rev, result_pool); + return svn_error_trace(open_pack_or_rev_file(*file, fs, rev, TRUE, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_fs_x__auto_read_footer(svn_fs_x__revision_file_t *file) +{ + if (file->l2p_offset == -1) + { + apr_off_t filesize = 0; + unsigned char footer_length; + svn_stringbuf_t *footer; + + /* Determine file size. */ + SVN_ERR(svn_io_file_seek(file->file, APR_END, &filesize, file->pool)); + + /* Read last byte (containing the length of the footer). */ + SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, + filesize - 1, file->pool)); + SVN_ERR(svn_io_file_read_full2(file->file, &footer_length, + sizeof(footer_length), NULL, NULL, + file->pool)); + + /* Read footer. */ + footer = svn_stringbuf_create_ensure(footer_length, file->pool); + SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, + filesize - 1 - footer_length, + file->pool)); + SVN_ERR(svn_io_file_read_full2(file->file, footer->data, footer_length, + &footer->len, NULL, file->pool)); + footer->data[footer->len] = '\0'; + + /* Extract index locations. */ + SVN_ERR(svn_fs_x__parse_footer(&file->l2p_offset, &file->l2p_checksum, + &file->p2l_offset, &file->p2l_checksum, + footer, file->start_revision, + file->pool)); + file->footer_offset = filesize - footer_length - 1; + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__open_proto_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *apr_file; + SVN_ERR(svn_io_file_open(&apr_file, + svn_fs_x__path_txn_proto_rev(fs, txn_id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + result_pool)); + + return svn_error_trace(svn_fs_x__wrap_temp_rev_file(file, fs, apr_file, + result_pool)); +} + +svn_error_t * +svn_fs_x__wrap_temp_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + apr_file_t *temp_file, + apr_pool_t *result_pool) +{ + *file = create_revision_file(fs, result_pool); + (*file)->file = temp_file; + (*file)->stream = svn_stream_from_aprfile2(temp_file, TRUE, result_pool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__close_revision_file(svn_fs_x__revision_file_t *file) +{ + if (file->stream) + SVN_ERR(svn_stream_close(file->stream)); + if (file->file) + SVN_ERR(svn_io_file_close(file->file, file->pool)); + + file->file = NULL; + file->stream = NULL; + file->l2p_stream = NULL; + file->p2l_stream = NULL; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/rev_file.h b/contrib/subversion/subversion/libsvn_fs_x/rev_file.h new file mode 100644 index 000000000..b96d03518 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/rev_file.h @@ -0,0 +1,154 @@ +/* rev_file.h --- revision file and index access data structure + * + * ==================================================================== + * 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_FS_X__REV_FILE_H +#define SVN_LIBSVN_FS_X__REV_FILE_H + +#include "svn_fs.h" +#include "id.h" + +/* In format 7, index files must be read in sync with the respective + * revision / pack file. I.e. we must use packed index files for packed + * rev files and unpacked ones for non-packed rev files. So, the whole + * point is to open them with matching "is packed" setting in case some + * background pack process was run. + */ + +/* Opaque index stream type. + */ +typedef struct svn_fs_x__packed_number_stream_t + svn_fs_x__packed_number_stream_t; + +/* Data file, including indexes data, and associated properties for + * START_REVISION. As the FILE is kept open, background pack operations + * will not cause access to this file to fail. + */ +typedef struct svn_fs_x__revision_file_t +{ + /* first (potentially only) revision in the rev / pack file. + * SVN_INVALID_REVNUM for txn proto-rev files. */ + svn_revnum_t start_revision; + + /* the revision was packed when the first file / stream got opened */ + svn_boolean_t is_packed; + + /* rev / pack file */ + apr_file_t *file; + + /* stream based on FILE and not NULL exactly when FILE is not NULL */ + svn_stream_t *stream; + + /* the opened P2L index stream or NULL. Always NULL for txns. */ + svn_fs_x__packed_number_stream_t *p2l_stream; + + /* the opened L2P index stream or NULL. Always NULL for txns. */ + svn_fs_x__packed_number_stream_t *l2p_stream; + + /* Copied from FS->FFD->BLOCK_SIZE upon creation. It allows us to + * use aligned seek() without having the FS handy. */ + apr_off_t block_size; + + /* Offset within FILE at which the rev data ends and the L2P index + * data starts. Less than P2L_OFFSET. -1 if svn_fs_fs__auto_read_footer + * has not been called, yet. */ + apr_off_t l2p_offset; + + /* MD5 checksum on the whole on-disk representation of the L2P index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *l2p_checksum; + + /* Offset within FILE at which the L2P index ends and the P2L index + * data starts. Greater than L2P_OFFSET. -1 if svn_fs_fs__auto_read_footer + * has not been called, yet. */ + apr_off_t p2l_offset; + + /* MD5 checksum on the whole on-disk representation of the P2L index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *p2l_checksum; + + /* Offset within FILE at which the P2L index ends and the footer starts. + * Greater than P2L_OFFSET. -1 if svn_fs_fs__auto_read_footer has not + * been called, yet. */ + apr_off_t footer_offset; + + /* pool containing this object */ + apr_pool_t *pool; +} svn_fs_x__revision_file_t; + +/* 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. Allocate *FILE in RESULT_POOL and use SCRATCH_POOL + * for temporaries. */ +svn_error_t * +svn_fs_x__open_pack_or_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Open the correct revision file for REV with read and write access. + * If necessary, temporarily reset the file's read-only state. 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. + * Allocate *FILE in RESULT_POOL and use SCRATCH_POOLfor temporaries. */ +svn_error_t * +svn_fs_x__open_pack_or_rev_file_writable(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* If the footer data in FILE has not been read, yet, do so now. + * Index locations will only be read upon request as we assume they get + * cached and the FILE is usually used for REP data access only. + * Hence, the separate step. + */ +svn_error_t * +svn_fs_x__auto_read_footer(svn_fs_x__revision_file_t *file); + +/* Open the proto-rev file of transaction TXN_ID in FS and return it in *FILE. + * Allocate *FILE in RESULT_POOL use and SCRATCH_POOL for temporaries.. */ +svn_error_t * +svn_fs_x__open_proto_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t* result_pool, + apr_pool_t *scratch_pool); + +/* Wrap the TEMP_FILE, used in the context of FS, into a revision file + * struct, allocated in RESULT_POOL, and return it in *FILE. + */ +svn_error_t * +svn_fs_x__wrap_temp_rev_file(svn_fs_x__revision_file_t **file, + svn_fs_t *fs, + apr_file_t *temp_file, + apr_pool_t *result_pool); + +/* Close all files and streams in FILE. + */ +svn_error_t * +svn_fs_x__close_revision_file(svn_fs_x__revision_file_t *file); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/revprops.c b/contrib/subversion/subversion/libsvn_fs_x/revprops.c new file mode 100644 index 000000000..5bc62ccc1 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/revprops.c @@ -0,0 +1,1948 @@ +/* revprops.c --- everything needed to handle revprops in FSX + * + * ==================================================================== + * 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 +#include + +#include "svn_pools.h" +#include "svn_hash.h" +#include "svn_dirent_uri.h" + +#include "fs_x.h" +#include "revprops.h" +#include "util.h" +#include "transaction.h" + +#include "private/svn_subr_private.h" +#include "private/svn_string_private.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* 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) + +/* In case of an inconsistent read, close the generation file, yield, + re-open and re-read. This is the number of times we try this before + giving up. */ +#define GENERATION_READ_RETRY_COUNT 100 + +/* Maximum size of the generation number file contents (including NUL). */ +#define CHECKSUMMED_NUMBER_BUFFER_LEN \ + (SVN_INT64_BUFFER_SIZE + 3 + APR_MD5_DIGESTSIZE * 2) + + +svn_error_t * +svn_fs_x__upgrade_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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) + { + svn_pool_clear(iterpool); + + 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(svn_fs_x__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, + cancel_func, cancel_baton, iterpool)); + if (notify_func) + SVN_ERR(notify_func(notify_baton, shard, + svn_fs_upgrade_pack_revprops, iterpool)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__upgrade_cleanup_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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) + { + svn_pool_clear(iterpool); + + revprops_shard_path = svn_dirent_join(revsprops_dir, + apr_psprintf(iterpool, "%" APR_INT64_T_FMT, shard), + iterpool); + SVN_ERR(svn_fs_x__delete_revprops_shard(revprops_shard_path, + shard, ffd->max_files_per_dir, + cancel_func, cancel_baton, + iterpool)); + if (notify_func) + SVN_ERR(notify_func(notify_baton, shard, + svn_fs_upgrade_cleanup_revprops, iterpool)); + } + + svn_pool_destroy(iterpool); + + 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. 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. + * + * We track the revprop generation in a persistent, unbuffered file that + * we may keep open for the lifetime of the svn_fs_t. It is the OS' + * responsibility to provide us with the latest contents upon read. To + * detect incomplete updates due to non-atomic reads, we put a MD5 checksum + * next to the actual generation number and verify that it matches. + * + * Since we cannot guarantee that the OS will provide us with up-to-date + * data buffers for open files, we re-open and re-read the file before + * modifying it. This will prevent lost updates. + * + * A race condition exists between switching to the modified revprop data + * and bumping the generation number. In particular, the process may crash + * just after switching to the new revprop data and before bumping the + * generation. To be able to detect this scenario, we bump the generation + * twice per revprop change: once immediately before (creating an odd number) + * and once after the atomic switch (even generation). + * + * A writer holding the write lock can immediately assume a crashed writer + * in case of an odd generation or they would not have been able to acquire + * the lock. A reader detecting an odd generation will use that number and + * be forced to re-read any revprop data - usually getting the new revprops + * already. If the generation file modification timestamp is too old, the + * reader will assume a crashed writer, acquire the write lock and bump + * the generation if it is still odd. So, for about REVPROP_CHANGE_TIMEOUT + * after the crash, reader caches may be stale. + */ + +/* If the revprop generation file in FS is open, close it. This is a no-op + * if the file is not open. + */ +static svn_error_t * +close_revprop_generation_file(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + if (ffd->revprop_generation_file) + { + SVN_ERR(svn_io_file_close(ffd->revprop_generation_file, scratch_pool)); + ffd->revprop_generation_file = NULL; + } + + return SVN_NO_ERROR; +} + +/* Make sure the revprop_generation member in FS is set. If READ_ONLY is + * set, open the file w/o write permission if the file is not open yet. + * The file is kept open if it has sufficient rights (or more) but will be + * closed and re-opened if it provided insufficient access rights. + * + * Call only for repos that support revprop caching. + */ +static svn_error_t * +open_revprop_generation_file(svn_fs_t *fs, + svn_boolean_t read_only, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_int32_t flags = read_only ? APR_READ : (APR_READ | APR_WRITE); + + /* Close the current file handle if it has insufficient rights. */ + if ( ffd->revprop_generation_file + && (apr_file_flags_get(ffd->revprop_generation_file) & flags) != flags) + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + + /* If not open already, open with sufficient rights. */ + if (ffd->revprop_generation_file == NULL) + { + const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool); + SVN_ERR(svn_io_file_open(&ffd->revprop_generation_file, path, + flags, APR_OS_DEFAULT, fs->pool)); + } + + return SVN_NO_ERROR; +} + +/* Return the textual representation of NUMBER and its checksum in *BUFFER. + */ +static svn_error_t * +checkedsummed_number(svn_stringbuf_t **buffer, + apr_int64_t number, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_checksum_t *checksum; + const char *digest; + + char str[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__i64toa(str, number); + str[len] = 0; + + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, len, scratch_pool)); + digest = svn_checksum_to_cstring_display(checksum, scratch_pool); + + *buffer = svn_stringbuf_createf(result_pool, "%s %s\n", digest, str); + + return SVN_NO_ERROR; +} + +/* Extract the generation number from the text BUFFER of LEN bytes and + * verify it against the checksum in the same BUFFER. If they match, return + * the generation in *NUMBER. Otherwise, return an error. + * BUFFER does not need to be NUL-terminated. + */ +static svn_error_t * +verify_extract_number(apr_int64_t *number, + const char *buffer, + apr_size_t len, + apr_pool_t *scratch_pool) +{ + const char *digest_end = strchr(buffer, ' '); + + /* Does the buffer even contain checksum _and_ number? */ + if (digest_end != NULL) + { + svn_checksum_t *expected; + svn_checksum_t *actual; + + SVN_ERR(svn_checksum_parse_hex(&expected, svn_checksum_md5, buffer, + scratch_pool)); + SVN_ERR(svn_checksum(&actual, svn_checksum_md5, digest_end + 1, + (buffer + len) - (digest_end + 1), scratch_pool)); + + if (svn_checksum_match(expected, actual)) + return svn_error_trace(svn_cstring_atoi64(number, digest_end + 1)); + } + + /* Incomplete buffer or not a match. */ + return svn_error_create(SVN_ERR_FS_INVALID_GENERATION, NULL, + _("Invalid generation number data.")); +} + +/* Read revprop generation as stored on disk for repository FS. The result is + * returned in *CURRENT. Call only for repos that support revprop caching. + */ +static svn_error_t * +read_revprop_generation_file(apr_int64_t *current, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + char buf[CHECKSUMMED_NUMBER_BUFFER_LEN]; + apr_size_t len; + apr_off_t offset = 0; + int i; + svn_error_t *err = SVN_NO_ERROR; + + /* Retry in case of incomplete file buffer updates. */ + for (i = 0; i < GENERATION_READ_RETRY_COUNT; ++i) + { + svn_error_clear(err); + svn_pool_clear(iterpool); + + /* If we can't even access the data, things are very wrong. + * Don't retry in that case. + */ + SVN_ERR(open_revprop_generation_file(fs, TRUE, iterpool)); + SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset, + iterpool)); + + len = sizeof(buf); + SVN_ERR(svn_io_read_length_line(ffd->revprop_generation_file, buf, &len, + iterpool)); + + /* Some data has been read. It will most likely be complete and + * consistent. Extract and verify anyway. */ + err = verify_extract_number(current, buf, len, iterpool); + if (!err) + break; + + /* Got unlucky and data was invalid. Retry. */ + SVN_ERR(close_revprop_generation_file(fs, iterpool)); + +#if APR_HAS_THREADS + apr_thread_yield(); +#else + apr_sleep(0); +#endif + } + + svn_pool_destroy(iterpool); + + /* If we had to give up, propagate the error. */ + return svn_error_trace(err); +} + +/* Write the CURRENT revprop generation to disk for repository FS. + * Call only for repos that support revprop caching. + */ +static svn_error_t * +write_revprop_generation_file(svn_fs_t *fs, + apr_int64_t current, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_stringbuf_t *buffer; + apr_off_t offset = 0; + + SVN_ERR(checkedsummed_number(&buffer, current, scratch_pool, scratch_pool)); + + SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool)); + SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset, + scratch_pool)); + SVN_ERR(svn_io_file_write_full(ffd->revprop_generation_file, buffer->data, + buffer->len, NULL, scratch_pool)); + SVN_ERR(svn_io_file_flush_to_disk(ffd->revprop_generation_file, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool); + svn_stringbuf_t *buffer; + + /* Unconditionally close the revprop generation file. + * Don't care about FS formats. This ensures consistent internal state. */ + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + + /* Unconditionally remove any old revprop generation file. + * Don't care about FS formats. This ensures consistent on-disk state + * for old format repositories. */ + SVN_ERR(svn_io_remove_file2(path, TRUE, scratch_pool)); + + /* Write the initial revprop generation file contents, if supported by + * the current format. This ensures consistent on-disk state for new + * format repositories. */ + SVN_ERR(checkedsummed_number(&buffer, 0, scratch_pool, scratch_pool)); + SVN_ERR(svn_io_write_atomic(path, buffer->data, buffer->len, NULL, + scratch_pool)); + + /* ffd->revprop_generation_file will be re-opened on demand. */ + + return SVN_NO_ERROR; +} + +/* Create an error object with the given MESSAGE and pass it to the + WARNING member of FS. Clears UNDERLYING_ERR. */ +static void +log_revprop_cache_init_warning(svn_fs_t *fs, + svn_error_t *underlying_err, + const char *message, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = svn_error_createf( + SVN_ERR_FS_REVPROP_CACHE_INIT_FAILURE, + underlying_err, message, + svn_dirent_local_style(fs->path, scratch_pool)); + + 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 *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_error_t *error; + + /* is the cache (still) enabled? */ + if (ffd->revprop_cache == NULL) + return FALSE; + + /* try initialize our file-backed infrastructure */ + error = open_revprop_generation_file(fs, TRUE, scratch_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 infrastructure for revprop " + "caching failed to initialize.", + scratch_pool); + + return FALSE; + } + + return TRUE; +} + +/* Baton structure for revprop_generation_fixup. */ +typedef struct revprop_generation_fixup_t +{ + /* revprop generation to read */ + apr_int64_t *generation; + + /* file system context */ + svn_fs_t *fs; +} 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 everyone's + cache content. + Execute this only while holding the write lock to the repo in baton->FFD. + */ +static svn_error_t * +revprop_generation_fixup(void *void_baton, + apr_pool_t *scratch_pool) +{ + revprop_generation_upgrade_t *baton = void_baton; + svn_fs_x__data_t *ffd = baton->fs->fsap_data; + assert(ffd->has_write_lock); + + /* Make sure we don't operate on stale OS buffers. */ + SVN_ERR(close_revprop_generation_file(baton->fs, scratch_pool)); + + /* Maybe, either the original revprop writer or some other reader has + already corrected / bumped the revprop generation. Thus, we need + to read it again. However, we will now be the only ones changing + the file contents due to us holding the write lock. */ + SVN_ERR(read_revprop_generation_file(baton->generation, baton->fs, + scratch_pool)); + + /* Cause everyone to re-read revprops upon their next access, if the + last revprop write did not complete properly. */ + if (*baton->generation % 2) + { + ++*baton->generation; + SVN_ERR(write_revprop_generation_file(baton->fs, + *baton->generation, + scratch_pool)); + } + + 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 *scratch_pool) +{ + apr_int64_t current = 0; + svn_fs_x__data_t *ffd = fs->fsap_data; + + /* read the current revprop generation number */ + SVN_ERR(read_revprop_generation_file(¤t, fs, scratch_pool)); + + /* is an unfinished revprop write under the way? */ + if (current % 2) + { + svn_boolean_t timeout = FALSE; + + /* Has the writer process been aborted? + * Either by timeout or by us being the writer now. + */ + if (!ffd->has_write_lock) + { + apr_time_t mtime; + SVN_ERR(svn_io_file_affected_time(&mtime, + svn_fs_x__path_revprop_generation(fs, scratch_pool), + scratch_pool)); + timeout = apr_time_now() > mtime + REVPROP_CHANGE_TIMEOUT; + } + + if (ffd->has_write_lock || timeout) + { + revprop_generation_upgrade_t baton; + baton.generation = ¤t; + baton.fs = fs; + + /* 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, scratch_pool)); + else + SVN_ERR(svn_fs_x__with_write_lock(fs, revprop_generation_fixup, + &baton, scratch_pool)); + } + } + + /* return the value we just got */ + *generation = current; + return SVN_NO_ERROR; +} + +/* Set the revprop generation in FS to the next odd number to indicate + that there is a revprop write process under way. Return that value + in *GENERATION. If the change times out, readers shall recover from + that state & re-read revprops. + This is a no-op for repo formats that don't support revprop caching. */ +static svn_error_t * +begin_revprop_change(apr_int64_t *generation, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + SVN_ERR_ASSERT(ffd->has_write_lock); + + /* Close and re-open to make sure we read the latest data. */ + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool)); + + /* Set the revprop generation to an odd value to indicate + * that a write is in progress. + */ + SVN_ERR(read_revprop_generation(generation, fs, scratch_pool)); + ++*generation; + SVN_ERR(write_revprop_generation_file(fs, *generation, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Set the revprop generation in FS to the next even generation after + the odd value in GENERATION to indicate that + a) readers shall re-read revprops, and + b) the write process has been completed (no recovery required). + This is a no-op for repo formats that don't support revprop caching. */ +static svn_error_t * +end_revprop_change(svn_fs_t *fs, + apr_int64_t generation, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + SVN_ERR_ASSERT(ffd->has_write_lock); + SVN_ERR_ASSERT(generation % 2); + + /* Set the revprop generation to an even value to indicate + * that a write has been completed. Since we held the write + * lock, nobody else could have updated the file contents. + */ + SVN_ERR(write_revprop_generation_file(fs, generation + 1, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* 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 RESULT_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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *stream = svn_stream_from_string(content, scratch_pool); + *properties = apr_hash_make(result_pool); + + SVN_ERR(svn_hash_read2(*properties, stream, SVN_HASH_TERMINATOR, + result_pool)); + if (has_revprop_cache(fs, scratch_pool)) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__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. + * + * Allocate *PROPERTIES in RESULT_POOL and temporaries in SCRATCH_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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *content = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_boolean_t missing = FALSE; + int i; + + for (i = 0; + i < SVN_FS_X__RECOVERABLE_RETRY_COUNT && !missing && !content; + ++i) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_x__try_stringbuf_from_file(&content, + &missing, + svn_fs_x__path_revprops(fs, rev, iterpool), + i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT, + iterpool)); + } + + if (content) + SVN_ERR(parse_revprop(properties, fs, rev, generation, + svn_stringbuf__morph_into_string(content), + result_pool, iterpool)); + + svn_pool_clear(iterpool); + + return SVN_NO_ERROR; +} + +/* Return the minimum length of any packed revprop file name in REVPROPS. */ +static apr_size_t +get_min_filename_len(packed_revprops_t *revprops) +{ + char number_buffer[SVN_INT64_BUFFER_SIZE]; + + /* The revprop filenames have the format . - with being + * at least the first rev in the shard and having at least one + * digit. Thus, the minimum is 2 + #decimal places in the start rev. + */ + return svn__i64toa(number_buffer, revprops->manifest_start) + 2; +} + +/* Given FS and REVPROPS->REVISION, fill the FILENAME, FOLDER and MANIFEST + * members. Use RESULT_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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_stringbuf_t *content = NULL; + const char *manifest_file_path; + int idx, rev_count; + char *buffer, *buffer_end; + const char **filenames, **filenames_end; + apr_size_t min_filename_len; + + /* Determine the dimensions. Rev 0 is excluded from the first shard. */ + rev_count = ffd->max_files_per_dir; + revprops->manifest_start + = revprops->revision - (revprops->revision % rev_count); + if (revprops->manifest_start == 0) + { + ++revprops->manifest_start; + --rev_count; + } + + revprops->manifest = apr_array_make(result_pool, rev_count, + sizeof(const char*)); + + /* No line in the file can be less than this number of chars long. */ + min_filename_len = get_min_filename_len(revprops); + + /* Read the content of the manifest file */ + revprops->folder + = svn_fs_x__path_revprops_pack_shard(fs, revprops->revision, result_pool); + manifest_file_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, + result_pool); + + SVN_ERR(svn_fs_x__read_content(&content, manifest_file_path, result_pool)); + + /* There CONTENT must have a certain minimal size and there no + * unterminated lines at the end of the file. Both guarantees also + * simplify the parser loop below. + */ + if ( content->len < rev_count * (min_filename_len + 1) + || content->data[content->len - 1] != '\n') + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld not " + "properly terminated"), revprops->revision); + + /* Chop (parse) the manifest CONTENT into filenames, one per line. + * We only have to replace all newlines with NUL and add all line + * starts to REVPROPS->MANIFEST. + * + * There must be exactly REV_COUNT lines and that is the number of + * lines we parse from BUFFER to FILENAMES. Set the end pointer for + * the source BUFFER such that BUFFER+MIN_FILENAME_LEN is still valid + * BUFFER_END is always valid due to CONTENT->LEN > MIN_FILENAME_LEN. + * + * Please note that this loop is performance critical for e.g. 'svn log'. + * It is run 1000x per revprop access, i.e. per revision and about + * 50 million times per sec (and CPU core). + */ + for (filenames = (const char **)revprops->manifest->elts, + filenames_end = filenames + rev_count, + buffer = content->data, + buffer_end = buffer + content->len - min_filename_len; + (filenames < filenames_end) && (buffer < buffer_end); + ++filenames) + { + /* BUFFER always points to the start of the next line / filename. */ + *filenames = buffer; + + /* Find the next EOL. This is guaranteed to stay within the CONTENT + * buffer because we left enough room after BUFFER_END and we know + * we will always see a newline as the last non-NUL char. */ + buffer += min_filename_len; + while (*buffer != '\n') + ++buffer; + + /* Found EOL. Turn it into the filename terminator and move BUFFER + * to the start of the next line or CONTENT buffer end. */ + *buffer = '\0'; + ++buffer; + } + + /* We must have reached the end of both buffers. */ + if (buffer < content->data + content->len) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld " + "has too many entries"), revprops->revision); + + if (filenames < filenames_end) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Packed revprop manifest for r%ld " + "has too few entries"), revprops->revision); + + /* The target array has now exactly one entry per revision. */ + revprops->manifest->nelts = rev_count; + + /* Now get the file name */ + idx = (int)(revprops->revision - revprops->manifest_start); + 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) +{ + svn_fs_x__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 member, and make PACKED_REVPROPS point to the + * first serialized revprop. If READ_ALL is set, initialize the SIZES + * and OFFSETS members as well. + * + * 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, + svn_boolean_t read_all, + apr_pool_t *result_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); + svn_boolean_t cache_all = has_revprop_cache(fs, scratch_pool); + + /* decompress (even if the data is only "stored", there is still a + * length header to remove) */ + svn_stringbuf_t *compressed = revprops->packed_revprops; + svn_stringbuf_t *uncompressed = svn_stringbuf_create_empty(result_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(svn_fs_x__read_number_from_stream(&first_rev, NULL, stream, + iterpool)); + SVN_ERR(svn_fs_x__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 (!svn_fs_x__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(result_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. */ + revprops->start_revision = (svn_revnum_t)first_rev; + if (read_all) + { + /* Init / construct REVPROPS members. */ + revprops->sizes = apr_array_make(result_pool, (int)count, + sizeof(offset)); + revprops->offsets = apr_array_make(result_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; + svn_revnum_t revision = (svn_revnum_t)(first_rev + i); + svn_pool_clear(iterpool); + + /* read & check the serialized size */ + SVN_ERR(svn_fs_x__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) + { + /* Parse (and possibly cache) the one revprop list we care about. */ + SVN_ERR(parse_revprop(&revprops->properties, fs, revision, + revprops->generation, &serialized, + result_pool, iterpool)); + revprops->serialized_size = serialized.len; + + /* If we only wanted the revprops for REVISION then we are done. */ + if (!read_all && !cache_all) + break; + } + else if (cache_all) + { + /* Parse and cache all other revprop lists. */ + apr_hash_t *properties; + SVN_ERR(parse_revprop(&properties, fs, revision, + revprops->generation, &serialized, + iterpool, iterpool)); + } + + if (read_all) + { + /* 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; + } + + 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. + * If you want to modify revprop contents / update REVPROPS, READ_ALL + * must be set. Otherwise, only the properties of REV are being provided. + * + * Allocate *PROPERTIES in RESULT_POOL and temporaries in SCRATCH_POOL. + */ +static svn_error_t * +read_pack_revprop(packed_revprops_t **revprops, + svn_fs_t *fs, + svn_revnum_t rev, + apr_int64_t generation, + svn_boolean_t read_all, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_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 (!svn_fs_x__is_packed_revprop(fs, rev)) + SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, iterpool)); + + if (!svn_fs_x__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(result_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 < SVN_FS_X__RECOVERABLE_RETRY_COUNT && !result->packed_revprops; + ++i) + { + const char *file_path; + svn_pool_clear(iterpool); + + /* there might have been concurrent writes. + * Re-read the manifest and the pack file. + */ + SVN_ERR(get_revprop_packname(fs, result, result_pool, iterpool)); + file_path = svn_dirent_join(result->folder, + result->filename, + iterpool); + SVN_ERR(svn_fs_x__try_stringbuf_from_file(&result->packed_revprops, + &missing, + file_path, + i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT, + result_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, iterpool)) + SVN_ERR(read_revprop_generation(&result->generation, fs, 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, read_all, 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. + */ +svn_error_t * +svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t bypass_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + + /* Try cache lookup first. */ + if (!bypass_cache && has_revprop_cache(fs, scratch_pool)) + { + svn_boolean_t is_cached; + svn_fs_x__pair_cache_key_t key = { 0 }; + + SVN_ERR(read_revprop_generation(&generation, fs, scratch_pool)); + + key.revision = rev; + key.second = generation; + SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached, + ffd->revprop_cache, &key, result_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 (!svn_fs_x__is_packed_revprop(fs, rev)) + { + svn_error_t *err = read_non_packed_revprop(proplist_p, fs, rev, + generation, result_pool, + scratch_pool); + if (err) + { + if (!APR_STATUS_IS_ENOENT(err->apr_err)) + 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 (!*proplist_p) + { + packed_revprops_t *revprops; + SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, FALSE, + result_pool, scratch_pool)); + *proplist_p = 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. + * + * Allocate *FINAL_PATH and *TMP_PATH in RESULT_POOL. Use SCRATCH_POOL + * for temporary 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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *stream; + *final_path = svn_fs_x__path_revprops(fs, rev, result_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, + scratch_pool), + svn_io_file_del_none, + result_pool, scratch_pool)); + SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, + scratch_pool)); + SVN_ERR(svn_stream_close(stream)); + + 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 SCRATCH_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 *scratch_pool) +{ + apr_int64_t generation; + + /* 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(&generation, fs, scratch_pool)); + + SVN_ERR(svn_fs_x__move_into_place(tmp_path, final_path, perms_reference, + scratch_pool)); + + /* Indicate that the update (if relevant) has been completed. */ + if (bump_generation) + SVN_ERR(end_revprop_change(fs, generation, scratch_pool)); + + /* Clean up temporary files, if necessary. */ + if (files_to_delete) + { + apr_pool_t *iterpool = svn_pool_create(scratch_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_pool_clear(iterpool); + SVN_ERR(svn_io_remove_file2(path, TRUE, 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 *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + SVN_ERR_ASSERT(start < end); + + /* start revision and entry count */ + SVN_ERR(svn_stream_printf(stream, scratch_pool, "%ld\n", start_revision)); + SVN_ERR(svn_stream_printf(stream, scratch_pool, "%d\n", end - start)); + + /* the sizes array */ + for (i = start; i < end; ++i) + { + /* Non-standard pool usage. + * + * We only allocate a few bytes each iteration -- even with a + * million iterations we would still be in good shape memory-wise. + */ + 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_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Writes the a pack file to FILE_STREAM. 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. + * SCRATCH_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_pool_t *scratch_pool) +{ + svn_fs_x__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, scratch_pool); + svn_stringbuf_t *compressed + = svn_stringbuf_create_empty(scratch_pool); + stream = svn_stream_from_stringbuf(uncompressed, scratch_pool); + + /* write the header*/ + SVN_ERR(serialize_revprops_header(stream, revprops->start_revision + start, + revprops->sizes, start, end, + scratch_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(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)); + + 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 RESULT_POOL. Allocate the paths + * in *FILES_TO_DELETE from the same pool that contains the array itself. + * + * Use SCRATCH_POOL for temporary allocations. + */ +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 *result_pool, + apr_pool_t *scratch_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(result_pool, 3, sizeof(const char*)); + + APR_ARRAY_PUSH(*files_to_delete, const char*) + = svn_dirent_join(revprops->folder, old_filename, + (*files_to_delete)->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((*files_to_delete)->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, + scratch_pool), + APR_WRITE | APR_CREATE, APR_OS_DEFAULT, + result_pool)); + *stream = svn_stream_from_aprfile2(file, FALSE, result_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. + * + * Allocate output values in RESULT_POOL and temporaries from SCRATCH_POOL. + */ +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 *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + packed_revprops_t *revprops; + apr_int64_t generation = 0; + svn_stream_t *stream; + 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, scratch_pool)) + SVN_ERR(read_revprop_generation(&generation, fs, scratch_pool)); + + /* read contents of the current pack file */ + SVN_ERR(read_pack_revprop(&revprops, fs, rev, generation, TRUE, + scratch_pool, scratch_pool)); + + /* serialize the new revprops */ + serialized = svn_stringbuf_create_empty(scratch_pool); + stream = svn_stream_from_stringbuf(serialized, scratch_pool); + SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, + scratch_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, + result_pool); + SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder, + svn_io_file_del_none, result_pool, + scratch_pool)); + SVN_ERR(repack_revprops(fs, revprops, 0, revprops->sizes->nelts, + changed_index, serialized, new_total_size, + stream, scratch_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; + } + + /* Allocate this here such that we can call the repack functions with + * the scratch pool alone. */ + if (*files_to_delete == NULL) + *files_to_delete = apr_array_make(result_pool, 3, + sizeof(const char*)); + + /* write the new, split files */ + if (left_count) + { + SVN_ERR(repack_stream_open(&stream, fs, revprops, 0, + left_count, files_to_delete, + scratch_pool, scratch_pool)); + SVN_ERR(repack_revprops(fs, revprops, 0, left_count, + changed_index, serialized, new_total_size, + stream, scratch_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, + scratch_pool, scratch_pool)); + SVN_ERR(repack_revprops(fs, revprops, changed_index, + changed_index + 1, + changed_index, serialized, new_total_size, + stream, scratch_pool)); + } + + if (right_count) + { + SVN_ERR(repack_stream_open(&stream, fs, revprops, + revprops->sizes->nelts - right_count, + revprops->sizes->nelts, + files_to_delete, scratch_pool, + scratch_pool)); + SVN_ERR(repack_revprops(fs, revprops, + revprops->sizes->nelts - right_count, + revprops->sizes->nelts, changed_index, + serialized, new_total_size, stream, + scratch_pool)); + } + + /* write the new manifest */ + *final_path = svn_dirent_join(revprops->folder, PATH_MANIFEST, + result_pool); + SVN_ERR(svn_stream_open_unique(&stream, tmp_path, revprops->folder, + svn_io_file_del_none, result_pool, + scratch_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, scratch_pool, "%s\n", filename)); + } + + SVN_ERR(svn_stream_close(stream)); + } + + return SVN_NO_ERROR; +} + +/* Set the revision property list of revision REV in filesystem FS to + PROPLIST. Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__set_revision_proplist(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *proplist, + apr_pool_t *scratch_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(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + + /* this info will not change while we hold the global FS write lock */ + is_packed = svn_fs_x__is_packed_revprop(fs, rev); + + /* Test whether revprops already exist for this revision. + * Only then will we need to bump the revprop generation. + * The fact that they did not yet exist is never cached. */ + if (is_packed) + { + bump_generation = TRUE; + } + else + { + svn_node_kind_t kind; + SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, + scratch_pool), + &kind, scratch_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, scratch_pool, + scratch_pool)); + else + SVN_ERR(write_non_packed_revprop(&final_path, &tmp_path, + fs, rev, proplist, scratch_pool, + scratch_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.) + */ + perms_reference = svn_fs_x__path_rev_absolute(fs, rev, scratch_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, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Return TRUE, if for REVISION in FS, we can find the revprop pack file. + * Use SCRATCH_POOL for temporary allocations. + * Set *MISSING, if the reason is a missing manifest or pack file. + */ +svn_boolean_t +svn_fs_x__packed_revprop_available(svn_boolean_t *missing, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_stringbuf_t *content = NULL; + + /* try to read the manifest file */ + const char *folder = svn_fs_x__path_revprops_pack_shard(fs, revision, + scratch_pool); + const char *manifest_path = svn_dirent_join(folder, PATH_MANIFEST, + scratch_pool); + + svn_error_t *err = svn_fs_x__try_stringbuf_from_file(&content, + missing, + manifest_path, + FALSE, + scratch_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, + scratch_pool), + &kind, scratch_pool); + if (err) + { + svn_error_clear(err); + return FALSE; + } + + *missing = kind == svn_node_none; + return kind == svn_node_file; + } + + content->data = next; + } + + return FALSE; +} + + +/****** Packing FSX shards *********/ + +svn_error_t * +svn_fs_x__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(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; +} + +svn_error_t * +svn_fs_x__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; + /* Special special case: if max_files_per_dir is 1, then at this point + start_rev == 1 and end_rev == 0 (!). Fortunately, everything just + works. */ + + /* 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(svn_fs_x__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(svn_fs_x__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; +} + +svn_error_t * +svn_fs_x__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_pool_clear(iterpool); + + 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_destroy(iterpool); + } + else + SVN_ERR(svn_io_remove_dir2(shard_path, TRUE, + cancel_func, cancel_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + diff --git a/contrib/subversion/subversion/libsvn_fs_x/revprops.h b/contrib/subversion/subversion/libsvn_fs_x/revprops.h new file mode 100644 index 000000000..c4827c42c --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/revprops.h @@ -0,0 +1,184 @@ +/* revprops.h --- everything needed to handle revprops in FSX + * + * ==================================================================== + * 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_FS__REVPROPS_H +#define SVN_LIBSVN_FS__REVPROPS_H + +#include "svn_fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Auto-create / replace the revprop generation file in FS with its + * initial contents. In any case, FS will not hold an open handle to + * it after this function succeeds. + * + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs, + 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. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Temporary allocations are done in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_x__upgrade_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* In the filesystem FS, remove all non-packed revprop shards up to + * min_unpacked_rev. Temporary allocations are done in SCRATCH_POOL. + * + * NOTIFY_FUNC and NOTIFY_BATON as well as CANCEL_FUNC and CANCEL_BATON are + * used in the usual way. Cancellation is supported in the sense that we + * will cleanly abort the operation. However, there will be remnant shards + * that must be removed manually. + * + * See upgrade_pack_revprops for more info. + */ +svn_error_t * +svn_fs_x__upgrade_cleanup_pack_revprops(svn_fs_t *fs, + svn_fs_upgrade_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* Read the revprops for revision REV in FS and return them in *PROPLIST_P. + * If BYPASS_CACHE is set, don't consult the disks but always read from disk. + * + * Allocate the *PROPLIST_P in RESULT_POOL and use SCRATCH_POOL for temporary + * allocations. + */ +svn_error_t * +svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t bypass_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Set the revision property list of revision REV in filesystem FS to + PROPLIST. Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__set_revision_proplist(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *proplist, + apr_pool_t *scratch_pool); + + +/* Return TRUE, if for REVISION in FS, we can find the revprop pack file. + * Use SCRATCH_POOL for temporary allocations. + * Set *MISSING, if the reason is a missing manifest or pack file. + */ +svn_boolean_t +svn_fs_x__packed_revprop_available(svn_boolean_t *missing, + svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *scratch_pool); + + +/****** Packing FSX shards *********/ + +/* 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. + */ +svn_error_t * +svn_fs_x__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); + +/* 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. + */ +svn_error_t * +svn_fs_x__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); + +/* 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. + */ +svn_error_t * +svn_fs_x__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); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS__REVPROPS_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/string_table.c b/contrib/subversion/subversion/libsvn_fs_x/string_table.c new file mode 100644 index 000000000..7b3b6450c --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/string_table.c @@ -0,0 +1,904 @@ +/* string_table.c : operations on string tables + * + * ==================================================================== + * 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 +#include +#include + +#include "svn_string.h" +#include "svn_sorts.h" +#include "private/svn_dep_compat.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_packed_data.h" +#include "string_table.h" + + + +#define MAX_DATA_SIZE 0xffff +#define MAX_SHORT_STRING_LEN (MAX_DATA_SIZE / 4) +#define TABLE_SHIFT 13 +#define MAX_STRINGS_PER_TABLE (1 << (TABLE_SHIFT - 1)) +#define LONG_STRING_MASK (1 << (TABLE_SHIFT - 1)) +#define STRING_INDEX_MASK ((1 << (TABLE_SHIFT - 1)) - 1) +#define PADDING (sizeof(apr_uint64_t)) + + +typedef struct builder_string_t +{ + svn_string_t string; + int position; + apr_size_t depth; + struct builder_string_t *previous; + struct builder_string_t *next; + apr_size_t previous_match_len; + apr_size_t next_match_len; + struct builder_string_t *left; + struct builder_string_t *right; +} builder_string_t; + +typedef struct builder_table_t +{ + apr_size_t max_data_size; + builder_string_t *top; + builder_string_t *first; + builder_string_t *last; + apr_array_header_t *short_strings; + apr_array_header_t *long_strings; + apr_hash_t *long_string_dict; + apr_size_t long_string_size; +} builder_table_t; + +struct string_table_builder_t +{ + apr_pool_t *pool; + apr_array_header_t *tables; +}; + +typedef struct string_header_t +{ + apr_uint16_t head_string; + apr_uint16_t head_length; + apr_uint16_t tail_start; + apr_uint16_t tail_length; +} string_header_t; + +typedef struct string_sub_table_t +{ + const char *data; + apr_size_t data_size; + + string_header_t *short_strings; + apr_size_t short_string_count; + + svn_string_t *long_strings; + apr_size_t long_string_count; +} string_sub_table_t; + +struct string_table_t +{ + apr_size_t size; + string_sub_table_t *sub_tables; +}; + + +/* Accessing ID Pieces. */ + +static builder_table_t * +add_table(string_table_builder_t *builder) +{ + builder_table_t *table = apr_pcalloc(builder->pool, sizeof(*table)); + table->max_data_size = MAX_DATA_SIZE - PADDING; /* ensure there remain a few + unused bytes at the end */ + table->short_strings = apr_array_make(builder->pool, 64, + sizeof(builder_string_t *)); + table->long_strings = apr_array_make(builder->pool, 0, + sizeof(svn_string_t)); + table->long_string_dict = svn_hash__make(builder->pool); + + APR_ARRAY_PUSH(builder->tables, builder_table_t *) = table; + + return table; +} + +string_table_builder_t * +svn_fs_x__string_table_builder_create(apr_pool_t *result_pool) +{ + string_table_builder_t *result = apr_palloc(result_pool, sizeof(*result)); + result->pool = result_pool; + result->tables = apr_array_make(result_pool, 1, sizeof(builder_table_t *)); + + add_table(result); + + return result; +} + +static void +balance(builder_table_t *table, + builder_string_t **parent, + builder_string_t *node) +{ + apr_size_t left_height = node->left ? node->left->depth + 1 : 0; + apr_size_t right_height = node->right ? node->right->depth + 1 : 0; + + if (left_height > right_height + 1) + { + builder_string_t *temp = node->left->right; + node->left->right = node; + *parent = node->left; + node->left = temp; + + --left_height; + } + else if (left_height + 1 < right_height) + { + builder_string_t *temp = node->right->left; + *parent = node->right; + node->right->left = node; + node->right = temp; + + --right_height; + } + + node->depth = MAX(left_height, right_height); +} + +static apr_uint16_t +match_length(const svn_string_t *lhs, + const svn_string_t *rhs) +{ + apr_size_t len = MIN(lhs->len, rhs->len); + return (apr_uint16_t)svn_cstring__match_length(lhs->data, rhs->data, len); +} + +static apr_uint16_t +insert_string(builder_table_t *table, + builder_string_t **parent, + builder_string_t *to_insert) +{ + apr_uint16_t result; + builder_string_t *current = *parent; + int diff = strcmp(current->string.data, to_insert->string.data); + if (diff == 0) + { + apr_array_pop(table->short_strings); + return current->position; + } + + if (diff < 0) + { + if (current->left == NULL) + { + current->left = to_insert; + + to_insert->previous = current->previous; + to_insert->next = current; + + if (to_insert->previous == NULL) + { + table->first = to_insert; + } + else + { + builder_string_t *previous = to_insert->previous; + to_insert->previous_match_len + = match_length(&previous->string, &to_insert->string); + + previous->next = to_insert; + previous->next_match_len = to_insert->previous_match_len; + } + + current->previous = to_insert; + to_insert->next_match_len + = match_length(¤t->string, &to_insert->string); + current->previous_match_len = to_insert->next_match_len; + + table->max_data_size -= to_insert->string.len; + if (to_insert->previous == NULL) + table->max_data_size += to_insert->next_match_len; + else + table->max_data_size += MIN(to_insert->previous_match_len, + to_insert->next_match_len); + + return to_insert->position; + } + else + result = insert_string(table, ¤t->left, to_insert); + } + else + { + if (current->right == NULL) + { + current->right = to_insert; + + to_insert->next = current->next; + to_insert->previous = current; + + if (to_insert->next == NULL) + { + table->last = to_insert; + } + else + { + builder_string_t *next = to_insert->next; + to_insert->next_match_len + = match_length(&next->string, &to_insert->string); + + next->previous = to_insert; + next->previous_match_len = to_insert->next_match_len; + } + + current->next = current->right; + to_insert->previous_match_len + = match_length(¤t->string, &to_insert->string); + current->next_match_len = to_insert->previous_match_len; + + table->max_data_size -= to_insert->string.len; + if (to_insert->next == NULL) + table->max_data_size += to_insert->previous_match_len; + else + table->max_data_size += MIN(to_insert->previous_match_len, + to_insert->next_match_len); + + return to_insert->position; + } + else + result = insert_string(table, ¤t->right, to_insert); + } + + balance(table, parent, current); + return result; +} + +apr_size_t +svn_fs_x__string_table_builder_add(string_table_builder_t *builder, + const char *string, + apr_size_t len) +{ + apr_size_t result; + builder_table_t *table = APR_ARRAY_IDX(builder->tables, + builder->tables->nelts - 1, + builder_table_t *); + if (len == 0) + len = strlen(string); + + string = apr_pstrmemdup(builder->pool, string, len); + if (len > MAX_SHORT_STRING_LEN) + { + void *idx_void; + svn_string_t item; + item.data = string; + item.len = len; + + idx_void = apr_hash_get(table->long_string_dict, string, len); + result = (apr_uintptr_t)idx_void; + if (result) + return result - 1 + + LONG_STRING_MASK + + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT); + + if (table->long_strings->nelts == MAX_STRINGS_PER_TABLE) + table = add_table(builder); + + result = table->long_strings->nelts + + LONG_STRING_MASK + + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT); + APR_ARRAY_PUSH(table->long_strings, svn_string_t) = item; + apr_hash_set(table->long_string_dict, string, len, + (void*)(apr_uintptr_t)table->long_strings->nelts); + + table->long_string_size += len; + } + else + { + builder_string_t *item = apr_pcalloc(builder->pool, sizeof(*item)); + item->string.data = string; + item->string.len = len; + item->previous_match_len = 0; + item->next_match_len = 0; + + if ( table->short_strings->nelts == MAX_STRINGS_PER_TABLE + || table->max_data_size < len) + table = add_table(builder); + + item->position = table->short_strings->nelts; + APR_ARRAY_PUSH(table->short_strings, builder_string_t *) = item; + + if (table->top == NULL) + { + table->max_data_size -= len; + table->top = item; + table->first = item; + table->last = item; + + result = ((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT; + } + else + { + result = insert_string(table, &table->top, item) + + (((apr_size_t)builder->tables->nelts - 1) << TABLE_SHIFT); + } + } + + return result; +} + +apr_size_t +svn_fs_x__string_table_builder_estimate_size(string_table_builder_t *builder) +{ + apr_size_t total = 0; + int i; + + for (i = 0; i < builder->tables->nelts; ++i) + { + builder_table_t *table + = APR_ARRAY_IDX(builder->tables, i, builder_table_t*); + + /* total number of chars to store, + * 8 bytes per short string table entry + * 4 bytes per long string table entry + * some static overhead */ + apr_size_t table_size + = MAX_DATA_SIZE - table->max_data_size + + table->long_string_size + + table->short_strings->nelts * 8 + + table->long_strings->nelts * 4 + + 10; + + total += table_size; + } + + /* ZIP compression should give us a 50% reduction. + * add some static overhead */ + return 200 + total / 2; + +} + +static void +create_table(string_sub_table_t *target, + builder_table_t *source, + apr_pool_t *pool, + apr_pool_t *scratch_pool) +{ + int i = 0; + apr_hash_t *tails = svn_hash__make(scratch_pool); + svn_stringbuf_t *data + = svn_stringbuf_create_ensure(MAX_DATA_SIZE - source->max_data_size, + scratch_pool); + + /* pack sub-strings */ + target->short_string_count = (apr_size_t)source->short_strings->nelts; + target->short_strings = apr_palloc(pool, sizeof(*target->short_strings) * + target->short_string_count); + for (i = 0; i < source->short_strings->nelts; ++i) + { + const builder_string_t *string + = APR_ARRAY_IDX(source->short_strings, i, const builder_string_t *); + + string_header_t *entry = &target->short_strings[i]; + const char *tail = string->string.data + string->previous_match_len; + string_header_t *tail_match; + apr_size_t head_length = string->previous_match_len; + + /* Minimize the number of strings to visit when reconstructing the + string head. So, skip all predecessors that don't contribute to + first HEAD_LENGTH chars of our string. */ + if (head_length) + { + const builder_string_t *furthest_prev = string->previous; + while (furthest_prev->previous_match_len >= head_length) + furthest_prev = furthest_prev->previous; + entry->head_string = furthest_prev->position; + } + else + entry->head_string = 0; + + /* head & tail length are known */ + entry->head_length = (apr_uint16_t)head_length; + entry->tail_length + = (apr_uint16_t)(string->string.len - entry->head_length); + + /* try to reuse an existing tail segment */ + tail_match = apr_hash_get(tails, tail, entry->tail_length); + if (tail_match) + { + entry->tail_start = tail_match->tail_start; + } + else + { + entry->tail_start = (apr_uint16_t)data->len; + svn_stringbuf_appendbytes(data, tail, entry->tail_length); + apr_hash_set(tails, tail, entry->tail_length, entry); + } + } + + /* pack long strings */ + target->long_string_count = (apr_size_t)source->long_strings->nelts; + target->long_strings = apr_palloc(pool, sizeof(*target->long_strings) * + target->long_string_count); + for (i = 0; i < source->long_strings->nelts; ++i) + { + svn_string_t *string = &target->long_strings[i]; + *string = APR_ARRAY_IDX(source->long_strings, i, svn_string_t); + string->data = apr_pstrmemdup(pool, string->data, string->len); + } + + data->len += PADDING; /* add a few extra bytes at the end of the buffer + that we want to keep valid for chunky access */ + assert(data->len < data->blocksize); + memset(data->data + data->len - PADDING, 0, PADDING); + + target->data = apr_pmemdup(pool, data->data, data->len); + target->data_size = data->len; +} + +string_table_t * +svn_fs_x__string_table_create(const string_table_builder_t *builder, + apr_pool_t *pool) +{ + apr_size_t i; + + string_table_t *result = apr_pcalloc(pool, sizeof(*result)); + result->size = (apr_size_t)builder->tables->nelts; + result->sub_tables + = apr_pcalloc(pool, result->size * sizeof(*result->sub_tables)); + + for (i = 0; i < result->size; ++i) + create_table(&result->sub_tables[i], + APR_ARRAY_IDX(builder->tables, i, builder_table_t*), + pool, + builder->pool); + + return result; +} + +/* Masks used by table_copy_string. copy_mask[I] is used if the target + content to be preserved starts at byte I within the current chunk. + This is used to work around alignment issues. + */ +#if SVN_UNALIGNED_ACCESS_IS_OK +static const char *copy_masks[8] = { "\xff\xff\xff\xff\xff\xff\xff\xff", + "\x00\xff\xff\xff\xff\xff\xff\xff", + "\x00\x00\xff\xff\xff\xff\xff\xff", + "\x00\x00\x00\xff\xff\xff\xff\xff", + "\x00\x00\x00\x00\xff\xff\xff\xff", + "\x00\x00\x00\x00\x00\xff\xff\xff", + "\x00\x00\x00\x00\x00\x00\xff\xff", + "\x00\x00\x00\x00\x00\x00\x00\xff" }; +#endif + +static void +table_copy_string(char *buffer, + apr_size_t len, + const string_sub_table_t *table, + string_header_t *header) +{ + buffer[len] = '\0'; + do + { + assert(header->head_length <= len); + { +#if SVN_UNALIGNED_ACCESS_IS_OK + /* the sections that we copy tend to be short but we can copy + *all* of it chunky because we made sure that source and target + buffer have some extra padding to prevent segfaults. */ + apr_uint64_t mask; + apr_size_t to_copy = len - header->head_length; + apr_size_t copied = 0; + + const char *source = table->data + header->tail_start; + char *target = buffer + header->head_length; + len = header->head_length; + + /* copy whole chunks */ + while (to_copy >= copied + sizeof(apr_uint64_t)) + { + *(apr_uint64_t *)(target + copied) + = *(const apr_uint64_t *)(source + copied); + copied += sizeof(apr_uint64_t); + } + + /* copy the remainder assuming that we have up to 8 extra bytes + of addressable buffer on the source and target sides. + Now, we simply copy 8 bytes and use a mask to filter & merge + old with new data. */ + mask = *(const apr_uint64_t *)copy_masks[to_copy - copied]; + *(apr_uint64_t *)(target + copied) + = (*(apr_uint64_t *)(target + copied) & mask) + | (*(const apr_uint64_t *)(source + copied) & ~mask); +#else + memcpy(buffer + header->head_length, + table->data + header->tail_start, + len - header->head_length); + len = header->head_length; +#endif + } + + header = &table->short_strings[header->head_string]; + } + while (len); +} + +const char* +svn_fs_x__string_table_get(const string_table_t *table, + apr_size_t idx, + apr_size_t *length, + apr_pool_t *pool) +{ + apr_size_t table_number = idx >> TABLE_SHIFT; + apr_size_t sub_index = idx & STRING_INDEX_MASK; + + if (table_number < table->size) + { + string_sub_table_t *sub_table = &table->sub_tables[table_number]; + if (idx & LONG_STRING_MASK) + { + if (sub_index < sub_table->long_string_count) + { + if (length) + *length = sub_table->long_strings[sub_index].len; + + return apr_pstrmemdup(pool, + sub_table->long_strings[sub_index].data, + sub_table->long_strings[sub_index].len); + } + } + else + { + if (sub_index < sub_table->short_string_count) + { + string_header_t *header = sub_table->short_strings + sub_index; + apr_size_t len = header->head_length + header->tail_length; + char *result = apr_palloc(pool, len + PADDING); + + if (length) + *length = len; + table_copy_string(result, len, sub_table, header); + + return result; + } + } + } + + return apr_pstrmemdup(pool, "", 0); +} + +svn_error_t * +svn_fs_x__write_string_table(svn_stream_t *stream, + const string_table_t *table, + apr_pool_t *scratch_pool) +{ + apr_size_t i, k; + + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + svn_packed__int_stream_t *table_sizes + = svn_packed__create_int_stream(root, FALSE, FALSE); + svn_packed__int_stream_t *small_strings_headers + = svn_packed__create_int_stream(root, FALSE, FALSE); + svn_packed__byte_stream_t *large_strings + = svn_packed__create_bytes_stream(root); + svn_packed__byte_stream_t *small_strings_data + = svn_packed__create_bytes_stream(root); + + svn_packed__create_int_substream(small_strings_headers, TRUE, FALSE); + svn_packed__create_int_substream(small_strings_headers, FALSE, FALSE); + svn_packed__create_int_substream(small_strings_headers, TRUE, FALSE); + svn_packed__create_int_substream(small_strings_headers, FALSE, FALSE); + + /* number of sub-tables */ + + svn_packed__add_uint(table_sizes, table->size); + + /* all short-string char data sizes */ + + for (i = 0; i < table->size; ++i) + svn_packed__add_uint(table_sizes, + table->sub_tables[i].short_string_count); + + for (i = 0; i < table->size; ++i) + svn_packed__add_uint(table_sizes, + table->sub_tables[i].long_string_count); + + /* all strings */ + + for (i = 0; i < table->size; ++i) + { + string_sub_table_t *sub_table = &table->sub_tables[i]; + svn_packed__add_bytes(small_strings_data, + sub_table->data, + sub_table->data_size); + + for (k = 0; k < sub_table->short_string_count; ++k) + { + string_header_t *string = &sub_table->short_strings[k]; + + svn_packed__add_uint(small_strings_headers, string->head_string); + svn_packed__add_uint(small_strings_headers, string->head_length); + svn_packed__add_uint(small_strings_headers, string->tail_start); + svn_packed__add_uint(small_strings_headers, string->tail_length); + } + + for (k = 0; k < sub_table->long_string_count; ++k) + svn_packed__add_bytes(large_strings, + sub_table->long_strings[k].data, + sub_table->long_strings[k].len + 1); + } + + /* write to target stream */ + + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_string_table(string_table_t **table_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i, k; + + string_table_t *table = apr_palloc(result_pool, sizeof(*table)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *table_sizes; + svn_packed__byte_stream_t *large_strings; + svn_packed__byte_stream_t *small_strings_data; + svn_packed__int_stream_t *headers; + + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + table_sizes = svn_packed__first_int_stream(root); + headers = svn_packed__next_int_stream(table_sizes); + large_strings = svn_packed__first_byte_stream(root); + small_strings_data = svn_packed__next_byte_stream(large_strings); + + /* create sub-tables */ + + table->size = (apr_size_t)svn_packed__get_uint(table_sizes); + table->sub_tables = apr_pcalloc(result_pool, + table->size * sizeof(*table->sub_tables)); + + /* read short strings */ + + for (i = 0; i < table->size; ++i) + { + string_sub_table_t *sub_table = &table->sub_tables[i]; + + sub_table->short_string_count + = (apr_size_t)svn_packed__get_uint(table_sizes); + if (sub_table->short_string_count) + { + sub_table->short_strings + = apr_pcalloc(result_pool, sub_table->short_string_count + * sizeof(*sub_table->short_strings)); + + /* read short string headers */ + + for (k = 0; k < sub_table->short_string_count; ++k) + { + string_header_t *string = &sub_table->short_strings[k]; + + string->head_string = (apr_uint16_t)svn_packed__get_uint(headers); + string->head_length = (apr_uint16_t)svn_packed__get_uint(headers); + string->tail_start = (apr_uint16_t)svn_packed__get_uint(headers); + string->tail_length = (apr_uint16_t)svn_packed__get_uint(headers); + } + } + + sub_table->data = svn_packed__get_bytes(small_strings_data, + &sub_table->data_size); + } + + /* read long strings */ + + for (i = 0; i < table->size; ++i) + { + /* initialize long string table */ + string_sub_table_t *sub_table = &table->sub_tables[i]; + + sub_table->long_string_count = svn_packed__get_uint(table_sizes); + if (sub_table->long_string_count) + { + sub_table->long_strings + = apr_pcalloc(result_pool, sub_table->long_string_count + * sizeof(*sub_table->long_strings)); + + /* read long strings */ + + for (k = 0; k < sub_table->long_string_count; ++k) + { + svn_string_t *string = &sub_table->long_strings[k]; + string->data = svn_packed__get_bytes(large_strings, + &string->len); + string->len--; + } + } + } + + /* done */ + + *table_p = table; + + return SVN_NO_ERROR; +} + +void +svn_fs_x__serialize_string_table(svn_temp_serializer__context_t *context, + string_table_t **st) +{ + apr_size_t i, k; + string_table_t *string_table = *st; + if (string_table == NULL) + return; + + /* string table struct */ + svn_temp_serializer__push(context, + (const void * const *)st, + sizeof(*string_table)); + + /* sub-table array (all structs in a single memory block) */ + svn_temp_serializer__push(context, + (const void * const *)&string_table->sub_tables, + sizeof(*string_table->sub_tables) * + string_table->size); + + /* sub-elements of all sub-tables */ + for (i = 0; i < string_table->size; ++i) + { + string_sub_table_t *sub_table = &string_table->sub_tables[i]; + svn_temp_serializer__add_leaf(context, + (const void * const *)&sub_table->data, + sub_table->data_size); + svn_temp_serializer__add_leaf(context, + (const void * const *)&sub_table->short_strings, + sub_table->short_string_count * sizeof(string_header_t)); + + /* all "long string" instances form a single memory block */ + svn_temp_serializer__push(context, + (const void * const *)&sub_table->long_strings, + sub_table->long_string_count * sizeof(svn_string_t)); + + /* serialize actual long string contents */ + for (k = 0; k < sub_table->long_string_count; ++k) + { + svn_string_t *string = &sub_table->long_strings[k]; + svn_temp_serializer__add_leaf(context, + (const void * const *)&string->data, + string->len + 1); + } + + svn_temp_serializer__pop(context); + } + + /* back to the caller's nesting level */ + svn_temp_serializer__pop(context); + svn_temp_serializer__pop(context); +} + +void +svn_fs_x__deserialize_string_table(void *buffer, + string_table_t **table) +{ + apr_size_t i, k; + string_sub_table_t *sub_tables; + + svn_temp_deserializer__resolve(buffer, (void **)table); + if (*table == NULL) + return; + + svn_temp_deserializer__resolve(*table, (void **)&(*table)->sub_tables); + sub_tables = (*table)->sub_tables; + for (i = 0; i < (*table)->size; ++i) + { + string_sub_table_t *sub_table = sub_tables + i; + + svn_temp_deserializer__resolve(sub_tables, + (void **)&sub_table->data); + svn_temp_deserializer__resolve(sub_tables, + (void **)&sub_table->short_strings); + svn_temp_deserializer__resolve(sub_tables, + (void **)&sub_table->long_strings); + + for (k = 0; k < sub_table->long_string_count; ++k) + svn_temp_deserializer__resolve(sub_table->long_strings, + (void **)&sub_table->long_strings[k].data); + } +} + +const char* +svn_fs_x__string_table_get_func(const string_table_t *table, + apr_size_t idx, + apr_size_t *length, + apr_pool_t *pool) +{ + apr_size_t table_number = idx >> TABLE_SHIFT; + apr_size_t sub_index = idx & STRING_INDEX_MASK; + + if (table_number < table->size) + { + /* resolve TABLE->SUB_TABLES pointer and select sub-table */ + string_sub_table_t *sub_tables + = (string_sub_table_t *)svn_temp_deserializer__ptr(table, + (const void *const *)&table->sub_tables); + string_sub_table_t *sub_table = sub_tables + table_number; + + /* pick the right kind of string */ + if (idx & LONG_STRING_MASK) + { + if (sub_index < sub_table->long_string_count) + { + /* resolve SUB_TABLE->LONG_STRINGS, select the string we want + and resolve the pointer to its char data */ + svn_string_t *long_strings + = (svn_string_t *)svn_temp_deserializer__ptr(sub_table, + (const void *const *)&sub_table->long_strings); + const char *str_data + = (const char*)svn_temp_deserializer__ptr(long_strings, + (const void *const *)&long_strings[sub_index].data); + + /* return a copy of the char data */ + if (length) + *length = long_strings[sub_index].len; + + return apr_pstrmemdup(pool, + str_data, + long_strings[sub_index].len); + } + } + else + { + if (sub_index < sub_table->short_string_count) + { + string_header_t *header; + apr_size_t len; + char *result; + + /* construct a copy of our sub-table struct with SHORT_STRINGS + and DATA pointers resolved. Leave all other pointers as + they are. This allows us to use the same code for string + reconstruction here as in the non-serialized case. */ + string_sub_table_t table_copy = *sub_table; + table_copy.data + = (const char *)svn_temp_deserializer__ptr(sub_tables, + (const void *const *)&sub_table->data); + table_copy.short_strings + = (string_header_t *)svn_temp_deserializer__ptr(sub_tables, + (const void *const *)&sub_table->short_strings); + + /* reconstruct the char data and return it */ + header = table_copy.short_strings + sub_index; + len = header->head_length + header->tail_length; + result = apr_palloc(pool, len + PADDING); + if (length) + *length = len; + + table_copy_string(result, len, &table_copy, header); + + return result; + } + } + } + + return ""; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/string_table.h b/contrib/subversion/subversion/libsvn_fs_x/string_table.h new file mode 100644 index 000000000..f7ab47698 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/string_table.h @@ -0,0 +1,133 @@ +/* string_table.h : interface to string tables, private to libsvn_fs_x + * + * ==================================================================== + * 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_FS_X_STRING_TABLE_H +#define SVN_LIBSVN_FS_X_STRING_TABLE_H + +#include "svn_io.h" +#include "private/svn_temp_serializer.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* A string table is a very space efficient, read-only representation for + * a set of strings with high degreed of prefix and postfix overhead. + * + * Creating a string table is a two-stage process: Use a builder class, + * stuff all the strings in there and let it then do the heavy lifting of + * classification and compression to create the actual string table object. + * + * We will use this for the various path values in FSX change lists and + * node revision items. + */ + +/* the string table builder */ +typedef struct string_table_builder_t string_table_builder_t; + +/* the string table */ +typedef struct string_table_t string_table_t; + +/* Returns a new string table builder object, allocated in RESULT_POOL. + */ +string_table_builder_t * +svn_fs_x__string_table_builder_create(apr_pool_t *result_pool); + +/* Add an arbitrary NUL-terminated C-string STRING of the given length LEN + * to BUILDER. Return the index of that string in the future string table. + * If LEN is 0, determine the length of the C-string internally. + */ +apr_size_t +svn_fs_x__string_table_builder_add(string_table_builder_t *builder, + const char *string, + apr_size_t len); + +/* Return an estimate for the on-disk size of the resulting string table. + * The estimate may err in both directions but tends to overestimate the + * space requirements for larger tables. + */ +apr_size_t +svn_fs_x__string_table_builder_estimate_size(string_table_builder_t *builder); + +/* From the given BUILDER object, create a string table object allocated + * in POOL that contains all strings previously added to BUILDER. + */ +string_table_t * +svn_fs_x__string_table_create(const string_table_builder_t *builder, + apr_pool_t *pool); + +/* Extract string number INDEX from TABLE and return a copy of it allocated + * in POOL. If LENGTH is not NULL, set *LENGTH to strlen() of the result + * string. Returns an empty string for invalid indexes. + */ +const char* +svn_fs_x__string_table_get(const string_table_t *table, + apr_size_t index, + apr_size_t *length, + apr_pool_t *pool); + +/* Write a serialized representation of the string table TABLE to STREAM. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__write_string_table(svn_stream_t *stream, + const string_table_t *table, + apr_pool_t *scratch_pool); + +/* Read the serialized string table representation from STREAM and return + * the resulting runtime representation in *TABLE_P. Allocate it in + * RESULT_POOL and use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__read_string_table(string_table_t **table_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Serialize string table *ST within the serialization CONTEXT. + */ +void +svn_fs_x__serialize_string_table(svn_temp_serializer__context_t *context, + string_table_t **st); + +/* Deserialize string table *TABLE within the BUFFER. + */ +void +svn_fs_x__deserialize_string_table(void *buffer, + string_table_t **table); + +/* Extract string number INDEX from the cache serialized representation at + * TABLE and return a copy of it allocated in POOL. If LENGTH is not NULL, + * set *LENGTH to strlen() of the result string. Returns an empty string + * for invalid indexes. + */ +const char* +svn_fs_x__string_table_get_func(const string_table_t *table, + apr_size_t idx, + apr_size_t *length, + apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_X_STRING_TABLE_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/structure b/contrib/subversion/subversion/libsvn_fs_x/structure new file mode 100644 index 000000000..8d10c3d0a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/structure @@ -0,0 +1,336 @@ +This file will describe the design, layouts, and file formats of a +libsvn_fs_x repository. + +Since FSX is still in a very early phase of its development, all sections +either subject to major change or simply "TBD". + +Design +------ + +TBD. + +Similar to FSFS format 7 but using a radically different on-disk format. + +In FSFS, each committed revision is represented as an immutable file +containing the new node-revisions, contents, and changed-path +information for the revision, plus a second, changeable file +containing the revision properties. + +To reduce the size of the on-disk representation, revision data gets +packed, i.e. multiple revision files get combined into a single pack +file of smaller total size. The same strategy is applied to revprops. + +In-progress transactions are represented with a prototype rev file +containing only the new text representations of files (appended to as +changed file contents come in), along with a separate file for each +node-revision, directory representation, or property representation +which has been changed or added in the transaction. During the final +stage of the commit, these separate files are marshalled onto the end +of the prototype rev file to form the immutable revision file. + +Layout of the FS directory +-------------------------- + +The layout of the FS directory (the "db" subdirectory of the +repository) is: + + revs/ Subdirectory containing revs + / Shard directory, if sharding is in use (see below) + File containing rev + .pack/ Pack directory, if the repo has been packed (see below) + pack Pack file, if the repository has been packed (see below) + manifest Pack manifest file, if a pack file exists (see below) + revprops/ Subdirectory containing rev-props + / Shard directory, if sharding is in use (see below) + File containing rev-props for + .pack/ Pack directory, if the repo has been packed (see below) + . Pack file, if the repository has been packed (see below) + manifest Pack manifest file, if a pack file exists (see below) + revprops.db SQLite database of the packed revision properties + transactions/ Subdirectory containing transactions + .txn/ Directory containing transaction + txn-protorevs/ Subdirectory containing transaction proto-revision files + .rev Proto-revision file for transaction + .rev-lock Write lock for proto-rev file + txn-current File containing the next transaction key + locks/ Subdirectory containing locks + / Subdirectory named for first 3 letters of an MD5 digest + File containing locks/children for path with + current File specifying current revision and next node/copy id + fs-type File identifying this filesystem as an FSFS filesystem + write-lock Empty file, locked to serialise writers + pack-lock Empty file, locked to serialise 'svnadmin pack' (f. 7+) + txn-current-lock Empty file, locked to serialise 'txn-current' + uuid File containing the UUID of the repository + format File containing the format number of this filesystem + fsx.conf Configuration file + min-unpacked-rev File containing the oldest revision not in a pack file + min-unpacked-revprop File containing the oldest revision of unpacked revprop + rep-cache.db SQLite database mapping rep checksums to locations + +Files in the revprops directory are in the hash dump format used by +svn_hash_write. + +The format of the "current" file is a single line of the form +"\n" giving the youngest revision for the +repository. + +The "write-lock" file is an empty file which is locked before the +final stage of a commit and unlocked after the new "current" file has +been moved into place to indicate that a new revision is present. It +is also locked during a revprop propchange while the revprop file is +read in, mutated, and written out again. Furthermore, it will be used +to serialize the repository structure changes during 'svnadmin pack' +(see also next section). Note that readers are never blocked by any +operation - writers must ensure that the filesystem is always in a +consistent state. + +The "pack-lock" file is an empty file which is locked before an 'svnadmin +pack' operation commences. Thus, only one process may attempt to modify +the repository structure at a time while other processes may still read +and write (commit) to the repository during most of the pack procedure. +It is only available with format 7 and newer repositories. Older formats +use the global write-lock instead which disables commits completely +for the duration of the pack process. + +The "txn-current" file is a file with a single line of text that +contains only a base-36 number. The current value will be used in the +next transaction name, along with the revision number the transaction +is based on. This sequence number ensures that transaction names are +not reused, even if the transaction is aborted and a new transaction +based on the same revision is begun. The only operation that FSFS +performs on this file is "get and increment"; the "txn-current-lock" +file is locked during this operation. + +"fsx.conf" is a configuration file in the standard Subversion/Python +config format. It is automatically generated when you create a new +repository; read the generated file for details on what it controls. + +When representation sharing is enabled, the filesystem tracks +representation checksum and location mappings using a SQLite database in +"rep-cache.db". The database has a single table, which stores the sha1 +hash text as the primary key, mapped to the representation revision, offset, +size and expanded size. This file is only consulted during writes and never +during reads. Consequently, it is not required, and may be removed at an +abritrary time, with the subsequent loss of rep-sharing capabilities for +revisions written thereafter. + +Filesystem formats +------------------ + +TBD. + +The "format" file defines what features are permitted within the +filesystem, and indicates changes that are not backward-compatible. +It serves the same purpose as the repository file of the same name. + +So far, there is only format 1. + + +Node-revision IDs +----------------- + +A node-rev ID consists of the following three fields: + + node_revision_id ::= node_id '.' copy_id '.' txn_id + +At this level, the form of the ID is the same as for BDB - see the +section called "ID's" in <../libsvn_fs_base/notes/structure>. + +In order to support efficient lookup of node-revisions by their IDs +and to simplify the allocation of fresh node-IDs during a transaction, +we treat the fields of a node-rev ID in new and interesting ways. + +Within a new transaction: + + New node-revision IDs assigned within a transaction have a txn-id + field of the form "t". + + When a new node-id or copy-id is assigned in a transaction, the ID + used is a "_" followed by a base36 number unique to the transaction. + +Within a revision: + + Within a revision file, node-revs have a txn-id field of the form + "r/", to support easy lookup. The is the (ASCII + decimal) number of bytes from the start of the revision file to the + start of the node-rev. + + During the final phase of a commit, node-revision IDs are rewritten + to have repository-wide unique node-ID and copy-ID fields, and to have + "r/" txn-id fields. + + This uniqueness is done by changing a temporary + id of "_" to "-". Note that this means that the + originating revision of a line of history or a copy can be determined + by looking at the node ID. + +The temporary assignment of node-ID and copy-ID fields has +implications for svn_fs_compare_ids and svn_fs_check_related. The ID +_1.0.t1 is not related to the ID _1.0.t2 even though they have the +same node-ID, because temporary node-IDs are restricted in scope to +the transactions they belong to. + +Copy-IDs and copy roots +----------------------- + +Copy-IDs are assigned in the same manner as they are in the BDB +implementation: + + * A node-rev resulting from a creation operation (with no copy + history) receives the copy-ID of its parent directory. + + * A node-rev resulting from a copy operation receives a fresh + copy-ID, as one would expect. + + * A node-rev resulting from a modification operation receives a + copy-ID depending on whether its predecessor derives from a + copy operation or whether it derives from a creation operation + with no intervening copies: + + - If the predecessor does not derive from a copy, the new + node-rev receives the copy-ID of its parent directory. If the + node-rev is being modified through its created-path, this will + be the same copy-ID as the predecessor node-rev has; however, + if the node-rev is being modified through a copied ancestor + directory (i.e. we are performing a "lazy copy"), this will be + a different copy-ID. + + - If the predecessor derives from a copy and the node-rev is + being modified through its created-path, the new node-rev + receives the copy-ID of the predecessor. + + - If the predecessor derives from a copy and the node-rev is not + being modified through its created path, the new node-rev + receives a fresh copy-ID. This is called a "soft copy" + operation, as distinct from a "true copy" operation which was + actually requested through the svn_fs interface. Soft copies + exist to ensure that the same pair is not + used twice within a transaction. + +Unlike the BDB implementation, we do not have a "copies" table. +Instead, each node-revision record contains a "copyroot" field +identifying the node-rev resulting from the true copy operation most +proximal to the node-rev. If the node-rev does not itself derive from +a copy operation, then the copyroot field identifies the copy of an +ancestor directory; if no ancestor directories derive from a copy +operation, then the copyroot field identifies the root directory of +rev 0. + +Revision file format +-------------------- + +TBD + +A revision file contains a concatenation of various kinds of data: + + * Text and property representations + * Node-revisions + * The changed-path data + +That data is aggregated in compressed containers with a binary on-disk +representation. + +Transaction layout +------------------ + +A transaction directory has the following layout: + + props Transaction props + props-final Final transaction props (optional) + next-ids Next temporary node-ID and copy-ID + changes Changed-path information so far + node.. New node-rev data for node + node...props Props for new node-rev, if changed + node...children Directory contents for node-rev + Text representation of that sha1 + + txn-protorevs/rev Prototype rev file with new text reps + txn-protorevs/rev-lock Lockfile for writing to the above + +The prototype rev file is used to store the text representations as +they are received from the client. To ensure that only one client is +writing to the file at a given time, the "rev-lock" file is locked for +the duration of each write. + +The three kinds of props files are all in hash dump format. The "props" +file will always be present. The "node...props" file will +only be present if the node-rev properties have been changed. The +"props-final" only exists while converting the transaction into a revision. + +The files' content is that of text rep references: +" " +They will be written for text reps in the current transaction and be +used to eliminate duplicate reps within that transaction. + +The "next-ids" file contains a single line " +\n" giving the next temporary node-ID and copy-ID +assignments (without the leading underscores). The next node-ID is +also used as a uniquifier for representations which may share the same +underlying rep. + +The "children" file for a node-revision begins with a copy of the hash +dump representation of the directory entries from the old node-rev (or +a dump of the empty hash for new directories), and then an incremental +hash dump entry for each change made to the directory. + +The "changes" file contains changed-path entries in the same form as +the changed-path entries in a rev file, except that and +may both be "reset" (in which case and are both +always "false") to indicate that all changes to a path should be +considered undone. Reset entries are only used during the final merge +phase of a transaction. Actions in the "changes" file always contain +a node kind. + +The node-rev files have the same format as node-revs in a revision +file, except that the "text" and "props" fields are augmented as +follows: + + * The "props" field may have the value "-1" if properties have + been changed and are contained in a "props" file within the + node-rev subdirectory. + + * For directory node-revs, the "text" field may have the value + "-1" if entries have been changed and are contained in a + "contents" file in the node-rev subdirectory. + + * For the directory node-rev representing the root of the + transaction, the "is-fresh-txn-root" field indicates that it has + not been made mutable yet (see Issue #2608). + + * For file node-revs, the "text" field may have the value "-1 + " if the text representation is + within the prototype rev file. + + * The "copyroot" field may have the value "-1 " if the + copy root of the node-rev is part of the transaction in process. + + +Locks layout +------------ + +Locks in FSX are stored in serialized hash format in files whose +names are MD5 digests of the FS path which the lock is associated +with. For the purposes of keeping directory inode usage down, these +digest files live in subdirectories of the main lock directory whose +names are the first 3 characters of the digest filename. + +Also stored in the digest file for a given FS path are pointers to +other digest files which contain information associated with other FS +paths that are beneath our path (an immediate child thereof, or a +grandchild, or a great-grandchild, ...). + +To answer the question, "Does path FOO have a lock associated with +it?", one need only generate the MD5 digest of FOO's +absolute-in-the-FS path (say, 3b1b011fed614a263986b5c4869604e8), look +for a file located like so: + + /path/to/repos/locks/3b1/3b1b011fed614a263986b5c4869604e8 + +And then see if that file contains lock information. + +To inquire about locks on children of the path FOO, you would +reference the same path as above, but look for a list of children in +that file (instead of lock information). Children are listed as MD5 +digests, too, so you would simply iterate over those digests and +consult the files they reference for lock information. diff --git a/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.c b/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.c new file mode 100644 index 000000000..65a2c3fde --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.c @@ -0,0 +1,1337 @@ +/* temp_serializer.c: serialization functions for caching of FSX structures + * + * ==================================================================== + * 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 + +#include "svn_pools.h" +#include "svn_hash.h" +#include "svn_sorts.h" +#include "svn_fs.h" + +#include "private/svn_fs_util.h" +#include "private/svn_sorts_private.h" +#include "private/svn_temp_serializer.h" +#include "private/svn_subr_private.h" + +#include "id.h" +#include "temp_serializer.h" +#include "low_level.h" +#include "cached_data.h" + +/* Utility to encode a signed NUMBER into a variable-length sequence of + * 8-bit chars in KEY_BUFFER and return the last writen position. + * + * Numbers will be stored in 7 bits / byte and using byte values above + * 32 (' ') to make them combinable with other string by simply separating + * individual parts with spaces. + */ +static char* +encode_number(apr_int64_t number, char *key_buffer) +{ + /* encode the sign in the first byte */ + if (number < 0) + { + number = -number; + *key_buffer = (char)((number & 63) + ' ' + 65); + } + else + *key_buffer = (char)((number & 63) + ' ' + 1); + number /= 64; + + /* write 7 bits / byte until no significant bits are left */ + while (number) + { + *++key_buffer = (char)((number & 127) + ' ' + 1); + number /= 128; + } + + /* return the last written position */ + return key_buffer; +} + +const char* +svn_fs_x__combine_number_and_string(apr_int64_t number, + const char *string, + apr_pool_t *pool) +{ + apr_size_t len = strlen(string); + + /* number part requires max. 10x7 bits + 1 space. + * Add another 1 for the terminal 0 */ + char *key_buffer = apr_palloc(pool, len + 12); + const char *key = key_buffer; + + /* Prepend the number to the string and separate them by space. No other + * number can result in the same prefix, no other string in the same + * postfix nor can the boundary between them be ambiguous. */ + key_buffer = encode_number(number, key_buffer); + *++key_buffer = ' '; + memcpy(++key_buffer, string, len+1); + + /* return the start of the key */ + return key; +} + +/* Utility function to serialize string S in the given serialization CONTEXT. + */ +static void +serialize_svn_string(svn_temp_serializer__context_t *context, + const svn_string_t * const *s) +{ + const svn_string_t *string = *s; + + /* Nothing to do for NULL string references. */ + if (string == NULL) + return; + + svn_temp_serializer__push(context, + (const void * const *)s, + sizeof(*string)); + + /* the "string" content may actually be arbitrary binary data. + * Thus, we cannot use svn_temp_serializer__add_string. */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&string->data, + string->len + 1); + + /* back to the caller's nesting level */ + svn_temp_serializer__pop(context); +} + +/* Utility function to deserialize the STRING inside the BUFFER. + */ +static void +deserialize_svn_string(void *buffer, svn_string_t **string) +{ + svn_temp_deserializer__resolve(buffer, (void **)string); + if (*string == NULL) + return; + + svn_temp_deserializer__resolve(*string, (void **)&(*string)->data); +} + +/* Utility function to serialize the REPRESENTATION within the given + * serialization CONTEXT. + */ +static void +serialize_representation(svn_temp_serializer__context_t *context, + svn_fs_x__representation_t * const *representation) +{ + const svn_fs_x__representation_t * rep = *representation; + if (rep == NULL) + return; + + /* serialize the representation struct itself */ + svn_temp_serializer__add_leaf(context, + (const void * const *)representation, + sizeof(*rep)); +} + +void +svn_fs_x__serialize_apr_array(svn_temp_serializer__context_t *context, + apr_array_header_t **a) +{ + const apr_array_header_t *array = *a; + + /* Nothing to do for NULL string references. */ + if (array == NULL) + return; + + /* array header struct */ + svn_temp_serializer__push(context, + (const void * const *)a, + sizeof(*array)); + + /* contents */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&array->elts, + (apr_size_t)array->nelts * array->elt_size); + + /* back to the caller's nesting level */ + svn_temp_serializer__pop(context); +} + +void +svn_fs_x__deserialize_apr_array(void *buffer, + apr_array_header_t **array, + apr_pool_t *pool) +{ + svn_temp_deserializer__resolve(buffer, (void **)array); + if (*array == NULL) + return; + + svn_temp_deserializer__resolve(*array, (void **)&(*array)->elts); + (*array)->pool = pool; +} + +/* auxilliary structure representing the content of a directory array */ +typedef struct dir_data_t +{ + /* number of entries in the directory + * (it's int because the directory is an APR array) */ + int count; + + /* number of unused dir entry buckets in the index */ + apr_size_t over_provision; + + /* internal modifying operations counter + * (used to repack data once in a while) */ + apr_size_t operations; + + /* size of the serialization buffer actually used. + * (we will allocate more than we actually need such that we may + * append more data in situ later) */ + apr_size_t len; + + /* reference to the entries */ + svn_fs_x__dirent_t **entries; + + /* size of the serialized entries and don't be too wasteful + * (needed since the entries are no longer in sequence) */ + apr_uint32_t *lengths; +} dir_data_t; + +/* Utility function to serialize the *ENTRY_P into a the given + * serialization CONTEXT. Return the serialized size of the + * dir entry in *LENGTH. + */ +static void +serialize_dir_entry(svn_temp_serializer__context_t *context, + svn_fs_x__dirent_t **entry_p, + apr_uint32_t *length) +{ + svn_fs_x__dirent_t *entry = *entry_p; + apr_size_t initial_length = svn_temp_serializer__get_length(context); + + svn_temp_serializer__push(context, + (const void * const *)entry_p, + sizeof(svn_fs_x__dirent_t)); + + svn_temp_serializer__add_string(context, &entry->name); + + *length = (apr_uint32_t)( svn_temp_serializer__get_length(context) + - APR_ALIGN_DEFAULT(initial_length)); + + svn_temp_serializer__pop(context); +} + +/* Utility function to serialize the ENTRIES into a new serialization + * context to be returned. + * + * Temporary allocation will be made form SCRATCH_POOL. + */ +static svn_temp_serializer__context_t * +serialize_dir(apr_array_header_t *entries, + apr_pool_t *scratch_pool) +{ + dir_data_t dir_data; + int i = 0; + svn_temp_serializer__context_t *context; + + /* calculate sizes */ + int count = entries->nelts; + apr_size_t over_provision = 2 + count / 4; + apr_size_t entries_len = (count + over_provision) + * sizeof(svn_fs_x__dirent_t*); + apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t); + + /* copy the hash entries to an auxiliary struct of known layout */ + dir_data.count = count; + dir_data.over_provision = over_provision; + dir_data.operations = 0; + dir_data.entries = apr_palloc(scratch_pool, entries_len); + dir_data.lengths = apr_palloc(scratch_pool, lengths_len); + + for (i = 0; i < count; ++i) + dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); + + /* Serialize that aux. structure into a new one. Also, provide a good + * estimate for the size of the buffer that we will need. */ + context = svn_temp_serializer__init(&dir_data, + sizeof(dir_data), + 50 + count * 200 + entries_len, + scratch_pool); + + /* serialize entries references */ + svn_temp_serializer__push(context, + (const void * const *)&dir_data.entries, + entries_len); + + /* serialize the individual entries and their sub-structures */ + for (i = 0; i < count; ++i) + serialize_dir_entry(context, + &dir_data.entries[i], + &dir_data.lengths[i]); + + svn_temp_serializer__pop(context); + + /* serialize entries references */ + svn_temp_serializer__push(context, + (const void * const *)&dir_data.lengths, + lengths_len); + + return context; +} + +/* Utility function to reconstruct a dir entries array from serialized data + * in BUFFER and DIR_DATA. Allocation will be made form POOL. + */ +static apr_array_header_t * +deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool) +{ + apr_array_header_t *result + = apr_array_make(pool, dir_data->count, sizeof(svn_fs_x__dirent_t *)); + apr_size_t i; + apr_size_t count; + svn_fs_x__dirent_t *entry; + svn_fs_x__dirent_t **entries; + + /* resolve the reference to the entries array */ + svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries); + entries = dir_data->entries; + + /* fixup the references within each entry and add it to the hash */ + for (i = 0, count = dir_data->count; i < count; ++i) + { + svn_temp_deserializer__resolve(entries, (void **)&entries[i]); + entry = dir_data->entries[i]; + + /* pointer fixup */ + svn_temp_deserializer__resolve(entry, (void **)&entry->name); + + /* add the entry to the hash */ + APR_ARRAY_PUSH(result, svn_fs_x__dirent_t *) = entry; + } + + /* return the now complete hash */ + return result; +} + +void +svn_fs_x__noderev_serialize(svn_temp_serializer__context_t *context, + svn_fs_x__noderev_t * const *noderev_p) +{ + const svn_fs_x__noderev_t *noderev = *noderev_p; + if (noderev == NULL) + return; + + /* serialize the representation struct itself */ + svn_temp_serializer__push(context, + (const void * const *)noderev_p, + sizeof(*noderev)); + + /* serialize sub-structures */ + serialize_representation(context, &noderev->prop_rep); + serialize_representation(context, &noderev->data_rep); + + svn_temp_serializer__add_string(context, &noderev->copyfrom_path); + svn_temp_serializer__add_string(context, &noderev->copyroot_path); + svn_temp_serializer__add_string(context, &noderev->created_path); + + /* return to the caller's nesting level */ + svn_temp_serializer__pop(context); +} + + +void +svn_fs_x__noderev_deserialize(void *buffer, + svn_fs_x__noderev_t **noderev_p, + apr_pool_t *pool) +{ + svn_fs_x__noderev_t *noderev; + + /* fixup the reference to the representation itself, + * if this is part of a parent structure. */ + if (buffer != *noderev_p) + svn_temp_deserializer__resolve(buffer, (void **)noderev_p); + + noderev = *noderev_p; + if (noderev == NULL) + return; + + /* fixup of sub-structures */ + svn_temp_deserializer__resolve(noderev, (void **)&noderev->prop_rep); + svn_temp_deserializer__resolve(noderev, (void **)&noderev->data_rep); + + svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path); + svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path); + svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path); +} + + +/* Utility function to serialize COUNT svn_txdelta_op_t objects + * at OPS in the given serialization CONTEXT. + */ +static void +serialize_txdelta_ops(svn_temp_serializer__context_t *context, + const svn_txdelta_op_t * const * ops, + apr_size_t count) +{ + if (*ops == NULL) + return; + + /* the ops form a contiguous chunk of memory with no further references */ + svn_temp_serializer__add_leaf(context, + (const void * const *)ops, + count * sizeof(svn_txdelta_op_t)); +} + +/* Utility function to serialize W in the given serialization CONTEXT. + */ +static void +serialize_txdeltawindow(svn_temp_serializer__context_t *context, + svn_txdelta_window_t * const * w) +{ + svn_txdelta_window_t *window = *w; + + /* serialize the window struct itself */ + svn_temp_serializer__push(context, + (const void * const *)w, + sizeof(svn_txdelta_window_t)); + + /* serialize its sub-structures */ + serialize_txdelta_ops(context, &window->ops, window->num_ops); + serialize_svn_string(context, &window->new_data); + + svn_temp_serializer__pop(context); +} + +svn_error_t * +svn_fs_x__serialize_txdelta_window(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool) +{ + svn_fs_x__txdelta_cached_window_t *window_info = item; + svn_stringbuf_t *serialized; + + /* initialize the serialization process and allocate a buffer large + * enough to do without the need of re-allocations in most cases. */ + apr_size_t text_len = window_info->window->new_data + ? window_info->window->new_data->len + : 0; + svn_temp_serializer__context_t *context = + svn_temp_serializer__init(window_info, + sizeof(*window_info), + 500 + text_len, + pool); + + /* serialize the sub-structure(s) */ + serialize_txdeltawindow(context, &window_info->window); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *buffer = serialized->data; + *buffer_size = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_txdelta_window(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool) +{ + svn_txdelta_window_t *window; + + /* Copy the _full_ buffer as it also contains the sub-structures. */ + svn_fs_x__txdelta_cached_window_t *window_info = + (svn_fs_x__txdelta_cached_window_t *)buffer; + + /* pointer reference fixup */ + svn_temp_deserializer__resolve(window_info, + (void **)&window_info->window); + window = window_info->window; + + svn_temp_deserializer__resolve(window, (void **)&window->ops); + + deserialize_svn_string(window, (svn_string_t**)&window->new_data); + + /* done */ + *item = window_info; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_manifest(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_array_header_t *manifest = in; + + *data_len = sizeof(apr_off_t) *manifest->nelts; + *data = apr_palloc(pool, *data_len); + memcpy(*data, manifest->elts, *data_len); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_manifest(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + apr_array_header_t *manifest = apr_array_make(pool, 1, sizeof(apr_off_t)); + + manifest->nelts = (int) (data_len / sizeof(apr_off_t)); + manifest->nalloc = (int) (data_len / sizeof(apr_off_t)); + manifest->elts = (char*)data; + + *out = manifest; + + return SVN_NO_ERROR; +} + +/* Auxiliary structure representing the content of a properties hash. + This structure is much easier to (de-)serialize than an apr_hash. + */ +typedef struct properties_data_t +{ + /* number of entries in the hash */ + apr_size_t count; + + /* reference to the keys */ + const char **keys; + + /* reference to the values */ + const svn_string_t **values; +} properties_data_t; + +/* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */ +static void +serialize_cstring_array(svn_temp_serializer__context_t *context, + const char ***strings, + apr_size_t count) +{ + apr_size_t i; + const char **entries = *strings; + + /* serialize COUNT entries pointers (the array) */ + svn_temp_serializer__push(context, + (const void * const *)strings, + count * sizeof(const char*)); + + /* serialize array elements */ + for (i = 0; i < count; ++i) + svn_temp_serializer__add_string(context, &entries[i]); + + svn_temp_serializer__pop(context); +} + +/* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */ +static void +serialize_svn_string_array(svn_temp_serializer__context_t *context, + const svn_string_t ***strings, + apr_size_t count) +{ + apr_size_t i; + const svn_string_t **entries = *strings; + + /* serialize COUNT entries pointers (the array) */ + svn_temp_serializer__push(context, + (const void * const *)strings, + count * sizeof(const char*)); + + /* serialize array elements */ + for (i = 0; i < count; ++i) + serialize_svn_string(context, &entries[i]); + + svn_temp_serializer__pop(context); +} + +svn_error_t * +svn_fs_x__serialize_properties(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_hash_t *hash = in; + properties_data_t properties; + svn_temp_serializer__context_t *context; + apr_hash_index_t *hi; + svn_stringbuf_t *serialized; + apr_size_t i; + + /* create our auxiliary data structure */ + properties.count = apr_hash_count(hash); + properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1)); + properties.values = apr_palloc(pool, sizeof(const char*) * properties.count); + + /* populate it with the hash entries */ + for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i) + { + properties.keys[i] = apr_hash_this_key(hi); + properties.values[i] = apr_hash_this_val(hi); + } + + /* serialize it */ + context = svn_temp_serializer__init(&properties, + sizeof(properties), + properties.count * 100, + pool); + + properties.keys[i] = ""; + serialize_cstring_array(context, &properties.keys, properties.count + 1); + serialize_svn_string_array(context, &properties.values, properties.count); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_properties(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + apr_hash_t *hash = svn_hash__make(pool); + properties_data_t *properties = (properties_data_t *)data; + size_t i; + + /* de-serialize our auxiliary data structure */ + svn_temp_deserializer__resolve(properties, (void**)&properties->keys); + svn_temp_deserializer__resolve(properties, (void**)&properties->values); + + /* de-serialize each entry and put it into the hash */ + for (i = 0; i < properties->count; ++i) + { + apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1; + svn_temp_deserializer__resolve(properties->keys, + (void**)&properties->keys[i]); + + deserialize_svn_string(properties->values, + (svn_string_t **)&properties->values[i]); + + apr_hash_set(hash, + properties->keys[i], len, + properties->values[i]); + } + + /* done */ + *out = hash; + + return SVN_NO_ERROR; +} + +/** Caching svn_fs_x__noderev_t objects. **/ + +svn_error_t * +svn_fs_x__serialize_node_revision(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool) +{ + svn_stringbuf_t *serialized; + svn_fs_x__noderev_t *noderev = item; + + /* create an (empty) serialization context with plenty of (initial) + * buffer space. */ + svn_temp_serializer__context_t *context = + svn_temp_serializer__init(NULL, 0, + 1024 - SVN_TEMP_SERIALIZER__OVERHEAD, + pool); + + /* serialize the noderev */ + svn_fs_x__noderev_serialize(context, &noderev); + + /* return serialized data */ + serialized = svn_temp_serializer__get(context); + *buffer = serialized->data; + *buffer_size = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_node_revision(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool) +{ + /* Copy the _full_ buffer as it also contains the sub-structures. */ + svn_fs_x__noderev_t *noderev = (svn_fs_x__noderev_t *)buffer; + + /* fixup of all pointers etc. */ + svn_fs_x__noderev_deserialize(noderev, &noderev, pool); + + /* done */ + *item = noderev; + return SVN_NO_ERROR; +} + +/* Utility function that returns the directory serialized inside CONTEXT + * to DATA and DATA_LEN. */ +static svn_error_t * +return_serialized_dir_context(svn_temp_serializer__context_t *context, + void **data, + apr_size_t *data_len) +{ + svn_stringbuf_t *serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->blocksize; + ((dir_data_t *)serialized->data)->len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_dir_entries(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_array_header_t *dir = in; + + /* serialize the dir content into a new serialization context + * and return the serialized data */ + return return_serialized_dir_context(serialize_dir(dir, pool), + data, + data_len); +} + +svn_error_t * +svn_fs_x__deserialize_dir_entries(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + /* Copy the _full_ buffer as it also contains the sub-structures. */ + dir_data_t *dir_data = (dir_data_t *)data; + + /* reconstruct the hash from the serialized data */ + *out = deserialize_dir(dir_data, dir_data, pool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_sharded_offset(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + const apr_off_t *manifest = data; + apr_int64_t shard_pos = *(apr_int64_t *)baton; + + *(apr_off_t *)out = manifest[shard_pos]; + + return SVN_NO_ERROR; +} + +/* Utility function that returns the lowest index of the first entry in + * *ENTRIES that points to a dir entry with a name equal or larger than NAME. + * If an exact match has been found, *FOUND will be set to TRUE. COUNT is + * the number of valid entries in ENTRIES. + */ +static apr_size_t +find_entry(svn_fs_x__dirent_t **entries, + const char *name, + apr_size_t count, + svn_boolean_t *found) +{ + /* binary search for the desired entry by name */ + apr_size_t lower = 0; + apr_size_t upper = count; + apr_size_t middle; + + for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2) + { + const svn_fs_x__dirent_t *entry = + svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]); + const char* entry_name = + svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name); + + int diff = strcmp(entry_name, name); + if (diff < 0) + lower = middle + 1; + else + upper = middle; + } + + /* check whether we actually found a match */ + *found = FALSE; + if (lower < count) + { + const svn_fs_x__dirent_t *entry = + svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]); + const char* entry_name = + svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name); + + if (strcmp(entry_name, name) == 0) + *found = TRUE; + } + + return lower; +} + +/* Utility function that returns TRUE if entry number IDX in ENTRIES has the + * name NAME. + */ +static svn_boolean_t +found_entry(const svn_fs_x__dirent_t * const *entries, + const char *name, + apr_size_t idx) +{ + /* check whether we actually found a match */ + const svn_fs_x__dirent_t *entry = + svn_temp_deserializer__ptr(entries, (const void *const *)&entries[idx]); + const char* entry_name = + svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name); + + return strcmp(entry_name, name) == 0; +} + +svn_error_t * +svn_fs_x__extract_dir_entry(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + const dir_data_t *dir_data = data; + svn_fs_x__ede_baton_t *b = baton; + svn_boolean_t found; + apr_size_t pos; + + /* resolve the reference to the entries array */ + const svn_fs_x__dirent_t * const *entries = + svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries); + + /* resolve the reference to the lengths array */ + const apr_uint32_t *lengths = + svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths); + + /* Special case: Early out for empty directories. + That simplifies tests further down the road. */ + *out = NULL; + if (dir_data->count == 0) + return SVN_NO_ERROR; + + /* HINT _might_ be the position we hit last time. + If within valid range, check whether HINT+1 is a hit. */ + if ( b->hint < dir_data->count - 1 + && found_entry(entries, b->name, b->hint + 1)) + { + /* Got lucky. */ + pos = b->hint + 1; + found = TRUE; + } + else + { + /* Binary search for the desired entry by name. */ + pos = find_entry((svn_fs_x__dirent_t **)entries, b->name, + dir_data->count, &found); + } + + /* Remember the hit index - if we FOUND the entry. */ + if (found) + b->hint = pos; + + /* de-serialize that entry or return NULL, if no match has been found */ + if (found) + { + const svn_fs_x__dirent_t *source = + svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]); + + /* Entries have been serialized one-by-one, each time including all + * nested structures and strings. Therefore, they occupy a single + * block of memory whose end-offset is either the beginning of the + * next entry or the end of the buffer + */ + apr_size_t size = lengths[pos]; + + /* copy & deserialize the entry */ + svn_fs_x__dirent_t *new_entry = apr_palloc(pool, size); + memcpy(new_entry, source, size); + + svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name); + *(svn_fs_x__dirent_t **)out = new_entry; + } + + return SVN_NO_ERROR; +} + +/* Utility function for svn_fs_x__replace_dir_entry that implements the + * modification as a simply deserialize / modify / serialize sequence. + */ +static svn_error_t * +slowly_replace_dir_entry(void **data, + apr_size_t *data_len, + void *baton, + apr_pool_t *pool) +{ + replace_baton_t *replace_baton = (replace_baton_t *)baton; + dir_data_t *dir_data = (dir_data_t *)*data; + apr_array_header_t *dir; + int idx = -1; + svn_fs_x__dirent_t *entry; + + SVN_ERR(svn_fs_x__deserialize_dir_entries((void **)&dir, + *data, + dir_data->len, + pool)); + + entry = svn_fs_x__find_dir_entry(dir, replace_baton->name, &idx); + + /* Replacement or removal? */ + if (replace_baton->new_entry) + { + /* Replace ENTRY with / insert the NEW_ENTRY */ + if (entry) + APR_ARRAY_IDX(dir, idx, svn_fs_x__dirent_t *) + = replace_baton->new_entry; + else + svn_sort__array_insert(dir, &replace_baton->new_entry, idx); + } + else + { + /* Remove the old ENTRY. */ + if (entry) + svn_sort__array_delete(dir, idx, 1); + } + + return svn_fs_x__serialize_dir_entries(data, data_len, dir, pool); +} + +svn_error_t * +svn_fs_x__replace_dir_entry(void **data, + apr_size_t *data_len, + void *baton, + apr_pool_t *pool) +{ + replace_baton_t *replace_baton = (replace_baton_t *)baton; + dir_data_t *dir_data = (dir_data_t *)*data; + svn_boolean_t found; + svn_fs_x__dirent_t **entries; + apr_uint32_t *lengths; + apr_uint32_t length; + apr_size_t pos; + + svn_temp_serializer__context_t *context; + + /* after quite a number of operations, let's re-pack everything. + * This is to limit the number of wasted space as we cannot overwrite + * existing data but must always append. */ + if (dir_data->operations > 2 + dir_data->count / 4) + return slowly_replace_dir_entry(data, data_len, baton, pool); + + /* resolve the reference to the entries array */ + entries = (svn_fs_x__dirent_t **) + svn_temp_deserializer__ptr((const char *)dir_data, + (const void *const *)&dir_data->entries); + + /* resolve the reference to the lengths array */ + lengths = (apr_uint32_t *) + svn_temp_deserializer__ptr((const char *)dir_data, + (const void *const *)&dir_data->lengths); + + /* binary search for the desired entry by name */ + pos = find_entry(entries, replace_baton->name, dir_data->count, &found); + + /* handle entry removal (if found at all) */ + if (replace_baton->new_entry == NULL) + { + if (found) + { + /* remove reference to the entry from the index */ + memmove(&entries[pos], + &entries[pos + 1], + sizeof(entries[pos]) * (dir_data->count - pos)); + memmove(&lengths[pos], + &lengths[pos + 1], + sizeof(lengths[pos]) * (dir_data->count - pos)); + + dir_data->count--; + dir_data->over_provision++; + dir_data->operations++; + } + + return SVN_NO_ERROR; + } + + /* if not found, prepare to insert the new entry */ + if (!found) + { + /* fallback to slow operation if there is no place left to insert an + * new entry to index. That will automatically give add some spare + * entries ("overprovision"). */ + if (dir_data->over_provision == 0) + return slowly_replace_dir_entry(data, data_len, baton, pool); + + /* make entries[index] available for pointing to the new entry */ + memmove(&entries[pos + 1], + &entries[pos], + sizeof(entries[pos]) * (dir_data->count - pos)); + memmove(&lengths[pos + 1], + &lengths[pos], + sizeof(lengths[pos]) * (dir_data->count - pos)); + + dir_data->count++; + dir_data->over_provision--; + dir_data->operations++; + } + + /* de-serialize the new entry */ + entries[pos] = replace_baton->new_entry; + context = svn_temp_serializer__init_append(dir_data, + entries, + dir_data->len, + *data_len, + pool); + serialize_dir_entry(context, &entries[pos], &length); + + /* return the updated serialized data */ + SVN_ERR (return_serialized_dir_context(context, + data, + data_len)); + + /* since the previous call may have re-allocated the buffer, the lengths + * pointer may no longer point to the entry in that buffer. Therefore, + * re-map it again and store the length value after that. */ + + dir_data = (dir_data_t *)*data; + lengths = (apr_uint32_t *) + svn_temp_deserializer__ptr((const char *)dir_data, + (const void *const *)&dir_data->lengths); + lengths[pos] = length; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_rep_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__rep_header_t *copy = apr_palloc(pool, sizeof(*copy)); + *copy = *(svn_fs_x__rep_header_t *)in; + + *data_len = sizeof(svn_fs_x__rep_header_t); + *data = copy; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_rep_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__rep_header_t *copy = apr_palloc(pool, sizeof(*copy)); + SVN_ERR_ASSERT(data_len == sizeof(*copy)); + + *copy = *(svn_fs_x__rep_header_t *)data; + *out = data; + + return SVN_NO_ERROR; +} + +/* Utility function to serialize change CHANGE_P in the given serialization + * CONTEXT. + */ +static void +serialize_change(svn_temp_serializer__context_t *context, + svn_fs_x__change_t * const *change_p) +{ + const svn_fs_x__change_t * change = *change_p; + if (change == NULL) + return; + + /* serialize the change struct itself */ + svn_temp_serializer__push(context, + (const void * const *)change_p, + sizeof(*change)); + + /* serialize sub-structures */ + svn_temp_serializer__add_string(context, &change->path.data); + svn_temp_serializer__add_string(context, &change->copyfrom_path); + + /* return to the caller's nesting level */ + svn_temp_serializer__pop(context); +} + +/* Utility function to serialize the CHANGE_P within the given + * serialization CONTEXT. + */ +static void +deserialize_change(void *buffer, + svn_fs_x__change_t **change_p, + apr_pool_t *pool) +{ + svn_fs_x__change_t * change; + + /* fix-up of the pointer to the struct in question */ + svn_temp_deserializer__resolve(buffer, (void **)change_p); + + change = *change_p; + if (change == NULL) + return; + + /* fix-up of sub-structures */ + svn_temp_deserializer__resolve(change, (void **)&change->path.data); + svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path); +} + +/* Auxiliary structure representing the content of a svn_fs_x__change_t array. + This structure is much easier to (de-)serialize than an APR array. + */ +typedef struct changes_data_t +{ + /* number of entries in the array */ + int count; + + /* reference to the changes */ + svn_fs_x__change_t **changes; +} changes_data_t; + +svn_error_t * +svn_fs_x__serialize_changes(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + apr_array_header_t *array = in; + changes_data_t changes; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + int i; + + /* initialize our auxiliary data structure and link it to the + * array elements */ + changes.count = array->nelts; + changes.changes = (svn_fs_x__change_t **)array->elts; + + /* serialize it and all its elements */ + context = svn_temp_serializer__init(&changes, + sizeof(changes), + changes.count * 250, + pool); + + svn_temp_serializer__push(context, + (const void * const *)&changes.changes, + changes.count * sizeof(svn_fs_x__change_t*)); + + for (i = 0; i < changes.count; ++i) + serialize_change(context, &changes.changes[i]); + + svn_temp_serializer__pop(context); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_changes(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + int i; + changes_data_t *changes = (changes_data_t *)data; + apr_array_header_t *array = apr_array_make(pool, 0, + sizeof(svn_fs_x__change_t *)); + + /* de-serialize our auxiliary data structure */ + svn_temp_deserializer__resolve(changes, (void**)&changes->changes); + + /* de-serialize each entry and add it to the array */ + for (i = 0; i < changes->count; ++i) + deserialize_change(changes->changes, + (svn_fs_x__change_t **)&changes->changes[i], + pool); + + /* Use the changes buffer as the array's data buffer + * (DATA remains valid for at least as long as POOL). */ + array->elts = (char *)changes->changes; + array->nelts = changes->count; + array->nalloc = changes->count; + + /* done */ + *out = array; + + return SVN_NO_ERROR; +} + +/* Auxiliary structure representing the content of a svn_mergeinfo_t hash. + This structure is much easier to (de-)serialize than an APR array. + */ +typedef struct mergeinfo_data_t +{ + /* number of paths in the hash */ + unsigned count; + + /* COUNT keys (paths) */ + const char **keys; + + /* COUNT keys lengths (strlen of path) */ + apr_ssize_t *key_lengths; + + /* COUNT entries, each giving the number of ranges for the key */ + int *range_counts; + + /* all ranges in a single, concatenated buffer */ + svn_merge_range_t *ranges; +} mergeinfo_data_t; + +svn_error_t * +svn_fs_x__serialize_mergeinfo(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_mergeinfo_t mergeinfo = in; + mergeinfo_data_t merges; + svn_temp_serializer__context_t *context; + svn_stringbuf_t *serialized; + apr_hash_index_t *hi; + unsigned i; + int k; + apr_size_t range_count; + + /* initialize our auxiliary data structure */ + merges.count = apr_hash_count(mergeinfo); + merges.keys = apr_palloc(pool, sizeof(*merges.keys) * merges.count); + merges.key_lengths = apr_palloc(pool, sizeof(*merges.key_lengths) * + merges.count); + merges.range_counts = apr_palloc(pool, sizeof(*merges.range_counts) * + merges.count); + + i = 0; + range_count = 0; + for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi), ++i) + { + svn_rangelist_t *ranges; + apr_hash_this(hi, (const void**)&merges.keys[i], + &merges.key_lengths[i], + (void **)&ranges); + merges.range_counts[i] = ranges->nelts; + range_count += ranges->nelts; + } + + merges.ranges = apr_palloc(pool, sizeof(*merges.ranges) * range_count); + + i = 0; + for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) + { + svn_rangelist_t *ranges = apr_hash_this_val(hi); + for (k = 0; k < ranges->nelts; ++k, ++i) + merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*); + } + + /* serialize it and all its elements */ + context = svn_temp_serializer__init(&merges, + sizeof(merges), + range_count * 30, + pool); + + /* keys array */ + svn_temp_serializer__push(context, + (const void * const *)&merges.keys, + merges.count * sizeof(*merges.keys)); + + for (i = 0; i < merges.count; ++i) + svn_temp_serializer__add_string(context, &merges.keys[i]); + + svn_temp_serializer__pop(context); + + /* key lengths array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.key_lengths, + merges.count * sizeof(*merges.key_lengths)); + + /* range counts array */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.range_counts, + merges.count * sizeof(*merges.range_counts)); + + /* ranges */ + svn_temp_serializer__add_leaf(context, + (const void * const *)&merges.ranges, + range_count * sizeof(*merges.ranges)); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_mergeinfo(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + unsigned i; + int k, n; + mergeinfo_data_t *merges = (mergeinfo_data_t *)data; + svn_mergeinfo_t mergeinfo; + + /* de-serialize our auxiliary data structure */ + svn_temp_deserializer__resolve(merges, (void**)&merges->keys); + svn_temp_deserializer__resolve(merges, (void**)&merges->key_lengths); + svn_temp_deserializer__resolve(merges, (void**)&merges->range_counts); + svn_temp_deserializer__resolve(merges, (void**)&merges->ranges); + + /* de-serialize keys and add entries to the result */ + n = 0; + mergeinfo = svn_hash__make(pool); + for (i = 0; i < merges->count; ++i) + { + svn_rangelist_t *ranges = apr_array_make(pool, + merges->range_counts[i], + sizeof(svn_merge_range_t*)); + for (k = 0; k < merges->range_counts[i]; ++k, ++n) + APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n]; + + svn_temp_deserializer__resolve(merges->keys, + (void**)&merges->keys[i]); + apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges); + } + + /* done */ + *out = mergeinfo; + + return SVN_NO_ERROR; +} + diff --git a/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.h b/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.h new file mode 100644 index 000000000..80f500484 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/temp_serializer.h @@ -0,0 +1,301 @@ +/* temp_serializer.h : serialization functions for caching of FSX structures + * + * ==================================================================== + * 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_FS__TEMP_SERIALIZER_H +#define SVN_LIBSVN_FS__TEMP_SERIALIZER_H + +#include "private/svn_temp_serializer.h" +#include "fs.h" + +/** + * Prepend the @a number to the @a string in a space efficient way such that + * no other (number,string) combination can produce the same result. + * Allocate temporaries as well as the result from @a pool. + */ +const char* +svn_fs_x__combine_number_and_string(apr_int64_t number, + const char *string, + apr_pool_t *pool); + +/** + * Serialize a @a noderev_p within the serialization @a context. + */ +void +svn_fs_x__noderev_serialize(struct svn_temp_serializer__context_t *context, + svn_fs_x__noderev_t * const *noderev_p); + +/** + * Deserialize a @a noderev_p within the @a buffer and associate it with + * @a pool. + */ +void +svn_fs_x__noderev_deserialize(void *buffer, + svn_fs_x__noderev_t **noderev_p, + apr_pool_t *pool); + +/** + * Serialize APR array @a *a within the serialization @a context. + * The elements within the array must not contain pointers. + */ +void +svn_fs_x__serialize_apr_array(struct svn_temp_serializer__context_t *context, + apr_array_header_t **a); + +/** + * Deserialize APR @a *array within the @a buffer. Set its pool member to + * @a pool. The elements within the array must not contain pointers. + */ +void +svn_fs_x__deserialize_apr_array(void *buffer, + apr_array_header_t **array, + apr_pool_t *pool); + + +/** + * #svn_txdelta_window_t is not sufficient for caching the data it + * represents because data read process needs auxiliary information. + */ +typedef struct +{ + /* the txdelta window information cached / to be cached */ + svn_txdelta_window_t *window; + + /* the revision file read pointer position before reading the window */ + apr_off_t start_offset; + + /* the revision file read pointer position right after reading the window */ + apr_off_t end_offset; +} svn_fs_x__txdelta_cached_window_t; + +/** + * Implements #svn_cache__serialize_func_t for + * #svn_fs_x__txdelta_cached_window_t. + */ +svn_error_t * +svn_fs_x__serialize_txdelta_window(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for + * #svn_fs_x__txdelta_cached_window_t. + */ +svn_error_t * +svn_fs_x__deserialize_txdelta_window(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for a manifest + * (@a in is an #apr_array_header_t of apr_off_t elements). + */ +svn_error_t * +svn_fs_x__serialize_manifest(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for a manifest + * (@a *out is an #apr_array_header_t of apr_off_t elements). + */ +svn_error_t * +svn_fs_x__deserialize_manifest(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for a properties hash + * (@a in is an #apr_hash_t of svn_string_t elements, keyed by const char*). + */ +svn_error_t * +svn_fs_x__serialize_properties(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for a properties hash + * (@a *out is an #apr_hash_t of svn_string_t elements, keyed by const char*). + */ +svn_error_t * +svn_fs_x__deserialize_properties(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for #svn_fs_x__noderev_t + */ +svn_error_t * +svn_fs_x__serialize_node_revision(void **buffer, + apr_size_t *buffer_size, + void *item, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for #svn_fs_x__noderev_t + */ +svn_error_t * +svn_fs_x__deserialize_node_revision(void **item, + void *buffer, + apr_size_t buffer_size, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for a directory contents array + */ +svn_error_t * +svn_fs_x__serialize_dir_entries(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for a directory contents array + */ +svn_error_t * +svn_fs_x__deserialize_dir_entries(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/** + * Implements #svn_cache__partial_getter_func_t. Set (apr_off_t) @a *out + * to the element indexed by (apr_int64_t) @a *baton within the + * serialized manifest array @a data and @a data_len. */ +svn_error_t * +svn_fs_x__get_sharded_offset(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +/** + * Baton type to be used with svn_fs_x__extract_dir_entry. */ +typedef struct svn_fs_x__ede_baton_t +{ + /* Name of the directory entry to find. */ + const char *name; + + /* Lookup hint [in / out] */ + apr_size_t hint; +} svn_fs_x__ede_baton_t; + +/** + * Implements #svn_cache__partial_getter_func_t for a single + * #svn_fs_x__dirent_t within a serialized directory contents hash, + * identified by its name (given in @a svn_fs_x__ede_baton_t @a *baton). + */ +svn_error_t * +svn_fs_x__extract_dir_entry(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool); + +/** + * Describes the change to be done to a directory: Set the entry + * identify by @a name to the value @a new_entry. If the latter is + * @c NULL, the entry shall be removed if it exists. Otherwise it + * will be replaced or automatically added, respectively. + */ +typedef struct replace_baton_t +{ + /** name of the directory entry to modify */ + const char *name; + + /** directory entry to insert instead */ + svn_fs_x__dirent_t *new_entry; +} replace_baton_t; + +/** + * Implements #svn_cache__partial_setter_func_t for a single + * #svn_fs_x__dirent_t within a serialized directory contents hash, + * identified by its name in the #replace_baton_t in @a baton. + */ +svn_error_t * +svn_fs_x__replace_dir_entry(void **data, + apr_size_t *data_len, + void *baton, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for a #svn_fs_x__rep_header_t. + */ +svn_error_t * +svn_fs_x__serialize_rep_header(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for a #svn_fs_x__rep_header_t. + */ +svn_error_t * +svn_fs_x__deserialize_rep_header(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for an #apr_array_header_t of + * #svn_fs_x__change_t *. + */ +svn_error_t * +svn_fs_x__serialize_changes(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for an #apr_array_header_t of + * #svn_fs_x__change_t *. + */ +svn_error_t * +svn_fs_x__deserialize_changes(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +/** + * Implements #svn_cache__serialize_func_t for #svn_mergeinfo_t objects. + */ +svn_error_t * +svn_fs_x__serialize_mergeinfo(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool); + +/** + * Implements #svn_cache__deserialize_func_t for #svn_mergeinfo_t objects. + */ +svn_error_t * +svn_fs_x__deserialize_mergeinfo(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/transaction.c b/contrib/subversion/subversion/libsvn_fs_x/transaction.c new file mode 100644 index 000000000..5f3adc595 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/transaction.c @@ -0,0 +1,3782 @@ +/* transaction.c --- transaction-related functions of FSX + * + * ==================================================================== + * 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 "transaction.h" + +#include +#include + +#include "svn_hash.h" +#include "svn_props.h" +#include "svn_sorts.h" +#include "svn_time.h" +#include "svn_dirent_uri.h" + +#include "fs_x.h" +#include "tree.h" +#include "util.h" +#include "id.h" +#include "low_level.h" +#include "temp_serializer.h" +#include "cached_data.h" +#include "lock.h" +#include "rep-cache.h" +#include "index.h" + +#include "private/svn_fs_util.h" +#include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_io_private.h" +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* The vtable associated with an open transaction object. */ +static txn_vtable_t txn_vtable = { + svn_fs_x__commit_txn, + svn_fs_x__abort_txn, + svn_fs_x__txn_prop, + svn_fs_x__txn_proplist, + svn_fs_x__change_txn_prop, + svn_fs_x__txn_root, + svn_fs_x__change_txn_props +}; + +/* FSX-specific data being attached to svn_fs_txn_t. + */ +typedef struct fs_txn_data_t +{ + /* Strongly typed representation of the TXN's ID member. */ + svn_fs_x__txn_id_t txn_id; +} fs_txn_data_t; + +svn_fs_x__txn_id_t +svn_fs_x__txn_get_id(svn_fs_txn_t *txn) +{ + fs_txn_data_t *ftd = txn->fsap_data; + return ftd->txn_id; +} + +/* 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 svn_fs_x__shared_txn_data_t * +get_shared_txn(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_boolean_t create_new) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__shared_data_t *ffsd = ffd->shared; + svn_fs_x__shared_txn_data_t *txn; + + for (txn = ffsd->txns; txn; txn = txn->next) + if (txn->txn_id == txn_id) + 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; + } + + txn->txn_id = 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, svn_fs_x__txn_id_t txn_id) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__shared_data_t *ffsd = ffd->shared; + svn_fs_x__shared_txn_data_t *txn, *prev = NULL; + + for (txn = ffsd->txns; txn; prev = txn, txn = txn->next) + if (txn->txn_id == txn_id) + 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) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__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 RESULT_POOL. */ +static svn_error_t * +get_lock_on_filesystem(const char *lock_filename, + apr_pool_t *result_pool) +{ + return svn_error_trace(svn_io__file_lock_autocreate(lock_filename, + result_pool)); +} + +/* 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) +{ + svn_fs_x__data_t *ffd = baton_void; + ffd->has_write_lock = FALSE; + return APR_SUCCESS; +} + +/* Structure defining a file system lock to be acquired and the function + to be executed while the lock is held. + + Instances of this structure may be nested to allow for multiple locks to + be taken out before executing the user-provided body. In that case, BODY + and BATON of the outer instances will be with_lock and a with_lock_baton_t + instance (transparently, no special treatment is required.). It is + illegal to attempt to acquire the same lock twice within the same lock + chain or via nesting calls using separate lock chains. + + All instances along the chain share the same LOCK_POOL such that only one + pool needs to be created and cleared for all locks. We also allocate as + much data from that lock pool as possible to minimize memory usage in + caller pools. */ +typedef struct with_lock_baton_t +{ + /* The filesystem we operate on. Same for all instances along the chain. */ + svn_fs_t *fs; + + /* Mutex to complement the lock file in an APR threaded process. + No-op object for non-threaded processes but never NULL. */ + svn_mutex__t *mutex; + + /* Path to the file to lock. */ + const char *lock_path; + + /* If true, set FS->HAS_WRITE_LOCK after we acquired the lock. */ + svn_boolean_t is_global_lock; + + /* Function body to execute after we acquired the lock. + This may be user-provided or a nested call to with_lock(). */ + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool); + + /* Baton to pass to BODY; possibly NULL. + This may be user-provided or a nested lock baton instance. */ + void *baton; + + /* Pool for all allocations along the lock chain and BODY. Will hold the + file locks and gets destroyed after the outermost BODY returned, + releasing all file locks. + Same for all instances along the chain. */ + apr_pool_t *lock_pool; + + /* TRUE, iff BODY is the user-provided body. */ + svn_boolean_t is_inner_most_lock; + + /* TRUE, iff this is not a nested lock. + Then responsible for destroying LOCK_POOL. */ + svn_boolean_t is_outer_most_lock; +} with_lock_baton_t; + +/* Obtain a write lock on the file BATON->LOCK_PATH and call BATON->BODY + with BATON->BATON. If this is the outermost lock call, release all file + locks after the body returned. If BATON->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(with_lock_baton_t *baton) +{ + apr_pool_t *pool = baton->lock_pool; + svn_error_t *err = get_lock_on_filesystem(baton->lock_path, pool); + + if (!err) + { + svn_fs_t *fs = baton->fs; + svn_fs_x__data_t *ffd = fs->fsap_data; + + if (baton->is_global_lock) + { + /* set the "got the lock" flag and register reset function */ + apr_pool_cleanup_register(pool, + 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 (baton->is_inner_most_lock) + { + err = svn_fs_x__update_min_unpacked_rev(fs, pool); + if (!err) + err = svn_fs_x__youngest_rev(&ffd->youngest_rev_cache, fs, pool); + } + + if (!err) + err = baton->body(baton->baton, pool); + } + + if (baton->is_outer_most_lock) + svn_pool_destroy(pool); + + return svn_error_trace(err); +} + +/* Wraps with_some_lock_file, protecting it with BATON->MUTEX. + + SCRATCH_POOL is unused here and only provided for signature compatibility + with WITH_LOCK_BATON_T.BODY. */ +static svn_error_t * +with_lock(void *baton, + apr_pool_t *scratch_pool) +{ + with_lock_baton_t *lock_baton = baton; + SVN_MUTEX__WITH_LOCK(lock_baton->mutex, with_some_lock_file(lock_baton)); + + return SVN_NO_ERROR; +} + +/* Enum identifying a filesystem lock. */ +typedef enum lock_id_t +{ + write_lock, + txn_lock, + pack_lock +} lock_id_t; + +/* Initialize BATON->MUTEX, BATON->LOCK_PATH and BATON->IS_GLOBAL_LOCK + according to the LOCK_ID. All other members of BATON must already be + valid. */ +static void +init_lock_baton(with_lock_baton_t *baton, + lock_id_t lock_id) +{ + svn_fs_x__data_t *ffd = baton->fs->fsap_data; + svn_fs_x__shared_data_t *ffsd = ffd->shared; + + switch (lock_id) + { + case write_lock: + baton->mutex = ffsd->fs_write_lock; + baton->lock_path = svn_fs_x__path_lock(baton->fs, baton->lock_pool); + baton->is_global_lock = TRUE; + break; + + case txn_lock: + baton->mutex = ffsd->txn_current_lock; + baton->lock_path = svn_fs_x__path_txn_current_lock(baton->fs, + baton->lock_pool); + baton->is_global_lock = FALSE; + break; + + case pack_lock: + baton->mutex = ffsd->fs_pack_lock; + baton->lock_path = svn_fs_x__path_pack_lock(baton->fs, + baton->lock_pool); + baton->is_global_lock = FALSE; + break; + } +} + +/* Return the baton for the innermost lock of a (potential) lock chain. + The baton shall take out LOCK_ID from FS and execute BODY with BATON + while the lock is being held. Allocate the result in a sub-pool of + RESULT_POOL. + */ +static with_lock_baton_t * +create_lock_baton(svn_fs_t *fs, + lock_id_t lock_id, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *result_pool) +{ + /* Allocate everything along the lock chain into a single sub-pool. + This minimizes memory usage and cleanup overhead. */ + apr_pool_t *lock_pool = svn_pool_create(result_pool); + with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); + + /* Store parameters. */ + result->fs = fs; + result->body = body; + result->baton = baton; + + /* File locks etc. will use this pool as well for easy cleanup. */ + result->lock_pool = lock_pool; + + /* Right now, we are the first, (only, ) and last struct in the chain. */ + result->is_inner_most_lock = TRUE; + result->is_outer_most_lock = TRUE; + + /* Select mutex and lock file path depending on LOCK_ID. + Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ + init_lock_baton(result, lock_id); + + return result; +} + +/* Return a baton that wraps NESTED and requests LOCK_ID as additional lock. + * + * That means, when you create a lock chain, start with the last / innermost + * lock to take out and add the first / outermost lock last. + */ +static with_lock_baton_t * +chain_lock_baton(lock_id_t lock_id, + with_lock_baton_t *nested) +{ + /* Use the same pool for batons along the lock chain. */ + apr_pool_t *lock_pool = nested->lock_pool; + with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); + + /* All locks along the chain operate on the same FS. */ + result->fs = nested->fs; + + /* Execution of this baton means acquiring the nested lock and its + execution. */ + result->body = with_lock; + result->baton = nested; + + /* Shared among all locks along the chain. */ + result->lock_pool = lock_pool; + + /* We are the new outermost lock but surely not the innermost lock. */ + result->is_inner_most_lock = FALSE; + result->is_outer_most_lock = TRUE; + nested->is_outer_most_lock = FALSE; + + /* Select mutex and lock file path depending on LOCK_ID. + Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ + init_lock_baton(result, lock_id); + + return result; +} + +svn_error_t * +svn_fs_x__with_write_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + with_lock(create_lock_baton(fs, write_lock, body, baton, + scratch_pool), + scratch_pool)); +} + +svn_error_t * +svn_fs_x__with_pack_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + with_lock(create_lock_baton(fs, pack_lock, body, baton, + scratch_pool), + scratch_pool)); +} + +svn_error_t * +svn_fs_x__with_txn_current_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + with_lock(create_lock_baton(fs, txn_lock, body, baton, + scratch_pool), + scratch_pool)); +} + +svn_error_t * +svn_fs_x__with_all_locks(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool) +{ + /* Be sure to use the correct lock ordering as documented in + fs_fs_shared_data_t. The lock chain is being created in + innermost (last to acquire) -> outermost (first to acquire) order. */ + with_lock_baton_t *lock_baton + = create_lock_baton(fs, write_lock, body, baton, scratch_pool); + + lock_baton = chain_lock_baton(pack_lock, lock_baton); + lock_baton = chain_lock_baton(txn_lock, lock_baton); + + return svn_error_trace(with_lock(lock_baton, scratch_pool)); +} + + +/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(), + which see. */ +typedef struct unlock_proto_rev_baton_t +{ + svn_fs_x__txn_id_t txn_id; + void *lockcookie; +} unlock_proto_rev_baton_t; + +/* 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 *scratch_pool) +{ + const unlock_proto_rev_baton_t *b = baton; + apr_file_t *lockfile = b->lockcookie; + svn_fs_x__shared_txn_data_t *txn = get_shared_txn(fs, b->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'"), + svn_fs_x__txn_name(b->txn_id, scratch_pool)); + if (!txn->being_written) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Can't unlock nonlocked transaction '%s'"), + svn_fs_x__txn_name(b->txn_id, scratch_pool)); + + 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'"), + svn_fs_x__txn_name(b->txn_id, scratch_pool)); + 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'"), + svn_fs_x__txn_name(b->txn_id, scratch_pool)); + + 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 SCRATCH_POOL. */ +static svn_error_t * +unlock_proto_rev(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + void *lockcookie, + apr_pool_t *scratch_pool) +{ + unlock_proto_rev_baton_t b; + + b.txn_id = txn_id; + b.lockcookie = lockcookie; + return with_txnlist_lock(fs, unlock_proto_rev_body, &b, scratch_pool); +} + +/* A structure used by get_writable_proto_rev() and + get_writable_proto_rev_body(), which see. */ +typedef struct get_writable_proto_rev_baton_t +{ + void **lockcookie; + svn_fs_x__txn_id_t txn_id; +} get_writable_proto_rev_baton_t; + +/* 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 *scratch_pool) +{ + const get_writable_proto_rev_baton_t *b = baton; + void **lockcookie = b->lockcookie; + svn_fs_x__shared_txn_data_t *txn = get_shared_txn(fs, b->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"), + svn_fs_x__txn_name(b->txn_id, scratch_pool)); + + + /* 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 + = svn_fs_x__path_txn_proto_rev_lock(fs, b->txn_id, scratch_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, + scratch_pool)); + + apr_err = apr_file_lock(lockfile, + APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK); + if (apr_err) + { + svn_error_clear(svn_io_file_close(lockfile, scratch_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"), + svn_fs_x__txn_name(b->txn_id, + scratch_pool)); + + return svn_error_wrap_apr(apr_err, + _("Can't get exclusive lock on file '%s'"), + svn_dirent_local_style(lockfile_path, + scratch_pool)); + } + + *lockcookie = lockfile; + } + + /* We've successfully locked the transaction; mark it as such. */ + txn->being_written = TRUE; + + return SVN_NO_ERROR; +} + +/* Make sure the length ACTUAL_LENGTH of the proto-revision file PROTO_REV + of transaction TXN_ID in filesystem FS matches the proto-index file. + Trim any crash / failure related extra data from the proto-rev file. + + If the prototype revision file is too short, we can't do much but bail out. + + Perform all allocations in SCRATCH_POOL. */ +static svn_error_t * +auto_truncate_proto_rev(svn_fs_t *fs, + apr_file_t *proto_rev, + apr_off_t actual_length, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + /* Determine file range covered by the proto-index so far. Note that + we always append to both file, i.e. the last index entry also + corresponds to the last addition in the rev file. */ + const char *path = svn_fs_x__path_p2l_proto_index(fs, txn_id, scratch_pool); + apr_file_t *file; + apr_off_t indexed_length; + + SVN_ERR(svn_fs_x__p2l_proto_index_open(&file, path, scratch_pool)); + SVN_ERR(svn_fs_x__p2l_proto_index_next_offset(&indexed_length, file, + scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + /* Handle mismatches. */ + if (indexed_length < actual_length) + SVN_ERR(svn_io_file_trunc(proto_rev, indexed_length, scratch_pool)); + else if (indexed_length > actual_length) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l proto index offset %s beyond proto" + "rev file size %s for TXN %s"), + apr_off_t_toa(scratch_pool, indexed_length), + apr_off_t_toa(scratch_pool, actual_length), + svn_fs_x__txn_name(txn_id, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* 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, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *pool) +{ + get_writable_proto_rev_baton_t b; + svn_error_t *err; + apr_off_t end_offset = 0; + + b.lockcookie = lockcookie; + b.txn_id = txn_id; + + SVN_ERR(with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool)); + + /* Now open the prototype revision file and seek to the end. */ + err = svn_io_file_open(file, + svn_fs_x__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) + err = svn_io_file_seek(*file, APR_END, &end_offset, pool); + + /* We don't want unused sections (such as leftovers from failed delta + stream) in our file. If we use log addressing, we would need an + index entry for the unused section and that section would need to + be all NUL by convention. So, detect and fix those cases by truncating + the protorev file. */ + if (!err) + err = auto_truncate_proto_rev(fs, *file, end_offset, txn_id, pool); + + if (err) + { + err = svn_error_compose_create( + err, + unlock_proto_rev(fs, txn_id, *lockcookie, pool)); + + *lockcookie = NULL; + } + + return svn_error_trace(err); +} + +/* 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 *scratch_pool) +{ + svn_fs_x__txn_id_t txn_id = *(const svn_fs_x__txn_id_t *)baton; + + free_shared_txn(fs, txn_id); + + return SVN_NO_ERROR; +} + +/* Purge the shared data for transaction TXN_ID in filesystem FS. + Perform all temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +purge_shared_txn(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + return with_txnlist_lock(fs, purge_shared_txn_body, &txn_id, scratch_pool); +} + + +svn_boolean_t +svn_fs_x__is_fresh_txn_root(svn_fs_x__noderev_t *noderev) +{ + /* Is it a root node? */ + if (noderev->noderev_id.number != SVN_FS_X__ITEM_INDEX_ROOT_NODE) + return FALSE; + + /* ... in a transaction? */ + if (!svn_fs_x__is_txn(noderev->noderev_id.change_set)) + return FALSE; + + /* ... with no prop change in that txn? + (Once we set a property, the prop rep will never become NULL again.) */ + if (noderev->prop_rep && svn_fs_x__is_txn(noderev->prop_rep->id.change_set)) + return FALSE; + + /* ... and no sub-tree change? + (Once we set a text, the data rep will never become NULL again.) */ + if (noderev->data_rep && svn_fs_x__is_txn(noderev->data_rep->id.change_set)) + return FALSE; + + /* Root node of a txn with no changes. */ + return TRUE; +} + +svn_error_t * +svn_fs_x__put_node_revision(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *scratch_pool) +{ + apr_file_t *noderev_file; + const svn_fs_x__id_t *id = &noderev->noderev_id; + + if (! svn_fs_x__is_txn(id->change_set)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Attempted to write to non-transaction '%s'"), + svn_fs_x__id_unparse(id, scratch_pool)->data); + + SVN_ERR(svn_io_file_open(&noderev_file, + svn_fs_x__path_txn_node_rev(fs, id, scratch_pool, + scratch_pool), + APR_WRITE | APR_CREATE | APR_TRUNCATE + | APR_BUFFERED, APR_OS_DEFAULT, scratch_pool)); + + SVN_ERR(svn_fs_x__write_noderev(svn_stream_from_aprfile2(noderev_file, TRUE, + scratch_pool), + noderev, scratch_pool)); + + SVN_ERR(svn_io_file_close(noderev_file, scratch_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 SCATCH_POOL for temporary allocations. + */ +static svn_error_t * +store_sha1_rep_mapping(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *scratch_pool) +{ + svn_fs_x__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->has_sha1) + { + apr_file_t *rep_file; + apr_int64_t txn_id + = svn_fs_x__get_txn_id(noderev->data_rep->id.change_set); + const char *file_name + = svn_fs_x__path_txn_sha1(fs, txn_id, + noderev->data_rep->sha1_digest, + scratch_pool); + svn_stringbuf_t *rep_string + = svn_fs_x__unparse_representation(noderev->data_rep, + (noderev->kind == svn_node_dir), + scratch_pool, scratch_pool); + + SVN_ERR(svn_io_file_open(&rep_file, file_name, + APR_WRITE | APR_CREATE | APR_TRUNCATE + | APR_BUFFERED, APR_OS_DEFAULT, scratch_pool)); + + SVN_ERR(svn_io_file_write_full(rep_file, rep_string->data, + rep_string->len, NULL, scratch_pool)); + + SVN_ERR(svn_io_file_close(rep_file, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +unparse_dir_entry(svn_fs_x__dirent_t *dirent, + svn_stream_t *stream, + apr_pool_t *scratch_pool) +{ + const char *val + = apr_psprintf(scratch_pool, "%s %s", + (dirent->kind == svn_node_file) ? SVN_FS_X__KIND_FILE + : SVN_FS_X__KIND_DIR, + svn_fs_x__id_unparse(&dirent->id, scratch_pool)->data); + + SVN_ERR(svn_stream_printf(stream, scratch_pool, "K %" APR_SIZE_T_FMT + "\n%s\nV %" APR_SIZE_T_FMT "\n%s\n", + strlen(dirent->name), dirent->name, + strlen(val), val)); + return SVN_NO_ERROR; +} + +/* Write the directory given as array of dirent structs in ENTRIES to STREAM. + Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +unparse_dir_entries(apr_array_header_t *entries, + svn_stream_t *stream, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__dirent_t *dirent; + + svn_pool_clear(iterpool); + dirent = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); + SVN_ERR(unparse_dir_entry(dirent, stream, iterpool)); + } + + SVN_ERR(svn_stream_printf(stream, scratch_pool, "%s\n", + SVN_HASH_TERMINATOR)); + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Return a deep copy of SOURCE and allocate it in RESULT_POOL. + */ +static svn_fs_x__change_t * +path_change_dup(const svn_fs_x__change_t *source, + apr_pool_t *result_pool) +{ + svn_fs_x__change_t *result + = apr_pmemdup(result_pool, source, sizeof(*source)); + result->path.data + = apr_pstrmemdup(result_pool, source->path.data, source->path.len); + + if (source->copyfrom_path) + result->copyfrom_path = apr_pstrdup(result_pool, source->copyfrom_path); + + return result; +} + +/* Merge the internal-use-only CHANGE into a hash of public-FS + svn_fs_x__change_t CHANGED_PATHS, collapsing multiple changes into a + single summarical (is that real word?) change per path. DELETIONS is + also a path->svn_fs_x__change_t hash and contains all the deletions + that got turned into a replacement. */ +static svn_error_t * +fold_change(apr_hash_t *changed_paths, + apr_hash_t *deletions, + const svn_fs_x__change_t *change) +{ + apr_pool_t *pool = apr_hash_pool_get(changed_paths); + svn_fs_x__change_t *old_change, *new_change; + const svn_string_t *path = &change->path; + + if ((old_change = apr_hash_get(changed_paths, path->data, 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 unused node revision IDs in the + `reset' case. */ + if ((! svn_fs_x__id_used(&change->noderev_id)) + && (change->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 (svn_fs_x__id_used(&change->noderev_id) + && (!svn_fs_x__id_eq(&old_change->noderev_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->change_kind == svn_fs_path_change_replace) + || (change->change_kind == svn_fs_path_change_reset) + || (change->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->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->change_kind) + { + case svn_fs_path_change_reset: + /* A reset here will simply remove the path change from the + hash. */ + apr_hash_set(changed_paths, path->data, path->len, 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. (The caller will delete any child paths.) */ + apr_hash_set(changed_paths, path->data, path->len, NULL); + } + else if (old_change->change_kind == svn_fs_path_change_replace) + { + /* A deleting a 'replace' restore the original deletion. */ + new_change = apr_hash_get(deletions, path->data, path->len); + SVN_ERR_ASSERT(new_change); + apr_hash_set(changed_paths, path->data, path->len, new_change); + } + else + { + /* A deletion overrules a previous change (modify). */ + new_change = path_change_dup(change, pool); + apr_hash_set(changed_paths, path->data, path->len, new_change); + } + 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. Remember the original + deletion such that we are able to delete this path again + (the replacement may have changed node kind and id). */ + new_change = path_change_dup(change, pool); + new_change->change_kind = svn_fs_path_change_replace; + + apr_hash_set(changed_paths, path->data, path->len, new_change); + + /* Remember the original change. + * Make sure to allocate the hash key in a durable pool. */ + apr_hash_set(deletions, + apr_pstrmemdup(apr_hash_pool_get(deletions), + path->data, path->len), + path->len, old_change); + break; + + case svn_fs_path_change_modify: + default: + /* If the new change modifies some attribute of the node, set + the corresponding flag, whether it already was set or not. + Note: We do not reset a flag to FALSE if a change is undone. */ + if (change->text_mod) + old_change->text_mod = TRUE; + if (change->prop_mod) + old_change->prop_mod = TRUE; + if (change->mergeinfo_mod == svn_tristate_true) + old_change->mergeinfo_mod = svn_tristate_true; + break; + } + } + else + { + /* Add this path. The API makes no guarantees that this (new) key + will not be retained. Thus, we copy the key into the target pool + to ensure a proper lifetime. */ + new_change = path_change_dup(change, pool); + apr_hash_set(changed_paths, new_change->path.data, + new_change->path.len, new_change); + } + + return SVN_NO_ERROR; +} + +/* Baton type to be used with process_changes(). */ +typedef struct process_changes_baton_t +{ + /* Folded list of path changes. */ + apr_hash_t *changed_paths; + + /* Path changes that are deletions and have been turned into + replacements. If those replacements get deleted again, this + container contains the record that we have to revert to. */ + apr_hash_t *deletions; +} process_changes_baton_t; + +/* An implementation of svn_fs_x__change_receiver_t. + Examine all the changed path entries in CHANGES and store them in + *CHANGED_PATHS. Folding is done to remove redundant or unnecessary + data. Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +process_changes(void *baton_p, + svn_fs_x__change_t *change, + apr_pool_t *scratch_pool) +{ + process_changes_baton_t *baton = baton_p; + + SVN_ERR(fold_change(baton->changed_paths, baton->deletions, change)); + + /* 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->change_kind == svn_fs_path_change_delete) + || (change->change_kind == svn_fs_path_change_replace)) + { + 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 path_len = change->path.len; + apr_ssize_t min_child_len = path_len == 0 + ? 1 + : change->path.data[path_len-1] == '/' + ? path_len + 1 + : 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(scratch_pool, baton->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_fspath__skip_ancestor only if there is a chance that + this is actually a sub-path. + */ + if (klen >= min_child_len) + { + const char *child; + + child = svn_fspath__skip_ancestor(change->path.data, path); + if (child && child[0] != '\0') + apr_hash_set(baton->changed_paths, path, klen, NULL); + } + } + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__txn_changes_fetch(apr_hash_t **changed_paths_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *pool) +{ + apr_file_t *file; + apr_hash_t *changed_paths = apr_hash_make(pool); + apr_pool_t *scratch_pool = svn_pool_create(pool); + process_changes_baton_t baton; + + baton.changed_paths = changed_paths; + baton.deletions = apr_hash_make(scratch_pool); + + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_txn_changes(fs, txn_id, scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool)); + + SVN_ERR(svn_fs_x__read_changes_incrementally( + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + process_changes, &baton, + scratch_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. + Temporary allocations are from SCRATCH_POOL. */ +static svn_error_t * +create_new_txn_noderev_from_rev(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_fs_x__id_t *src, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, src, scratch_pool, + scratch_pool)); + + /* This must be a root node. */ + SVN_ERR_ASSERT( noderev->node_id.number == 0 + && noderev->copy_id.number == 0); + + if (svn_fs_x__is_txn(noderev->noderev_id.change_set)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Copying from transactions not allowed")); + + noderev->predecessor_id = noderev->noderev_id; + noderev->predecessor_count++; + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + + /* For the transaction root, the copyroot never changes. */ + svn_fs_x__init_txn_root(&noderev->noderev_id, txn_id); + + return svn_fs_x__put_node_revision(fs, noderev, scratch_pool); +} + +/* A structure used by get_and_increment_txn_key_body(). */ +typedef struct get_and_increment_txn_key_baton_t +{ + svn_fs_t *fs; + apr_uint64_t txn_number; +} get_and_increment_txn_key_baton_t; + +/* 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 *scratch_pool) +{ + get_and_increment_txn_key_baton_t *cb = baton; + const char *txn_current_filename = svn_fs_x__path_txn_current(cb->fs, + scratch_pool); + const char *tmp_filename; + char new_id_str[SVN_INT64_BUFFER_SIZE]; + + svn_stringbuf_t *buf; + SVN_ERR(svn_fs_x__read_content(&buf, txn_current_filename, scratch_pool)); + + /* remove trailing newlines */ + cb->txn_number = svn__base36toui64(NULL, buf->data); + + /* Increment the key and add a trailing \n to the string so the + txn-current file has a newline in it. */ + SVN_ERR(svn_io_write_unique(&tmp_filename, + svn_dirent_dirname(txn_current_filename, + scratch_pool), + new_id_str, + svn__ui64tobase36(new_id_str, cb->txn_number+1), + svn_io_file_del_none, scratch_pool)); + SVN_ERR(svn_fs_x__move_into_place(tmp_filename, txn_current_filename, + txn_current_filename, scratch_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 and *TXN_ID. 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_x__txn_id_t *txn_id, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + get_and_increment_txn_key_baton_t 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.fs = fs; + SVN_ERR(svn_fs_x__with_txn_current_lock(fs, + get_and_increment_txn_key_body, + &cb, + scratch_pool)); + *txn_id = cb.txn_number; + + *id_p = svn_fs_x__txn_name(*txn_id, result_pool); + txn_dir = svn_fs_x__path_txn_dir(fs, *txn_id, scratch_pool); + + return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, scratch_pool); +} + +/* Create a new transaction in filesystem FS, based on revision REV, + and store it in *TXN_P, allocated in RESULT_POOL. Allocate necessary + temporaries from SCRATCH_POOL. */ +static svn_error_t * +create_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_txn_t *txn; + fs_txn_data_t *ftd; + svn_fs_x__id_t root_id; + + txn = apr_pcalloc(result_pool, sizeof(*txn)); + ftd = apr_pcalloc(result_pool, sizeof(*ftd)); + + /* Valid revision number? */ + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + + /* Get the txn_id. */ + SVN_ERR(create_txn_dir(&txn->id, &ftd->txn_id, fs, result_pool, + scratch_pool)); + + txn->fs = fs; + txn->base_rev = rev; + + txn->vtable = &txn_vtable; + txn->fsap_data = ftd; + *txn_p = txn; + + /* Create a new root node for this transaction. */ + svn_fs_x__init_rev_root(&root_id, rev); + SVN_ERR(create_new_txn_noderev_from_rev(fs, ftd->txn_id, &root_id, + scratch_pool)); + + /* Create an empty rev file. */ + SVN_ERR(svn_io_file_create_empty( + svn_fs_x__path_txn_proto_rev(fs, ftd->txn_id, scratch_pool), + scratch_pool)); + + /* Create an empty rev-lock file. */ + SVN_ERR(svn_io_file_create_empty( + svn_fs_x__path_txn_proto_rev_lock(fs, ftd->txn_id, scratch_pool), + scratch_pool)); + + /* Create an empty changes file. */ + SVN_ERR(svn_io_file_create_empty( + svn_fs_x__path_txn_changes(fs, ftd->txn_id, scratch_pool), + scratch_pool)); + + /* Create the next-ids file. */ + SVN_ERR(svn_io_file_create( + svn_fs_x__path_txn_next_ids(fs, ftd->txn_id, scratch_pool), + "0 0\n", scratch_pool)); + + return SVN_NO_ERROR; +} + +/* 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, + svn_fs_x__txn_id_t 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 == SVN_FS_X__INVALID_TXN_ID) + 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, + svn_fs_x__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); +} + +/* Save the property list PROPS as the revprops for transaction TXN_ID + in FS. Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +set_txn_proplist(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_hash_t *props, + svn_boolean_t final, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *buf; + svn_stream_t *stream; + + /* Write out the new file contents to BUF. */ + buf = svn_stringbuf_create_ensure(1024, scratch_pool); + stream = svn_stream_from_stringbuf(buf, scratch_pool); + SVN_ERR(svn_hash_write2(props, stream, SVN_HASH_TERMINATOR, scratch_pool)); + SVN_ERR(svn_stream_close(stream)); + + /* Open the transaction properties file and write new contents to it. */ + SVN_ERR(svn_io_write_atomic((final + ? svn_fs_x__path_txn_props_final(fs, txn_id, + scratch_pool) + : svn_fs_x__path_txn_props(fs, txn_id, + scratch_pool)), + buf->data, buf->len, + NULL /* copy_perms_path */, scratch_pool)); + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__change_txn_prop(svn_fs_txn_t *txn, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *props = apr_array_make(scratch_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_x__change_txn_props(txn, props, scratch_pool); +} + +svn_error_t * +svn_fs_x__change_txn_props(svn_fs_txn_t *txn, + const apr_array_header_t *props, + apr_pool_t *scratch_pool) +{ + fs_txn_data_t *ftd = txn->fsap_data; + apr_hash_t *txn_prop = apr_hash_make(scratch_pool); + int i; + svn_error_t *err; + + err = get_txn_proplist(txn_prop, txn->fs, ftd->txn_id, scratch_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); + + if (svn_hash_gets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE) + && !strcmp(prop->name, SVN_PROP_REVISION_DATE)) + svn_hash_sets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE, + svn_string_create("1", scratch_pool)); + + 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. */ + SVN_ERR(set_txn_proplist(txn->fs, ftd->txn_id, txn_prop, FALSE, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__get_txn(svn_fs_x__transaction_t **txn_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *pool) +{ + svn_fs_x__transaction_t *txn; + svn_fs_x__noderev_t *noderev; + svn_fs_x__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)); + svn_fs_x__init_txn_root(&root_id, txn_id); + + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, &root_id, pool, pool)); + + txn->base_rev = svn_fs_x__get_revnum(noderev->predecessor_id.change_set); + txn->copies = NULL; + + *txn_p = txn; + + return SVN_NO_ERROR; +} + +/* If it is supported by the format of file system FS, store the (ITEM_INDEX, + * OFFSET) pair in the log-to-phys proto index file of transaction TXN_ID. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +store_l2p_index_entry(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_off_t offset, + apr_uint64_t item_index, + apr_pool_t *scratch_pool) +{ + const char *path = svn_fs_x__path_l2p_proto_index(fs, txn_id, scratch_pool); + apr_file_t *file; + SVN_ERR(svn_fs_x__l2p_proto_index_open(&file, path, scratch_pool)); + SVN_ERR(svn_fs_x__l2p_proto_index_add_entry(file, offset, 0, + item_index, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* If it is supported by the format of file system FS, store ENTRY in the + * phys-to-log proto index file of transaction TXN_ID. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +store_p2l_index_entry(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + const char *path = svn_fs_x__path_p2l_proto_index(fs, txn_id, scratch_pool); + apr_file_t *file; + SVN_ERR(svn_fs_x__p2l_proto_index_open(&file, path, scratch_pool)); + SVN_ERR(svn_fs_x__p2l_proto_index_add_entry(file, entry, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Allocate an item index in the transaction TXN_ID of file system FS and + * return it in *ITEM_INDEX. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +allocate_item_index(apr_uint64_t *item_index, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + apr_file_t *file; + char buffer[SVN_INT64_BUFFER_SIZE] = { 0 }; + svn_boolean_t eof = FALSE; + apr_size_t to_write; + apr_size_t read; + apr_off_t offset = 0; + + /* read number */ + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_txn_item_index(fs, txn_id, + scratch_pool), + APR_READ | APR_WRITE + | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, scratch_pool)); + SVN_ERR(svn_io_file_read_full2(file, buffer, sizeof(buffer)-1, + &read, &eof, scratch_pool)); + if (read) + SVN_ERR(svn_cstring_atoui64(item_index, buffer)); + else + *item_index = SVN_FS_X__ITEM_INDEX_FIRST_USER; + + /* increment it */ + to_write = svn__ui64toa(buffer, *item_index + 1); + + /* write it back to disk */ + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool)); + SVN_ERR(svn_io_file_write_full(file, buffer, to_write, NULL, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + 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 + SCRATCH_POOL. */ +static svn_error_t * +write_next_ids(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_uint64_t node_id, + apr_uint64_t copy_id, + apr_pool_t *scratch_pool) +{ + apr_file_t *file; + char buffer[2 * SVN_INT64_BUFFER_SIZE + 2]; + char *p = buffer; + + p += svn__ui64tobase36(p, node_id); + *(p++) = ' '; + p += svn__ui64tobase36(p, copy_id); + *(p++) = '\n'; + *(p++) = '\0'; + + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_txn_next_ids(fs, txn_id, + scratch_pool), + APR_WRITE | APR_TRUNCATE, + APR_OS_DEFAULT, scratch_pool)); + SVN_ERR(svn_io_file_write_full(file, buffer, p - buffer, NULL, + scratch_pool)); + return svn_io_file_close(file, scratch_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 temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +read_next_ids(apr_uint64_t *node_id, + apr_uint64_t *copy_id, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *buf; + const char *str; + SVN_ERR(svn_fs_x__read_content(&buf, + svn_fs_x__path_txn_next_ids(fs, txn_id, + scratch_pool), + scratch_pool)); + + /* Parse this into two separate strings. */ + + str = buf->data; + *node_id = svn__base36toui64(&str, str); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("next-id file corrupt")); + + ++str; + *copy_id = svn__base36toui64(&str, str); + if (*str != '\n') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("next-id file corrupt")); + + 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 temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +get_new_txn_node_id(svn_fs_x__id_t *node_id_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + apr_uint64_t node_id, copy_id; + + /* First read in the current next-ids file. */ + SVN_ERR(read_next_ids(&node_id, ©_id, fs, txn_id, scratch_pool)); + + node_id_p->change_set = svn_fs_x__change_set_by_txn(txn_id); + node_id_p->number = node_id; + + SVN_ERR(write_next_ids(fs, txn_id, ++node_id, copy_id, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__reserve_copy_id(svn_fs_x__id_t *copy_id_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + apr_uint64_t node_id, copy_id; + + /* First read in the current next-ids file. */ + SVN_ERR(read_next_ids(&node_id, ©_id, fs, txn_id, scratch_pool)); + + copy_id_p->change_set = svn_fs_x__change_set_by_txn(txn_id); + copy_id_p->number = copy_id; + + SVN_ERR(write_next_ids(fs, txn_id, node_id, ++copy_id, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__create_node(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + /* Get a new node-id for this node. */ + SVN_ERR(get_new_txn_node_id(&noderev->node_id, fs, txn_id, scratch_pool)); + + /* Assign copy-id. */ + noderev->copy_id = *copy_id; + + /* Noderev-id = Change set and item number within this change set. */ + noderev->noderev_id.change_set = svn_fs_x__change_set_by_txn(txn_id); + SVN_ERR(allocate_item_index(&noderev->noderev_id.number, fs, txn_id, + scratch_pool)); + + SVN_ERR(svn_fs_x__put_node_revision(fs, noderev, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__purge_txn(svn_fs_t *fs, + const char *txn_id_str, + apr_pool_t *scratch_pool) +{ + svn_fs_x__txn_id_t txn_id; + SVN_ERR(svn_fs_x__txn_by_name(&txn_id, txn_id_str)); + + /* Remove the shared transaction object associated with this transaction. */ + SVN_ERR(purge_shared_txn(fs, txn_id, scratch_pool)); + /* Remove the directory associated with this transaction. */ + SVN_ERR(svn_io_remove_dir2(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool), + FALSE, NULL, NULL, scratch_pool)); + + /* 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( + svn_fs_x__path_txn_proto_rev(fs, txn_id, scratch_pool), + TRUE, scratch_pool)); + SVN_ERR(svn_io_remove_file2( + svn_fs_x__path_txn_proto_rev_lock(fs, txn_id, scratch_pool), + TRUE, scratch_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__abort_txn(svn_fs_txn_t *txn, + apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_fs__check_fs(txn->fs, TRUE)); + + /* Now, purge the transaction. */ + SVN_ERR_W(svn_fs_x__purge_txn(txn->fs, txn->id, scratch_pool), + apr_psprintf(scratch_pool, _("Transaction '%s' cleanup failed"), + txn->id)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__set_entry(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_fs_x__noderev_t *parent_noderev, + const char *name, + const svn_fs_x__id_t *id, + svn_node_kind_t kind, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_fs_x__representation_t *rep = parent_noderev->data_rep; + const char *filename + = svn_fs_x__path_txn_node_children(fs, &parent_noderev->noderev_id, + scratch_pool, scratch_pool); + apr_file_t *file; + svn_stream_t *out; + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + if (!rep || !svn_fs_x__is_txn(rep->id.change_set)) + { + apr_array_header_t *entries; + + /* Before we can modify the directory, we need to dump its old + contents into a mutable representation file. */ + SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, parent_noderev, + subpool, subpool)); + SVN_ERR(svn_io_file_open(&file, filename, + APR_WRITE | APR_CREATE | APR_BUFFERED, + APR_OS_DEFAULT, scratch_pool)); + out = svn_stream_from_aprfile2(file, TRUE, scratch_pool); + SVN_ERR(unparse_dir_entries(entries, out, subpool)); + + svn_pool_clear(subpool); + + /* Provide the parent with a data rep if it had none before + (directories so far empty). */ + if (!rep) + { + rep = apr_pcalloc(result_pool, sizeof(*rep)); + parent_noderev->data_rep = rep; + } + + /* Mark the node-rev's data rep as mutable. */ + rep->id.change_set = svn_fs_x__change_set_by_txn(txn_id); + rep->id.number = SVN_FS_X__ITEM_INDEX_UNUSED; + + /* Save noderev to disk. */ + SVN_ERR(svn_fs_x__put_node_revision(fs, parent_noderev, subpool)); + } + 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, scratch_pool)); + out = svn_stream_from_aprfile2(file, TRUE, scratch_pool); + } + + /* update directory cache */ + { + /* build parameters: (name, new entry) pair */ + const svn_fs_x__id_t *key = &(parent_noderev->noderev_id); + 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->dir_cache, key, + svn_fs_x__replace_dir_entry, &baton, + subpool)); + } + svn_pool_clear(subpool); + + /* Append an incremental hash entry for the entry change. */ + if (id) + { + svn_fs_x__dirent_t entry; + entry.name = name; + entry.id = *id; + entry.kind = kind; + + SVN_ERR(unparse_dir_entry(&entry, out, subpool)); + } + 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; +} + +svn_error_t * +svn_fs_x__add_change(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const char *path, + const svn_fs_x__id_t *id, + svn_fs_path_change_kind_t change_kind, + svn_boolean_t text_mod, + svn_boolean_t prop_mod, + svn_boolean_t mergeinfo_mod, + svn_node_kind_t node_kind, + svn_revnum_t copyfrom_rev, + const char *copyfrom_path, + apr_pool_t *scratch_pool) +{ + apr_file_t *file; + svn_fs_x__change_t change; + apr_hash_t *changes = apr_hash_make(scratch_pool); + + /* Not using APR_BUFFERED to append change in one atomic write operation. */ + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_txn_changes(fs, txn_id, + scratch_pool), + APR_APPEND | APR_WRITE | APR_CREATE, + APR_OS_DEFAULT, scratch_pool)); + + change.path.data = path; + change.path.len = strlen(path); + change.noderev_id = *id; + change.change_kind = change_kind; + change.text_mod = text_mod; + change.prop_mod = prop_mod; + change.mergeinfo_mod = mergeinfo_mod ? svn_tristate_true + : svn_tristate_false; + change.node_kind = node_kind; + change.copyfrom_known = TRUE; + change.copyfrom_rev = copyfrom_rev; + if (copyfrom_path) + change.copyfrom_path = apr_pstrdup(scratch_pool, copyfrom_path); + + svn_hash_sets(changes, path, &change); + SVN_ERR(svn_fs_x__write_changes(svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + fs, changes, FALSE, scratch_pool)); + + return svn_io_file_close(file, scratch_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. */ +typedef struct rep_write_baton_t +{ + /* 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. */ + svn_fs_x__noderev_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; + + /* Receives the low-level checksum when closing REP_STREAM. */ + apr_uint32_t fnv1a_checksum; + + /* Local pool, available for allocations that must remain valid as long + as this baton is used but may be cleaned up immediately afterwards. */ + apr_pool_t *local_pool; + + /* Outer / result pool. */ + apr_pool_t *result_pool; +} rep_write_baton_t; + +/* Handler for the write method of the representation writable stream. + BATON is a rep_write_baton_t, 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) +{ + rep_write_baton_t *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; + + return svn_stream_write(b->delta_stream, data, len); +} + +/* Set *SPANNED to the number of shards touched when walking WALK steps on + * NODEREV's predecessor chain in FS. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +shards_spanned(int *spanned, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + int walk, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + int shard_size = ffd->max_files_per_dir; + apr_pool_t *iterpool; + + int count = walk ? 1 : 0; /* The start of a walk already touches a shard. */ + svn_revnum_t shard, last_shard = ffd->youngest_rev_cache / shard_size; + iterpool = svn_pool_create(scratch_pool); + while (walk-- && noderev->predecessor_count) + { + svn_fs_x__id_t id = noderev->predecessor_id; + + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, &id, scratch_pool, + iterpool)); + shard = svn_fs_x__get_revnum(id.change_set) / shard_size; + if (shard != last_shard) + { + ++count; + last_shard = shard; + } + } + svn_pool_destroy(iterpool); + + *spanned = count; + return SVN_NO_ERROR; +} + +/* 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(svn_fs_x__representation_t **rep, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + svn_boolean_t props, + apr_pool_t *pool) +{ + /* The zero-based index (counting from the "oldest" end), along NODEREVs line + * predecessors, of the node-rev we will use as delta base. */ + int count; + /* The length of the linear part of a delta chain. (Delta chains use + * skip-delta bits for the high-order bits and are linear in the low-order + * bits.) */ + int walk; + svn_fs_x__noderev_t *base; + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool; + + /* If we have no predecessors, or that one is empty, 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); + + /* 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. */ + walk = noderev->predecessor_count - count; + if (walk > (int)ffd->max_deltification_walk) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* 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. */ + if (walk < (int)ffd->max_linear_deltification) + { + int shards; + SVN_ERR(shards_spanned(&shards, fs, noderev, walk, pool)); + + /* We also don't want the linear deltification to span more shards + than if deltas we used in a simple skip-delta scheme. */ + if ((1 << (--shards)) <= walk) + count = noderev->predecessor_count - 1; + } + + /* 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; + iterpool = svn_pool_create(pool); + while ((count++) < noderev->predecessor_count) + { + svn_fs_x__id_t id = noderev->predecessor_id; + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_x__get_node_revision(&base, fs, &id, pool, iterpool)); + } + svn_pool_destroy(iterpool); + + /* return a suitable base representation */ + *rep = props ? base->prop_rep : base->data_rep; + + /* if we encountered a shared rep, its parent chain may be different + * from the node-rev parent chain. */ + if (*rep) + { + int chain_length = 0; + int shard_count = 0; + + /* Very short rep bases are simply not worth it as we are unlikely + * to re-coup the deltification space overhead of 20+ bytes. */ + svn_filesize_t rep_size = (*rep)->expanded_size + ? (*rep)->expanded_size + : (*rep)->size; + if (rep_size < 64) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* Check whether the length of the deltification chain is acceptable. + * Otherwise, shared reps may form a non-skipping delta chain in + * extreme cases. */ + SVN_ERR(svn_fs_x__rep_chain_length(&chain_length, &shard_count, + *rep, fs, pool)); + + /* Some reasonable limit, depending on how acceptable longer linear + * chains are in this repo. Also, allow for some minimal chain. */ + if (chain_length >= 2 * (int)ffd->max_linear_deltification + 2) + *rep = NULL; + else + /* To make it worth opening additional shards / pack files, we + * require that the reps have a certain minimal size. To deltify + * against a rep in different shard, the lower limit is 512 bytes + * and doubles with every extra shard to visit along the delta + * chain. */ + if ( shard_count > 1 + && ((svn_filesize_t)128 << shard_count) >= rep_size) + *rep = NULL; + } + + 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) +{ + svn_error_t *err; + rep_write_baton_t *b = data; + svn_fs_x__txn_id_t txn_id + = svn_fs_x__get_txn_id(b->noderev->noderev_id.change_set); + + /* Truncate and close the protorevfile. */ + err = svn_io_file_trunc(b->file, b->rep_offset, b->local_pool); + err = svn_error_compose_create(err, svn_io_file_close(b->file, + b->local_pool)); + + /* Remove our lock regardless of any preceding 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->local_pool)); + if (err) + { + apr_status_t rc = err->apr_err; + svn_error_clear(err); + return rc; + } + + return APR_SUCCESS; +} + +/* Get a rep_write_baton_t, allocated from RESULT_POOL, and store it in + WB_P for the representation indicated by NODEREV in filesystem FS. + Only appropriate for file contents, not for props or directory contents. + */ +static svn_error_t * +rep_write_get_baton(rep_write_baton_t **wb_p, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + rep_write_baton_t *b; + apr_file_t *file; + svn_fs_x__representation_t *base_rep; + svn_stream_t *source; + svn_txdelta_window_handler_t wh; + void *whb; + int diff_version = 1; + svn_fs_x__rep_header_t header = { 0 }; + svn_fs_x__txn_id_t txn_id + = svn_fs_x__get_txn_id(noderev->noderev_id.change_set); + + b = apr_pcalloc(result_pool, sizeof(*b)); + + b->sha1_checksum_ctx = svn_checksum_ctx_create(svn_checksum_sha1, + result_pool); + b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, + result_pool); + + b->fs = fs; + b->result_pool = result_pool; + b->local_pool = svn_pool_create(result_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, txn_id, + b->local_pool)); + + b->file = file; + b->rep_stream = svn_checksum__wrap_write_stream_fnv1a_32x4( + &b->fnv1a_checksum, + svn_stream_from_aprfile2(file, TRUE, + b->local_pool), + b->local_pool); + + SVN_ERR(svn_fs_x__get_file_offset(&b->rep_offset, file, b->local_pool)); + + /* Get the base for this delta. */ + SVN_ERR(choose_delta_base(&base_rep, fs, noderev, FALSE, b->local_pool)); + SVN_ERR(svn_fs_x__get_contents(&source, fs, base_rep, TRUE, + b->local_pool)); + + /* Write out the rep header. */ + if (base_rep) + { + header.base_revision = svn_fs_x__get_revnum(base_rep->id.change_set); + header.base_item_index = base_rep->id.number; + header.base_length = base_rep->size; + header.type = svn_fs_x__rep_delta; + } + else + { + header.type = svn_fs_x__rep_self_delta; + } + SVN_ERR(svn_fs_x__write_rep_header(&header, b->rep_stream, + b->local_pool)); + + /* Now determine the offset of the actual svndiff data. */ + SVN_ERR(svn_fs_x__get_file_offset(&b->delta_start, file, + b->local_pool)); + + /* Cleanup in case something goes wrong. */ + apr_pool_cleanup_register(b->local_pool, b, rep_write_cleanup, + apr_pool_cleanup_null); + + /* Prepare to write the svndiff data. */ + svn_txdelta_to_svndiff3(&wh, + &whb, + svn_stream_disown(b->rep_stream, b->result_pool), + diff_version, + ffd->delta_compression_level, + result_pool); + + b->delta_stream = svn_txdelta_target_push(wh, whb, source, + b->result_pool); + + *wb_p = b; + + return SVN_NO_ERROR; +} + +/* For REP->SHA1_CHECKSUM, 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 + svn_fs_x__representation_t*), otherwise pass in NULL for REPS_HASH. + Use RESULT_POOL for *OLD_REP allocations and SCRATCH_POOL for temporaries. + The lifetime of *OLD_REP is limited by both, RESULT_POOL and REP lifetime. + */ +static svn_error_t * +get_shared_rep(svn_fs_x__representation_t **old_rep, + svn_fs_t *fs, + svn_fs_x__representation_t *rep, + apr_hash_t *reps_hash, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_fs_x__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_digest, + APR_SHA1_DIGESTSIZE); + + /* If we haven't found anything yet, try harder and consult our DB. */ + if (*old_rep == NULL) + { + svn_checksum_t checksum; + checksum.digest = rep->sha1_digest; + checksum.kind = svn_checksum_sha1; + err = svn_fs_x__get_rep_reference(old_rep, fs, &checksum, result_pool, + scratch_pool); + + /* ### Other error codes that we shouldn't mask out? */ + if (err == SVN_NO_ERROR) + { + if (*old_rep) + SVN_ERR(svn_fs_x__check_rep(*old_rep, fs, scratch_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 && svn_fs_x__is_txn(rep->id.change_set)) + { + svn_node_kind_t kind; + const char *file_name + = svn_fs_x__path_txn_sha1(fs, + svn_fs_x__get_txn_id(rep->id.change_set), + rep->sha1_digest, scratch_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, scratch_pool)); + if (kind == svn_node_file) + { + svn_stringbuf_t *rep_string; + SVN_ERR(svn_stringbuf_from_file2(&rep_string, file_name, + scratch_pool)); + SVN_ERR(svn_fs_x__parse_representation(old_rep, rep_string, + result_pool, scratch_pool)); + } + } + + /* Add information that is missing in the cached data. */ + if (*old_rep) + { + /* Use the old rep for this content. */ + memcpy((*old_rep)->md5_digest, rep->md5_digest, sizeof(rep->md5_digest)); + } + + return SVN_NO_ERROR; +} + +/* Copy the hash sum calculation results from MD5_CTX, SHA1_CTX into REP. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +digests_final(svn_fs_x__representation_t *rep, + const svn_checksum_ctx_t *md5_ctx, + const svn_checksum_ctx_t *sha1_ctx, + apr_pool_t *scratch_pool) +{ + svn_checksum_t *checksum; + + SVN_ERR(svn_checksum_final(&checksum, md5_ctx, scratch_pool)); + memcpy(rep->md5_digest, checksum->digest, svn_checksum_size(checksum)); + SVN_ERR(svn_checksum_final(&checksum, sha1_ctx, scratch_pool)); + rep->has_sha1 = checksum != NULL; + if (rep->has_sha1) + memcpy(rep->sha1_digest, checksum->digest, svn_checksum_size(checksum)); + + return SVN_NO_ERROR; +} + +/* Close handler for the representation write stream. BATON is a + rep_write_baton_t. 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) +{ + rep_write_baton_t *b = baton; + svn_fs_x__representation_t *rep; + svn_fs_x__representation_t *old_rep; + apr_off_t offset; + apr_int64_t txn_id; + + rep = apr_pcalloc(b->result_pool, sizeof(*rep)); + + /* Close our delta stream so the last bits of svndiff are written + out. */ + SVN_ERR(svn_stream_close(b->delta_stream)); + + /* Determine the length of the svndiff data. */ + SVN_ERR(svn_fs_x__get_file_offset(&offset, b->file, b->local_pool)); + rep->size = offset - b->delta_start; + + /* Fill in the rest of the representation field. */ + rep->expanded_size = b->rep_size; + txn_id = svn_fs_x__get_txn_id(b->noderev->noderev_id.change_set); + rep->id.change_set = svn_fs_x__change_set_by_txn(txn_id); + + /* Finalize the checksum. */ + SVN_ERR(digests_final(rep, b->md5_checksum_ctx, b->sha1_checksum_ctx, + b->result_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->result_pool, + b->local_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->local_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")); + SVN_ERR(allocate_item_index(&rep->id.number, b->fs, txn_id, + b->local_pool)); + SVN_ERR(store_l2p_index_entry(b->fs, txn_id, b->rep_offset, + rep->id.number, b->local_pool)); + + b->noderev->data_rep = rep; + } + + SVN_ERR(svn_stream_close(b->rep_stream)); + + /* Remove cleanup callback. */ + apr_pool_cleanup_kill(b->local_pool, b, rep_write_cleanup); + + /* Write out the new node-rev information. */ + SVN_ERR(svn_fs_x__put_node_revision(b->fs, b->noderev, b->local_pool)); + if (!old_rep) + { + svn_fs_x__p2l_entry_t entry; + svn_fs_x__id_t noderev_id; + noderev_id.change_set = SVN_FS_X__INVALID_CHANGE_SET; + noderev_id.number = rep->id.number; + + entry.offset = b->rep_offset; + SVN_ERR(svn_fs_x__get_file_offset(&offset, b->file, b->local_pool)); + entry.size = offset - b->rep_offset; + entry.type = SVN_FS_X__ITEM_TYPE_FILE_REP; + entry.item_count = 1; + entry.items = &noderev_id; + entry.fnv1_checksum = b->fnv1a_checksum; + + SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->local_pool)); + SVN_ERR(store_p2l_index_entry(b->fs, txn_id, &entry, b->local_pool)); + } + + SVN_ERR(svn_io_file_close(b->file, b->local_pool)); + SVN_ERR(unlock_proto_rev(b->fs, txn_id, b->lockcookie, b->local_pool)); + svn_pool_destroy(b->local_pool); + + return SVN_NO_ERROR; +} + +/* Store a writable stream in *CONTENTS_P, allocated in RESULT_POOL, that + will receive all data written and store it as the file data representation + referenced by NODEREV in filesystem FS. 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, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool) +{ + rep_write_baton_t *wb; + + if (! svn_fs_x__is_txn(noderev->noderev_id.change_set)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Attempted to write to non-transaction '%s'"), + svn_fs_x__id_unparse(&noderev->noderev_id, + result_pool)->data); + + SVN_ERR(rep_write_get_baton(&wb, fs, noderev, result_pool)); + + *contents_p = svn_stream_create(wb, result_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_x__set_contents(svn_stream_t **stream, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_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, result_pool); +} + +svn_error_t * +svn_fs_x__create_successor(svn_fs_t *fs, + svn_fs_x__noderev_t *new_noderev, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + new_noderev->copy_id = *copy_id; + new_noderev->noderev_id.change_set = svn_fs_x__change_set_by_txn(txn_id); + SVN_ERR(allocate_item_index(&new_noderev->noderev_id.number, fs, txn_id, + scratch_pool)); + + if (! new_noderev->copyroot_path) + { + new_noderev->copyroot_path + = apr_pstrdup(scratch_pool, new_noderev->created_path); + new_noderev->copyroot_rev + = svn_fs_x__get_revnum(new_noderev->noderev_id.change_set); + } + + SVN_ERR(svn_fs_x__put_node_revision(fs, new_noderev, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__set_proplist(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_hash_t *proplist, + apr_pool_t *scratch_pool) +{ + const svn_fs_x__id_t *id = &noderev->noderev_id; + const char *filename = svn_fs_x__path_txn_node_props(fs, id, scratch_pool, + scratch_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, scratch_pool)); + out = svn_stream_from_aprfile2(file, TRUE, scratch_pool); + SVN_ERR(svn_hash_write2(proplist, out, SVN_HASH_TERMINATOR, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + /* Mark the node-rev's prop rep as mutable, if not already done. */ + if (!noderev->prop_rep + || svn_fs_x__is_revision(noderev->prop_rep->id.change_set)) + { + svn_fs_x__txn_id_t txn_id + = svn_fs_x__get_txn_id(noderev->noderev_id.change_set); + noderev->prop_rep = apr_pcalloc(scratch_pool, sizeof(*noderev->prop_rep)); + noderev->prop_rep->id.change_set = id->change_set; + SVN_ERR(allocate_item_index(&noderev->prop_rep->id.number, fs, + txn_id, scratch_pool)); + SVN_ERR(svn_fs_x__put_node_revision(fs, noderev, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* This baton is used by the stream created for write_container_rep. */ +typedef struct write_container_baton_t +{ + svn_stream_t *stream; + + apr_size_t size; + + svn_checksum_ctx_t *md5_ctx; + svn_checksum_ctx_t *sha1_ctx; +} write_container_baton_t; + +/* The handler for the write_container_rep stream. BATON is a + write_container_baton_t, DATA has the data to write and *LEN is the number + of bytes to write. */ +static svn_error_t * +write_container_handler(void *baton, + const char *data, + apr_size_t *len) +{ + write_container_baton_t *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; +} + +/* Callback function type. Write the data provided by BATON into STREAM. */ +typedef svn_error_t * +(* collection_writer_t)(svn_stream_t *stream, + void *baton, + apr_pool_t *scratch_pool); + +/* Implement collection_writer_t writing the C string->svn_string_t hash + given as BATON. */ +static svn_error_t * +write_hash_to_stream(svn_stream_t *stream, + void *baton, + apr_pool_t *scratch_pool) +{ + apr_hash_t *hash = baton; + SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Implement collection_writer_t writing the svn_fs_x__dirent_t* array given + as BATON. */ +static svn_error_t * +write_directory_to_stream(svn_stream_t *stream, + void *baton, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *dir = baton; + SVN_ERR(unparse_dir_entries(dir, stream, scratch_pool)); + + return SVN_NO_ERROR; +} + + +/* Write out the COLLECTION pertaining to the NODEREV in FS as a deltified + text representation to file FILE using WRITER. In the process, record the + total size and the md5 digest in REP and add the representation of type + ITEM_TYPE to the indexes if necessary. 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 ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume + that we want to a props representation as the base for our delta. + If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether + to write to the proto-index files. + Perform temporary allocations in SCRATCH_POOL. + */ +static svn_error_t * +write_container_delta_rep(svn_fs_x__representation_t *rep, + apr_file_t *file, + void *collection, + collection_writer_t writer, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_fs_x__noderev_t *noderev, + apr_hash_t *reps_hash, + apr_uint32_t item_type, + svn_revnum_t final_revision, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_txdelta_window_handler_t diff_wh; + void *diff_whb; + + svn_stream_t *file_stream; + svn_stream_t *stream; + svn_fs_x__representation_t *base_rep; + svn_fs_x__representation_t *old_rep; + svn_fs_x__p2l_entry_t entry; + svn_stream_t *source; + svn_fs_x__rep_header_t header = { 0 }; + + apr_off_t rep_end = 0; + apr_off_t delta_start = 0; + apr_off_t offset = 0; + + write_container_baton_t *whb; + int diff_version = 1; + svn_boolean_t is_props = (item_type == SVN_FS_X__ITEM_TYPE_FILE_PROPS) + || (item_type == SVN_FS_X__ITEM_TYPE_DIR_PROPS); + + /* Get the base for this delta. */ + SVN_ERR(choose_delta_base(&base_rep, fs, noderev, is_props, scratch_pool)); + SVN_ERR(svn_fs_x__get_contents(&source, fs, base_rep, FALSE, scratch_pool)); + + SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool)); + + /* Write out the rep header. */ + if (base_rep) + { + header.base_revision = svn_fs_x__get_revnum(base_rep->id.change_set); + header.base_item_index = base_rep->id.number; + header.base_length = base_rep->size; + header.type = svn_fs_x__rep_delta; + } + else + { + header.type = svn_fs_x__rep_self_delta; + } + + file_stream = svn_checksum__wrap_write_stream_fnv1a_32x4( + &entry.fnv1_checksum, + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + scratch_pool); + SVN_ERR(svn_fs_x__write_rep_header(&header, file_stream, scratch_pool)); + SVN_ERR(svn_fs_x__get_file_offset(&delta_start, file, scratch_pool)); + + /* Prepare to write the svndiff data. */ + svn_txdelta_to_svndiff3(&diff_wh, + &diff_whb, + svn_stream_disown(file_stream, scratch_pool), + diff_version, + ffd->delta_compression_level, + scratch_pool); + + whb = apr_pcalloc(scratch_pool, sizeof(*whb)); + whb->stream = svn_txdelta_target_push(diff_wh, diff_whb, source, + scratch_pool); + whb->size = 0; + whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool); + + /* serialize the hash */ + stream = svn_stream_create(whb, scratch_pool); + svn_stream_set_write(stream, write_container_handler); + + SVN_ERR(writer(stream, collection, scratch_pool)); + SVN_ERR(svn_stream_close(whb->stream)); + + /* Store the results. */ + SVN_ERR(digests_final(rep, whb->md5_ctx, whb->sha1_ctx, scratch_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, scratch_pool, + scratch_pool)); + + if (old_rep) + { + SVN_ERR(svn_stream_close(file_stream)); + + /* We need to erase from the protorev the data we just wrote. */ + SVN_ERR(svn_io_file_trunc(file, offset, scratch_pool)); + + /* Use the old rep for this content. */ + memcpy(rep, old_rep, sizeof (*rep)); + } + else + { + svn_fs_x__id_t noderev_id; + + /* Write out our cosmetic end marker. */ + SVN_ERR(svn_fs_x__get_file_offset(&rep_end, file, scratch_pool)); + SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n")); + SVN_ERR(svn_stream_close(file_stream)); + + SVN_ERR(allocate_item_index(&rep->id.number, fs, txn_id, + scratch_pool)); + SVN_ERR(store_l2p_index_entry(fs, txn_id, offset, rep->id.number, + scratch_pool)); + + noderev_id.change_set = SVN_FS_X__INVALID_CHANGE_SET; + noderev_id.number = rep->id.number; + + entry.offset = offset; + SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool)); + entry.size = offset - entry.offset; + entry.type = item_type; + entry.item_count = 1; + entry.items = &noderev_id; + + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, scratch_pool)); + + /* 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 SCRATCH_POOL for temporary + allocations. + + If you change this function, consider updating svn_fs_x__verify() too. + */ +static svn_error_t * +validate_root_noderev(svn_fs_t *fs, + svn_fs_x__noderev_t *root_noderev, + svn_revnum_t rev, + apr_pool_t *scratch_pool) +{ + svn_revnum_t head_revnum = rev-1; + int head_predecessor_count; + + SVN_ERR_ASSERT(rev > 0); + + /* Compute HEAD_PREDECESSOR_COUNT. */ + { + svn_fs_x__id_t head_root_id; + svn_fs_x__noderev_t *head_root_noderev; + + /* Get /@HEAD's noderev. */ + svn_fs_x__init_rev_root(&head_root_id, head_revnum); + SVN_ERR(svn_fs_x__get_node_revision(&head_root_noderev, fs, + &head_root_id, scratch_pool, + scratch_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 - 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; +} + +/* Given the potentially txn-local id PART, update that to a permanent ID + * based on the REVISION. + */ +static void +get_final_id(svn_fs_x__id_t *part, + svn_revnum_t revision) +{ + if (!svn_fs_x__is_revision(part->change_set)) + part->change_set = svn_fs_x__change_set_by_rev(revision); +} + +/* 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 noderev-id. 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 SCRATCH_POOL. */ +static svn_error_t * +write_final_rev(svn_fs_x__id_t *new_id_p, + apr_file_t *file, + svn_revnum_t rev, + svn_fs_t *fs, + const svn_fs_x__id_t *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 *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + apr_off_t my_offset; + svn_fs_x__id_t new_id; + svn_fs_x__id_t noderev_id; + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_x__txn_id_t txn_id = svn_fs_x__get_txn_id(id->change_set); + svn_fs_x__p2l_entry_t entry; + svn_fs_x__change_set_t change_set = svn_fs_x__change_set_by_rev(rev); + svn_stream_t *file_stream; + apr_pool_t *subpool; + + /* Check to see if this is a transaction node. */ + if (txn_id == SVN_FS_X__INVALID_TXN_ID) + { + svn_fs_x__id_reset(new_id_p); + return SVN_NO_ERROR; + } + + subpool = svn_pool_create(scratch_pool); + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, scratch_pool, + subpool)); + + if (noderev->kind == svn_node_dir) + { + apr_array_header_t *entries; + int i; + + /* This is a directory. Write out all the children first. */ + + SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, noderev, scratch_pool, + subpool)); + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__dirent_t *dirent = APR_ARRAY_IDX(entries, i, + svn_fs_x__dirent_t *); + + svn_pool_clear(subpool); + SVN_ERR(write_final_rev(&new_id, file, rev, fs, &dirent->id, + initial_offset, reps_to_cache, reps_hash, + reps_pool, FALSE, subpool)); + if ( svn_fs_x__id_used(&new_id) + && (svn_fs_x__get_revnum(new_id.change_set) == rev)) + dirent->id = new_id; + } + + if (noderev->data_rep + && ! svn_fs_x__is_revision(noderev->data_rep->id.change_set)) + { + /* Write out the contents of this directory as a text rep. */ + noderev->data_rep->id.change_set = change_set; + SVN_ERR(write_container_delta_rep(noderev->data_rep, file, + entries, + write_directory_to_stream, + fs, txn_id, noderev, NULL, + SVN_FS_X__ITEM_TYPE_DIR_REP, + rev, scratch_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 + && svn_fs_x__is_txn(noderev->data_rep->id.change_set)) + { + noderev->data_rep->id.change_set = change_set; + } + } + + svn_pool_destroy(subpool); + + /* Fix up the property reps. */ + if (noderev->prop_rep + && svn_fs_x__is_txn(noderev->prop_rep->id.change_set)) + { + apr_hash_t *proplist; + apr_uint32_t item_type = noderev->kind == svn_node_dir + ? SVN_FS_X__ITEM_TYPE_DIR_PROPS + : SVN_FS_X__ITEM_TYPE_FILE_PROPS; + SVN_ERR(svn_fs_x__get_proplist(&proplist, fs, noderev, scratch_pool, + scratch_pool)); + + noderev->prop_rep->id.change_set = change_set; + + SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist, + write_hash_to_stream, fs, txn_id, + noderev, reps_hash, item_type, rev, + scratch_pool)); + } + + /* Convert our temporary ID into a permanent revision one. */ + get_final_id(&noderev->node_id, rev); + get_final_id(&noderev->copy_id, rev); + get_final_id(&noderev->noderev_id, rev); + + if (noderev->copyroot_rev == SVN_INVALID_REVNUM) + noderev->copyroot_rev = rev; + + SVN_ERR(svn_fs_x__get_file_offset(&my_offset, file, scratch_pool)); + + SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, + noderev->noderev_id.number, scratch_pool)); + new_id = noderev->noderev_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 + && svn_fs_x__get_revnum(noderev->data_rep->id.change_set) == rev) + { + SVN_ERR_ASSERT(reps_to_cache && reps_pool); + APR_ARRAY_PUSH(reps_to_cache, svn_fs_x__representation_t *) + = svn_fs_x__rep_copy(noderev->data_rep, reps_pool); + } + + if ( noderev->prop_rep + && svn_fs_x__get_revnum(noderev->prop_rep->id.change_set) == rev) + { + /* Add new property reps to hash and on-disk cache. */ + svn_fs_x__representation_t *copy + = svn_fs_x__rep_copy(noderev->prop_rep, reps_pool); + + SVN_ERR_ASSERT(reps_to_cache && reps_pool); + APR_ARRAY_PUSH(reps_to_cache, svn_fs_x__representation_t *) = copy; + + apr_hash_set(reps_hash, + copy->sha1_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->has_sha1 = FALSE; + + /* don't serialize SHA1 for props to disk (waste of space) */ + if (noderev->prop_rep) + noderev->prop_rep->has_sha1 = FALSE; + + /* Write out our new node-revision. */ + if (at_root) + SVN_ERR(validate_root_noderev(fs, noderev, rev, scratch_pool)); + + file_stream = svn_checksum__wrap_write_stream_fnv1a_32x4( + &entry.fnv1_checksum, + svn_stream_from_aprfile2(file, TRUE, + scratch_pool), + scratch_pool); + SVN_ERR(svn_fs_x__write_noderev(file_stream, noderev, scratch_pool)); + SVN_ERR(svn_stream_close(file_stream)); + + /* reference the root noderev from the log-to-phys index */ + noderev_id = noderev->noderev_id; + noderev_id.change_set = SVN_FS_X__INVALID_CHANGE_SET; + + entry.offset = my_offset; + SVN_ERR(svn_fs_x__get_file_offset(&my_offset, file, scratch_pool)); + entry.size = my_offset - entry.offset; + entry.type = SVN_FS_X__ITEM_TYPE_NODEREV; + entry.item_count = 1; + entry.items = &noderev_id; + + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, scratch_pool)); + + /* Return our ID that references the revision file. */ + *new_id_p = new_id; + + return SVN_NO_ERROR; +} + +/* Write the changed path info CHANGED_PATHS from transaction TXN_ID to the + permanent rev-file FILE representing NEW_REV in filesystem FS. *OFFSET_P + is set the to offset in the file of the beginning of this information. + NEW_REV is the revision currently being committed. + Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +write_final_changed_path_info(apr_off_t *offset_p, + apr_file_t *file, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_hash_t *changed_paths, + svn_revnum_t new_rev, + apr_pool_t *scratch_pool) +{ + apr_off_t offset; + svn_stream_t *stream; + svn_fs_x__p2l_entry_t entry; + svn_fs_x__id_t rev_item + = {SVN_INVALID_REVNUM, SVN_FS_X__ITEM_INDEX_CHANGES}; + + SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool)); + + /* write to target file & calculate checksum */ + stream = svn_checksum__wrap_write_stream_fnv1a_32x4(&entry.fnv1_checksum, + svn_stream_from_aprfile2(file, TRUE, scratch_pool), + scratch_pool); + SVN_ERR(svn_fs_x__write_changes(stream, fs, changed_paths, TRUE, + scratch_pool)); + SVN_ERR(svn_stream_close(stream)); + + *offset_p = offset; + + /* reference changes from the indexes */ + entry.offset = offset; + SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool)); + entry.size = offset - entry.offset; + entry.type = SVN_FS_X__ITEM_TYPE_CHANGES; + entry.item_count = 1; + entry.items = &rev_item; + + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, scratch_pool)); + SVN_ERR(store_l2p_index_entry(fs, txn_id, entry.offset, + SVN_FS_X__ITEM_INDEX_CHANGES, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* 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_x__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 *scratch_pool) +{ +#ifdef SVN_DEBUG + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_fs_t *ft; /* fs++ == ft */ + svn_fs_root_t *root; + svn_fs_x__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(scratch_pool); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS, + svn_uuid_generate(scratch_pool)); + SVN_ERR(ffd->svn_fs_open_(&ft, fs->path, + fs_config, + scratch_pool, + scratch_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_x__revision_root(&root, ft, new_rev, scratch_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_x__verify_root(root, scratch_pool)); +#endif /* SVN_DEBUG */ + + return SVN_NO_ERROR; +} + +/* Verify that the user registered with FS has all the locks necessary to + permit all the changes associated 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, + svn_fs_x__txn_id_t txn_id, + apr_hash_t *changed_paths, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool; + apr_array_header_t *changed_paths_sorted; + svn_stringbuf_t *last_recursed = NULL; + int i; + + /* Make an array of the changed paths, and sort them depth-first-ily. */ + changed_paths_sorted = svn_sort__hash(changed_paths, + svn_sort_compare_items_as_paths, + scratch_pool); + + /* 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. */ + iterpool = svn_pool_create(scratch_pool); + for (i = 0; i < changed_paths_sorted->nelts; i++) + { + const svn_sort__item_t *item; + const char *path; + svn_fs_x__change_t *change; + svn_boolean_t recurse = TRUE; + + svn_pool_clear(iterpool); + + item = &APR_ARRAY_IDX(changed_paths_sorted, i, svn_sort__item_t); + + /* Fetch the change associated with our path. */ + path = item->key; + change = item->value; + + /* 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_fspath__skip_ancestor(last_recursed->data, path)) + continue; + + /* 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_x__allow_locked_operation(path, fs, recurse, TRUE, + iterpool)); + + /* 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, scratch_pool); + else + svn_stringbuf_set(last_recursed, path); + } + } + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +/* Return in *PATH the path to a file containing the properties that + make up the final revision properties file. This involves setting + svn:date and removing any temporary properties associated with the + commit flags. */ +static svn_error_t * +write_final_revprop(const char **path, + svn_fs_txn_t *txn, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *pool) +{ + apr_hash_t *txnprops; + svn_boolean_t final_mods = FALSE; + svn_string_t date; + svn_string_t *client_date; + + SVN_ERR(svn_fs_x__txn_proplist(&txnprops, txn, pool)); + + /* Remove any temporary txn props representing 'flags'. */ + if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD)) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, NULL); + final_mods = TRUE; + } + + if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS)) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL); + final_mods = TRUE; + } + + client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE); + if (client_date) + { + svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE, NULL); + final_mods = TRUE; + } + + /* Update commit time to ensure that svn:date revprops remain ordered if + requested. */ + if (!client_date || strcmp(client_date->data, "1")) + { + date.data = svn_time_to_cstring(apr_time_now(), pool); + date.len = strlen(date.data); + svn_hash_sets(txnprops, SVN_PROP_REVISION_DATE, &date); + final_mods = TRUE; + } + + if (final_mods) + { + SVN_ERR(set_txn_proplist(txn->fs, txn_id, txnprops, TRUE, pool)); + *path = svn_fs_x__path_txn_props_final(txn->fs, txn_id, pool); + } + else + { + *path = svn_fs_x__path_txn_props(txn->fs, txn_id, pool); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__add_index_data(svn_fs_t *fs, + apr_file_t *file, + const char *l2p_proto_index, + const char *p2l_proto_index, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + apr_off_t l2p_offset; + apr_off_t p2l_offset; + svn_stringbuf_t *footer; + unsigned char footer_length; + svn_checksum_t *l2p_checksum; + svn_checksum_t *p2l_checksum; + + /* Append the actual index data to the pack file. */ + l2p_offset = 0; + SVN_ERR(svn_io_file_seek(file, APR_END, &l2p_offset, scratch_pool)); + SVN_ERR(svn_fs_x__l2p_index_append(&l2p_checksum, fs, file, + l2p_proto_index, revision, + scratch_pool, scratch_pool)); + + p2l_offset = 0; + SVN_ERR(svn_io_file_seek(file, APR_END, &p2l_offset, scratch_pool)); + SVN_ERR(svn_fs_x__p2l_index_append(&p2l_checksum, fs, file, + p2l_proto_index, revision, + scratch_pool, scratch_pool)); + + /* Append footer. */ + footer = svn_fs_x__unparse_footer(l2p_offset, l2p_checksum, + p2l_offset, p2l_checksum, scratch_pool, + scratch_pool); + SVN_ERR(svn_io_file_write_full(file, footer->data, footer->len, NULL, + scratch_pool)); + + footer_length = footer->len; + SVN_ERR_ASSERT(footer_length == footer->len); + SVN_ERR(svn_io_file_write_full(file, &footer_length, 1, NULL, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Baton used for commit_body below. */ +typedef struct commit_baton_t { + 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; +} commit_baton_t; + +/* The work-horse for svn_fs_x__commit, called with the FS write lock. + This implements the svn_fs_x__with_write_lock() 'body' callback + type. BATON is a 'commit_baton_t *'. */ +static svn_error_t * +commit_body(void *baton, + apr_pool_t *scratch_pool) +{ + commit_baton_t *cb = baton; + svn_fs_x__data_t *ffd = cb->fs->fsap_data; + const char *old_rev_filename, *rev_filename, *proto_filename; + const char *revprop_filename, *final_revprop; + svn_fs_x__id_t root_id, new_root_id; + svn_revnum_t old_rev, new_rev; + apr_file_t *proto_file; + void *proto_file_lockcookie; + apr_off_t initial_offset, changed_path_offset; + svn_fs_x__txn_id_t txn_id = svn_fs_x__txn_get_id(cb->txn); + apr_hash_t *changed_paths; + + /* Re-Read the current repository format. All our repo upgrade and + config evaluation strategies are such that existing information in + FS and FFD remains valid. + + Although we don't recommend upgrading hot repositories, people may + still do it and we must make sure to either handle them gracefully + or to error out. + + Committing pre-format 3 txns will fail after upgrade to format 3+ + because the proto-rev cannot be found; no further action needed. + Upgrades from pre-f7 to f7+ means a potential change in addressing + mode for the final rev. We must be sure to detect that cause because + the failure would only manifest once the new revision got committed. + */ + SVN_ERR(svn_fs_x__read_format_file(cb->fs, scratch_pool)); + + /* Get the current youngest revision. */ + SVN_ERR(svn_fs_x__youngest_rev(&old_rev, cb->fs, scratch_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")); + + /* We need the changes list for verification as well as for writing it + to the final rev file. */ + SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, cb->fs, txn_id, + scratch_pool)); + + /* 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, txn_id, changed_paths, scratch_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, txn_id, scratch_pool)); + SVN_ERR(svn_fs_x__get_file_offset(&initial_offset, proto_file, + scratch_pool)); + + /* Write out all the node-revisions and directory contents. */ + svn_fs_x__init_txn_root(&root_id, txn_id); + SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, &root_id, + initial_offset, cb->reps_to_cache, cb->reps_hash, + cb->reps_pool, TRUE, scratch_pool)); + + /* Write the changed-path information. */ + SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file, + cb->fs, txn_id, changed_paths, + new_rev, scratch_pool)); + + /* Append the index data to the rev file. */ + SVN_ERR(svn_fs_x__add_index_data(cb->fs, proto_file, + svn_fs_x__path_l2p_proto_index(cb->fs, txn_id, scratch_pool), + svn_fs_x__path_p2l_proto_index(cb->fs, txn_id, scratch_pool), + new_rev, scratch_pool)); + + SVN_ERR(svn_io_file_flush_to_disk(proto_file, scratch_pool)); + SVN_ERR(svn_io_file_close(proto_file, scratch_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. */ + + /* 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 (new_rev % ffd->max_files_per_dir == 0) + { + /* Create the revs shard. */ + { + const char *new_dir + = svn_fs_x__path_rev_shard(cb->fs, new_rev, scratch_pool); + svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, + scratch_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, + scratch_pool), + new_dir, scratch_pool)); + } + + /* Create the revprops shard. */ + SVN_ERR_ASSERT(! svn_fs_x__is_packed_revprop(cb->fs, new_rev)); + { + const char *new_dir + = svn_fs_x__path_revprops_shard(cb->fs, new_rev, scratch_pool); + svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, + scratch_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, + scratch_pool), + new_dir, scratch_pool)); + } + } + + /* Move the finished rev file into place. + + ### This "breaks" the transaction by removing the protorev file + ### but the revision is not yet complete. If this commit does + ### not complete for any reason the transaction will be lost. */ + old_rev_filename = svn_fs_x__path_rev_absolute(cb->fs, old_rev, + scratch_pool); + + rev_filename = svn_fs_x__path_rev(cb->fs, new_rev, scratch_pool); + proto_filename = svn_fs_x__path_txn_proto_rev(cb->fs, txn_id, + scratch_pool); + SVN_ERR(svn_fs_x__move_into_place(proto_filename, rev_filename, + old_rev_filename, scratch_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, txn_id, proto_file_lockcookie, + scratch_pool)); + + /* Move the revprops file into place. */ + SVN_ERR_ASSERT(! svn_fs_x__is_packed_revprop(cb->fs, new_rev)); + SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, txn_id, + scratch_pool)); + final_revprop = svn_fs_x__path_revprops(cb->fs, new_rev, scratch_pool); + SVN_ERR(svn_fs_x__move_into_place(revprop_filename, final_revprop, + old_rev_filename, scratch_pool)); + + /* Update the 'current' file. */ + SVN_ERR(verify_as_revision_before_current_plus_plus(cb->fs, new_rev, + scratch_pool)); + SVN_ERR(svn_fs_x__write_current(cb->fs, new_rev, scratch_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_x__purge_txn(cb->fs, cb->txn->id, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Add the representations in REPS_TO_CACHE (an array of + * svn_fs_x__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++) + { + svn_fs_x__representation_t *rep + = APR_ARRAY_IDX(reps_to_cache, i, svn_fs_x__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_x__set_rep_reference(fs, rep, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__commit(svn_revnum_t *new_rev_p, + svn_fs_t *fs, + svn_fs_txn_t *txn, + apr_pool_t *scratch_pool) +{ + commit_baton_t cb; + svn_fs_x__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(scratch_pool, 5, + sizeof(svn_fs_x__representation_t *)); + cb.reps_hash = apr_hash_make(scratch_pool); + cb.reps_pool = scratch_pool; + } + else + { + cb.reps_to_cache = NULL; + cb.reps_hash = NULL; + cb.reps_pool = NULL; + } + + SVN_ERR(svn_fs_x__with_write_lock(fs, commit_body, &cb, scratch_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_x__open_rep_cache(fs, scratch_pool)); + + /* Write new entries to the rep-sharing database. + * + * We use an sqlite transaction to speed things up; + * see . + */ + /* ### A commit that touches thousands of files will starve other + (reader/writer) commits for the duration of the below call. + Maybe write in batches? */ + SVN_SQLITE__WITH_TXN( + write_reps_to_cache(fs, cb.reps_to_cache, scratch_pool), + ffd->rep_cache_db); + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__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_fs_x__path_txns_dir(fs, 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 = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(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_x__open_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + const char *name, + apr_pool_t *pool) +{ + svn_fs_txn_t *txn; + fs_txn_data_t *ftd; + svn_node_kind_t kind; + svn_fs_x__transaction_t *local_txn; + svn_fs_x__txn_id_t txn_id; + + SVN_ERR(svn_fs_x__txn_by_name(&txn_id, name)); + + /* First check to see if the directory exists. */ + SVN_ERR(svn_io_check_path(svn_fs_x__path_txn_dir(fs, txn_id, 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)); + ftd = apr_pcalloc(pool, sizeof(*ftd)); + ftd->txn_id = txn_id; + + /* Read in the root node of this transaction. */ + txn->id = apr_pstrdup(pool, name); + txn->fs = fs; + + SVN_ERR(svn_fs_x__get_txn(&local_txn, fs, txn_id, pool)); + + txn->base_rev = local_txn->base_rev; + + txn->vtable = &txn_vtable; + txn->fsap_data = ftd; + *txn_p = txn; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__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, svn_fs_x__txn_get_id(txn), + pool)); + *table_p = proplist; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__delete_node_revision(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__noderev_t *noderev; + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, scratch_pool, + scratch_pool)); + + /* Delete any mutable property representation. */ + if (noderev->prop_rep + && svn_fs_x__is_txn(noderev->prop_rep->id.change_set)) + SVN_ERR(svn_io_remove_file2(svn_fs_x__path_txn_node_props(fs, id, + scratch_pool, + scratch_pool), + FALSE, scratch_pool)); + + /* Delete any mutable data representation. */ + if (noderev->data_rep + && svn_fs_x__is_txn(noderev->data_rep->id.change_set) + && noderev->kind == svn_node_dir) + { + svn_fs_x__data_t *ffd = fs->fsap_data; + const svn_fs_x__id_t *key = id; + + SVN_ERR(svn_io_remove_file2( + svn_fs_x__path_txn_node_children(fs, id, scratch_pool, + scratch_pool), + FALSE, scratch_pool)); + + /* remove the corresponding entry from the cache, if such exists */ + SVN_ERR(svn_cache__set(ffd->dir_cache, key, NULL, scratch_pool)); + } + + return svn_io_remove_file2(svn_fs_x__path_txn_node_rev(fs, id, + scratch_pool, + scratch_pool), + FALSE, scratch_pool); +} + + + +/*** Transactions ***/ + +svn_error_t * +svn_fs_x__get_base_rev(svn_revnum_t *revnum, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool) +{ + svn_fs_x__transaction_t *txn; + SVN_ERR(svn_fs_x__get_txn(&txn, fs, txn_id, scratch_pool)); + *revnum = txn->base_rev; + + return SVN_NO_ERROR; +} + + +/* Generic transaction operations. */ + +svn_error_t * +svn_fs_x__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_x__txn_proplist(&table, txn, pool)); + + *value_p = svn_hash_gets(table, propname); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__begin_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_uint32_t flags, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_string_t date; + fs_txn_data_t *ftd; + apr_hash_t *props = apr_hash_make(scratch_pool); + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + + SVN_ERR(create_txn(txn_p, fs, rev, result_pool, scratch_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(), scratch_pool); + date.len = strlen(date.data); + + svn_hash_sets(props, SVN_PROP_REVISION_DATE, &date); + + /* Set temporary txn props that represent the requested 'flags' + behaviors. */ + if (flags & SVN_FS_TXN_CHECK_OOD) + svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_OOD, + svn_string_create("true", scratch_pool)); + + if (flags & SVN_FS_TXN_CHECK_LOCKS) + svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_LOCKS, + svn_string_create("true", scratch_pool)); + + if (flags & SVN_FS_TXN_CLIENT_DATE) + svn_hash_sets(props, SVN_FS__PROP_TXN_CLIENT_DATE, + svn_string_create("0", scratch_pool)); + + ftd = (*txn_p)->fsap_data; + SVN_ERR(set_txn_proplist(fs, ftd->txn_id, props, FALSE, scratch_pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/transaction.h b/contrib/subversion/subversion/libsvn_fs_x/transaction.h new file mode 100644 index 000000000..490f716d4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/transaction.h @@ -0,0 +1,316 @@ +/* transaction.h --- transaction-related functions of FSX + * + * ==================================================================== + * 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_FS__TRANSACTION_H +#define SVN_LIBSVN_FS__TRANSACTION_H + +#include "fs.h" + +/* Return the transaction ID of TXN. + */ +svn_fs_x__txn_id_t +svn_fs_x__txn_get_id(svn_fs_txn_t *txn); + +/* Obtain a write lock on the filesystem FS in a subpool of SCRATCH_POOL, + call BODY with BATON and that subpool, destroy the subpool (releasing the + write lock) and return what BODY returned. */ +svn_error_t * +svn_fs_x__with_write_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool); + +/* Obtain a pack operation lock on the filesystem FS in a subpool of + SCRATCH_POOL, call BODY with BATON and that subpool, destroy the subpool + (releasing the write lock) and return what BODY returned. */ +svn_error_t * +svn_fs_x__with_pack_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool); + +/* Obtain the txn-current file lock on the filesystem FS in a subpool of + SCRATCH_POOL, call BODY with BATON and that subpool, destroy the subpool + (releasing the write lock) and return what BODY returned. */ +svn_error_t * +svn_fs_x__with_txn_current_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool); + +/* Obtain all locks on the filesystem FS in a subpool of SCRATCH_POOL, + call BODY with BATON and that subpool, destroy the subpool (releasing + the locks) and return what BODY returned. + + This combines svn_fs_fs__with_write_lock, svn_fs_fs__with_pack_lock, + and svn_fs_fs__with_txn_current_lock, ensuring correct lock ordering. */ +svn_error_t * +svn_fs_x__with_all_locks(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *scratch_pool), + void *baton, + apr_pool_t *scratch_pool); + +/* Return TRUE, iff NODEREV is the root node of a transaction that has not + seen any modifications, yet. */ +svn_boolean_t +svn_fs_x__is_fresh_txn_root(svn_fs_x__noderev_t *noderev); + +/* Store NODEREV as the node-revision in the transaction defined by NODEREV's + ID within FS. Do any necessary temporary allocation in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__put_node_revision(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *scratch_pool); + +/* Find the paths which were changed in transaction TXN_ID of + filesystem FS and store them in *CHANGED_PATHS_P. + Get any temporary allocations from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__txn_changes_fetch(apr_hash_t **changed_paths_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + +/* Set the transaction property NAME to the value VALUE in transaction + TXN. Perform temporary allocations from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__change_txn_prop(svn_fs_txn_t *txn, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool); + +/* Change transaction properties in transaction TXN based on PROPS. + Perform temporary allocations from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__change_txn_props(svn_fs_txn_t *txn, + const apr_array_header_t *props, + apr_pool_t *scratch_pool); + +/* Store a transaction record in *TXN_P for the transaction identified + by TXN_ID in filesystem FS. Allocate everything from POOL. */ +svn_error_t * +svn_fs_x__get_txn(svn_fs_x__transaction_t **txn_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *pool); + +/* Return the next available copy_id in *COPY_ID for the transaction + TXN_ID in filesystem FS. Allocate temporaries in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__reserve_copy_id(svn_fs_x__id_t *copy_id_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + +/* Create an entirely new mutable node in the filesystem FS, whose + node-revision is NODEREV. COPY_ID is the copy_id to use in the + node revision ID. TXN_ID is the Subversion transaction under + which this occurs. */ +svn_error_t * +svn_fs_x__create_node(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + +/* Remove all references to the transaction TXN_ID from filesystem FS. + Temporary allocations are from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__purge_txn(svn_fs_t *fs, + const char *txn_id, + apr_pool_t *scratch_pool); + +/* Abort the existing transaction TXN, performing any temporary + allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__abort_txn(svn_fs_txn_t *txn, + apr_pool_t *scratch_pool); + +/* Add or set in filesystem FS, transaction TXN_ID, in directory + PARENT_NODEREV a directory entry for NAME pointing to ID of type + KIND. The PARENT_NODEREV's DATA_REP will be redirected to the in-txn + representation, if it had not been mutable before. + + If PARENT_NODEREV does not have a DATA_REP, allocate one in RESULT_POOL. + Temporary allocations are done in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__set_entry(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_fs_x__noderev_t *parent_noderev, + const char *name, + const svn_fs_x__id_t *id, + svn_node_kind_t kind, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Add a change to the changes record for filesystem FS in transaction + TXN_ID. Mark path PATH, having noderev-id ID, as changed according to + the type in CHANGE_KIND. If the text representation was changed set + TEXT_MOD to TRUE, and likewise for PROP_MOD as well as MERGEINFO_MOD. + If this change was the result of a copy, set COPYFROM_REV and + COPYFROM_PATH to the revision and path of the copy source, otherwise + they should be set to SVN_INVALID_REVNUM and NULL. Perform any + temporary allocations from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__add_change(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const char *path, + const svn_fs_x__id_t *id, + svn_fs_path_change_kind_t change_kind, + svn_boolean_t text_mod, + svn_boolean_t prop_mod, + svn_boolean_t mergeinfo_mod, + svn_node_kind_t node_kind, + svn_revnum_t copyfrom_rev, + const char *copyfrom_path, + apr_pool_t *scratch_pool); + +/* Return a writable stream in *STREAM, allocated in RESULT_POOL, that + allows storing the text representation of node-revision NODEREV in + filesystem FS. */ +svn_error_t * +svn_fs_x__set_contents(svn_stream_t **stream, + svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_pool_t *result_pool); + +/* Create a node revision in FS which is an immediate successor of + NEW_NODEREV's predecessor. Use SCRATCH_POOL for any temporary allocation. + + COPY_ID, is a key into the `copies' table, and + indicates that this new node is being created as the result of a + copy operation, and specifically which operation that was. + + TXN_ID is the Subversion transaction under which this occurs. + + After this call, the deltification code assumes that the new node's + contents will change frequently, and will avoid representing other + nodes as deltas against this node's contents. */ +svn_error_t * +svn_fs_x__create_successor(svn_fs_t *fs, + svn_fs_x__noderev_t *new_noderev, + const svn_fs_x__id_t *copy_id, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + +/* Write a new property list PROPLIST for node-revision NODEREV in + filesystem FS. Perform any temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__set_proplist(svn_fs_t *fs, + svn_fs_x__noderev_t *noderev, + apr_hash_t *proplist, + apr_pool_t *scratch_pool); + +/* Append the L2P and P2L indexes given by their proto index file names + * L2P_PROTO_INDEX and P2L_PROTO_INDEX to the revision / pack FILE. + * The latter contains revision(s) starting at REVISION in FS. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__add_index_data(svn_fs_t *fs, + apr_file_t *file, + const char *l2p_proto_index, + const char *p2l_proto_index, + svn_revnum_t revision, + apr_pool_t *scratch_pool); + +/* Commit the transaction TXN in filesystem FS and return its new + revision number in *REV. If the transaction is out of date, return + the error SVN_ERR_FS_TXN_OUT_OF_DATE. Use SCRATCH_POOL for temporary + allocations. */ +svn_error_t * +svn_fs_x__commit(svn_revnum_t *new_rev_p, + svn_fs_t *fs, + svn_fs_txn_t *txn, + apr_pool_t *scratch_pool); + +/* Set *NAMES_P to an array of names which are all the active + transactions in filesystem FS. Allocate the array from POOL. */ +svn_error_t * +svn_fs_x__list_transactions(apr_array_header_t **names_p, + svn_fs_t *fs, + apr_pool_t *pool); + +/* Open the transaction named NAME in filesystem FS. Set *TXN_P to + * the transaction. If there is no such transaction, return +` * SVN_ERR_FS_NO_SUCH_TRANSACTION. Allocate the new transaction in + * POOL. */ +svn_error_t * +svn_fs_x__open_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + const char *name, + apr_pool_t *pool); + +/* Return the property list from transaction TXN and store it in + *PROPLIST. Allocate the property list from POOL. */ +svn_error_t * +svn_fs_x__txn_proplist(apr_hash_t **table_p, + svn_fs_txn_t *txn, + apr_pool_t *pool); + +/* Delete the mutable node-revision referenced by ID, along with any + mutable props or directory contents associated with it. Perform + temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__delete_node_revision(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *scratch_pool); + +/* Retrieve information about the Subversion transaction TXN_ID from + the `transactions' table of FS, using SCRATCH_POOL for temporary + allocations. Set *RENUM to the transaction's base revision. + + If there is no such transaction, SVN_ERR_FS_NO_SUCH_TRANSACTION is + the error returned. + + Returns SVN_ERR_FS_TRANSACTION_NOT_MUTABLE if TXN_NAME refers to a + transaction that has already been committed. */ +svn_error_t * +svn_fs_x__get_base_rev(svn_revnum_t *revnum, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *scratch_pool); + +/* Find the value of the property named PROPNAME in transaction TXN. + Return the contents in *VALUE_P. The contents will be allocated + from POOL. */ +svn_error_t * +svn_fs_x__txn_prop(svn_string_t **value_p, + svn_fs_txn_t *txn, + const char *propname, + apr_pool_t *pool); + +/* Begin a new transaction in filesystem FS, based on existing + revision REV. The new transaction is returned in *TXN_P, allocated + in RESULT_POOL. Allocate temporaries from SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__begin_txn(svn_fs_txn_t **txn_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_uint32_t flags, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/tree.c b/contrib/subversion/subversion/libsvn_fs_x/tree.c new file mode 100644 index 000000000..ce247658d --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/tree.c @@ -0,0 +1,4542 @@ +/* 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 +#include +#include +#include +#include + +#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 "svn_sorts.h" + +#include "fs.h" +#include "dag.h" +#include "lock.h" +#include "tree.h" +#include "fs_x.h" +#include "fs_id.h" +#include "temp_serializer.h" +#include "cached_data.h" +#include "transaction.h" +#include "pack.h" +#include "util.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" + + + +/* 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 + svn_fs_x__noderev_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 dag_node_t fs_rev_root_data_t; + +typedef struct fs_txn_root_data_t +{ + /* TXN_ID value from the main struct but as a struct instead of a string */ + svn_fs_x__txn_id_t 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, + apr_pool_t *pool); + +static svn_fs_root_t * +make_revision_root(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +static svn_error_t * +make_txn_root(svn_fs_root_t **root_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_revnum_t base_rev, + apr_uint32_t flags, + apr_pool_t *result_pool); + +static svn_error_t * +x_closest_copy(svn_fs_root_t **root_p, + const char **path_p, + svn_fs_root_t *root, + const char *path, + 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 }; + +/* 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. + */ +struct svn_fs_x__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; + + /* Position of the last bucket hit that actually had a DAG node in it. + LAST_HIT may refer to a bucket that matches path@rev but has not + its NODE element set, yet. + This value is a mere hint for optimistic lookup and any value is + valid (as long as it is < BUCKET_COUNT). */ + apr_size_t last_non_empty; +}; + +svn_fs_x__dag_cache_t* +svn_fs_x__create_dag_cache(apr_pool_t *result_pool) +{ + svn_fs_x__dag_cache_t *result = apr_pcalloc(result_pool, sizeof(*result)); + result->pool = svn_pool_create(result_pool); + + return result; +} + +/* Clears the CACHE at regular intervals (destroying all cached nodes) + */ +static void +auto_clear_dag_cache(svn_fs_x__dag_cache_t* cache) +{ + 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( svn_fs_x__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)) + { + /* Remember the position of the last node we found in this cache. */ + if (result->node) + cache->last_non_empty = cache->last_hit; + + 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++; + } + else if (result->node) + { + /* This bucket is valid & has a suitable DAG node in it. + Remember its location. */ + cache->last_non_empty = bucket_index; + } + + return result; +} + +/* Optimistic lookup using the last seen non-empty location in CACHE. + Return the node of that entry, if it is still in use and matches PATH. + Return NULL otherwise. Since the caller usually already knows the path + length, provide it in PATH_LEN. */ +static dag_node_t * +cache_lookup_last_path(svn_fs_x__dag_cache_t *cache, + const char *path, + apr_size_t path_len) +{ + cache_entry_t *result = &cache->buckets[cache->last_non_empty]; + assert(strlen(path) == path_len); + + if ( result->node + && (result->path_len == path_len) + && !memcmp(result->path, path, path_len)) + { + return result->node; + } + + return NULL; +} + +/* 2nd level cache */ + +/* Find and return the DAG node cache for ROOT and the key that + should be used for PATH. + + RESULT_POOL will only be used for allocating a new keys if necessary. */ +static void +locate_cache(svn_cache__t **cache, + const char **key, + svn_fs_root_t *root, + const char *path, + apr_pool_t *result_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 + { + svn_fs_x__data_t *ffd = root->fs->fsap_data; + + if (cache) + *cache = ffd->rev_node_cache; + if (key && path) + *key = svn_fs_x__combine_number_and_string(root->rev, path, + result_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). + */ +static svn_error_t * +dag_node_cache_get(dag_node_t **node_p, + svn_fs_root_t *root, + const char *path, + 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 */ + + svn_fs_x__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)); + if (found && node) + { + /* Patch up the FS, since this might have come from an old FS + * object. */ + svn_fs_x__dag_set_fs(node, root->fs); + bucket->node = node; + } + } + else + { + node = bucket->node; + } + } + 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_x__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 *scratch_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, scratch_pool); + + return svn_cache__set(cache, key, node, scratch_pool); +} + + +/* Baton for find_descendants_in_cache. */ +typedef struct fdic_baton_t +{ + const char *path; + apr_array_header_t *list; + apr_pool_t *pool; +} fdic_baton_t; + +/* If the given item is a descendant 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_descendants_in_cache(void *baton, + const void *key, + apr_ssize_t klen, + void *val, + apr_pool_t *pool) +{ + fdic_baton_t *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 *scratch_pool) +{ + fdic_baton_t b; + svn_cache__t *cache; + apr_pool_t *iterpool; + int i; + + b.path = path; + b.pool = svn_pool_create(scratch_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_descendants_in_cache, + &b, b.pool)); + + iterpool = svn_pool_create(b.pool); + + for (i = 0; i < b.list->nelts; i++) + { + const char *descendant = APR_ARRAY_IDX(b.list, i, const char *); + svn_pool_clear(iterpool); + SVN_ERR(svn_cache__set(cache, descendant, 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_x__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_x__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, svn_fs_x__txn_get_id(txn), + txn->base_rev, flags, pool); +} + + +svn_error_t * +svn_fs_x__revision_root(svn_fs_root_t **root_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool)); + + *root_p = make_revision_root(fs, rev, pool); + + return SVN_NO_ERROR; +} + + + +/* Getting dag nodes for roots. */ + +/* Return the transaction ID to a given transaction ROOT. */ +static svn_fs_x__txn_id_t +root_txn_id(svn_fs_root_t *root) +{ + fs_txn_root_data_t *frd = root->fsap_data; + assert(root->is_txn_root); + + return frd->txn_id; +} + +/* Set *NODE_P to a freshly opened dag node referring to the root + directory of ROOT, allocating from RESULT_POOL. Use SCRATCH_POOL + for temporary allocations. */ +static svn_error_t * +root_node(dag_node_t **node_p, + svn_fs_root_t *root, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if (root->is_txn_root) + { + /* It's a transaction root. Open a fresh copy. */ + return svn_fs_x__dag_txn_root(node_p, root->fs, root_txn_id(root), + result_pool, scratch_pool); + } + else + { + /* It's a revision root, so we already have its root directory + opened. */ + return svn_fs_x__dag_revision_root(node_p, root->fs, root->rev, + result_pool, scratch_pool); + } +} + + +/* Set *NODE_P to a mutable root directory for ROOT, cloning if + necessary, allocating in RESULT_POOL. ROOT must be a transaction root. + Use ERROR_PATH in error messages. Use SCRATCH_POOL for temporaries.*/ +static svn_error_t * +mutable_root_node(dag_node_t **node_p, + svn_fs_root_t *root, + const char *error_path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + if (root->is_txn_root) + { + /* It's a transaction root. Open a fresh copy. */ + return svn_fs_x__dag_txn_root(node_p, root->fs, root_txn_id(root), + result_pool, scratch_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). 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, + apr_pool_t *pool) +{ + svn_fs_x__id_t child_copy_id, parent_copy_id; + svn_boolean_t related; + 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); + + /* Initialize some convenience variables. */ + SVN_ERR(svn_fs_x__dag_get_copy_id(&child_copy_id, child->node)); + SVN_ERR(svn_fs_x__dag_get_copy_id(&parent_copy_id, child->parent->node)); + + /* If this child is already mutable, we have nothing to do. */ + if (svn_fs_x__dag_check_mutable(child->node)) + { + *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 (svn_fs_x__id_is_root(&child_copy_id)) + 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_x__id_eq(&child_copy_id, &parent_copy_id)) + 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_x__dag_get_copyroot(©root_rev, ©root_path, + child->node)); + SVN_ERR(svn_fs_x__revision_root(©root_root, fs, copyroot_rev, pool)); + SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool)); + + SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node, child->node)); + if (!related) + 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_x__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 RESULT_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 *result_pool) +{ + parent_path_t *parent_path = apr_pcalloc(result_pool, sizeof(*parent_path)); + if (node) + parent_path->node = svn_fs_x__dag_copy_into_pool(node, result_pool); + 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, + + /* The caller wants a NULL path object instead of an error if the + path cannot be found. */ + open_path_allow_null = 8 +} open_path_flags_t; + +/* Try a short-cut for the open_path() function using the last node accessed. + * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is + * its "created path", return the node in *NODE_P. Set it to NULL otherwise. + * + * This function is used to support ra_serf-style access patterns where we + * are first asked for path@rev and then for path@c_rev of the same node. + * The shortcut works by ignoring the "rev" part of the cache key and then + * checking whether we got lucky. Lookup and verification are both quick + * plus there are many early outs for common types of mismatch. + */ +static svn_error_t * +try_match_last_node(dag_node_t **node_p, + svn_fs_root_t *root, + const char *path, + apr_size_t path_len, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = root->fs->fsap_data; + + /* Optimistic lookup: if the last node returned from the cache applied to + the same PATH, return it in NODE. */ + dag_node_t *node + = cache_lookup_last_path(ffd->dag_node_cache, path, path_len); + + /* Did we get a bucket with a committed node? */ + if (node && !svn_fs_x__dag_check_mutable(node)) + { + /* Get the path&rev pair at which this node was created. + This is repository location for which this node is _known_ to be + the right lookup result irrespective of how we found it. */ + const char *created_path + = svn_fs_x__dag_get_created_path(node); + svn_revnum_t revision = svn_fs_x__dag_get_revision(node); + + /* Is it an exact match? */ + if (revision == root->rev && strcmp(created_path, path) == 0) + { + /* Cache it under its full path@rev access path. */ + SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool)); + + *node_p = node; + return SVN_NO_ERROR; + } + } + + *node_p = NULL; + return SVN_NO_ERROR; +} + + +/* 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, + IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, 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, + svn_boolean_t is_txn_path, + 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 = NULL; /* The portion of PATH we haven't traversed yet. */ + apr_pool_t *iterpool = svn_pool_create(pool); + + /* path to the currently processed entry without trailing '/'. + We will reuse this across iterations by simply putting a NUL terminator + at the respective position and replacing that with a '/' in the next + iteration. This is correct as we assert() PATH to be canonical. */ + svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool); + apr_size_t path_len = path_so_far->len; + + /* Callers often traverse the DAG in some path-based order or along the + history segments. That allows us to try a few guesses about where to + find the next item. This is only useful if the caller didn't request + the full parent chain. */ + assert(svn_fs__is_canonical_abspath(path)); + path_so_far->len = 0; /* "" */ + if (flags & open_path_node_only) + { + const char *directory; + + /* First attempt: Assume that we access the DAG for the same path as + in the last lookup but for a different revision that happens to be + the last revision that touched the respective node. This is a + common pattern when e.g. checking out over ra_serf. Note that this + will only work for committed data as the revision info for nodes in + txns is bogus. + + This shortcut is quick and will exit this function upon success. + So, try it first. */ + if (!root->is_txn_root) + { + dag_node_t *node; + SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool)); + + /* Did the shortcut work? */ + if (node) + { + /* Construct and return the result. */ + svn_pool_destroy(iterpool); + + parent_path = make_parent_path(node, 0, 0, pool); + parent_path->copy_inherit = copy_id_inherit_self; + *parent_path_p = parent_path; + + return SVN_NO_ERROR; + } + } + + /* Second attempt: Try starting the lookup immediately at the parent + node. We will often have recently accessed either a sibling or + said parent DIRECTORY itself for the same revision. */ + directory = svn_dirent_dirname(path, pool); + if (directory[1] != 0) /* root nodes are covered anyway */ + { + SVN_ERR(dag_node_cache_get(&here, root, directory, pool)); + + /* Did the shortcut work? */ + if (here) + { + apr_size_t dirname_len = strlen(directory); + path_so_far->len = dirname_len; + rest = path + dirname_len + 1; + } + } + } + + /* did the shortcut work? */ + if (!here) + { + /* Make a parent_path item for the root node, using its own current + copy id. */ + SVN_ERR(root_node(&here, root, pool, iterpool)); + rest = path + 1; /* skip the leading '/', it saves in iteration */ + } + + path_so_far->data[path_so_far->len] = '\0'; + 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); + + /* The NODE in PARENT_PATH always lives in POOL, i.e. it will + * survive the cleanup of ITERPOOL and the DAG cache.*/ + here = parent_path->node; + + /* Parse out the next entry from the path. */ + entry = svn_fs__next_entry_name(&next, rest, pool); + + /* Update the path traversed thus far. */ + path_so_far->data[path_so_far->len] = '/'; + path_so_far->len += strlen(entry) + 1; + path_so_far->data[path_so_far->len] = '\0'; + + /* Given the behavior of svn_fs__next_entry_name(), ENTRY may be an + empty string 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. We only need to + process non-empty path segments. */ + if (*entry != '\0') + { + copy_id_inherit_t inherit; + const char *copy_path = NULL; + 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->data, + pool)); + if (cached_node) + child = cached_node; + else + SVN_ERR(svn_fs_x__dag_open(&child, here, entry, pool, iterpool)); + + /* "file not found" requires special handling. */ + if (child == NULL) + { + /* 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. */ + + if ((flags & open_path_last_optional) + && (! next || *next == '\0')) + { + parent_path = make_parent_path(NULL, entry, parent_path, + pool); + break; + } + else if (flags & open_path_allow_null) + { + parent_path = NULL; + break; + } + else + { + /* Build a better error message than svn_fs_x__dag_open + can provide, giving the root and full path name. */ + return SVN_FS__NOT_FOUND(root, path); + } + } + + if (flags & open_path_node_only) + { + /* Shortcut: the caller only wants the final DAG node. */ + parent_path->node = svn_fs_x__dag_copy_into_pool(child, pool); + } + else + { + /* Now, make a parent_path item for CHILD. */ + parent_path = make_parent_path(child, entry, parent_path, pool); + if (is_txn_path) + { + SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs, + parent_path, 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->data, 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_x__dag_node_kind(child) != svn_node_dir) + SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data), + apr_psprintf(iterpool, _("Failure opening '%s'"), path)); + + rest = next; + } + + 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 RESULT_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. Use SCRATCH_POOL for temporaries. */ +static svn_error_t * +make_path_mutable(svn_fs_root_t *root, + parent_path_t *parent_path, + const char *error_path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + dag_node_t *clone; + svn_fs_x__txn_id_t txn_id = root_txn_id(root); + + /* Is the node mutable already? */ + if (svn_fs_x__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) + { + svn_fs_x__id_t copy_id = { SVN_INVALID_REVNUM, 0 }; + svn_fs_x__id_t *copy_id_ptr = ©_id; + 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; + svn_boolean_t related; + + /* 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, result_pool, scratch_pool)); + + switch (inherit) + { + case copy_id_inherit_parent: + SVN_ERR(svn_fs_x__dag_get_copy_id(©_id, + parent_path->parent->node)); + break; + + case copy_id_inherit_new: + SVN_ERR(svn_fs_x__reserve_copy_id(©_id, root->fs, txn_id, + scratch_pool)); + break; + + case copy_id_inherit_self: + copy_id_ptr = 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_x__dag_get_copyroot(©root_rev, ©root_path, + parent_path->node)); + SVN_ERR(svn_fs_x__revision_root(©root_root, root->fs, + copyroot_rev, scratch_pool)); + SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, + result_pool)); + + SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node, + parent_path->node)); + if (!related) + is_parent_copyroot = TRUE; + + /* Now make this node mutable. */ + clone_path = parent_path_path(parent_path->parent, scratch_pool); + SVN_ERR(svn_fs_x__dag_clone_child(&clone, + parent_path->parent->node, + clone_path, + parent_path->entry, + copy_id_ptr, txn_id, + is_parent_copyroot, + result_pool, + scratch_pool)); + + /* Update the path cache. */ + SVN_ERR(dag_node_cache_set(root, + parent_path_path(parent_path, scratch_pool), + clone, scratch_pool)); + } + else + { + /* We're trying to clone the root directory. */ + SVN_ERR(mutable_root_node(&clone, root, error_path, result_pool, + scratch_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. + */ +static svn_error_t * +get_dag(dag_node_t **dag_node_p, + svn_fs_root_t *root, + const char *path, + 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, pool)); + + if (! node) + { + /* Canonicalize the input PATH. As it turns out, >95% of all paths + * seen here during e.g. svnadmin verify are non-canonical, i.e. + * miss the leading '/'. Unconditional canonicalization has a net + * performance benefit over previously checking path for being + * canonical. */ + path = svn_fs__canonicalize_abspath(path, 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, + FALSE, pool)); + node = parent_path->node; + + /* No need to cache our find -- open_path() will do that for us. */ + } + } + + *dag_node_p = svn_fs_x__dag_copy_into_pool(node, pool); + 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, PROP_MODs or + MERGEINFO_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. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +add_change(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const char *path, + const svn_fs_x__id_t *noderev_id, + svn_fs_path_change_kind_t change_kind, + svn_boolean_t text_mod, + svn_boolean_t prop_mod, + svn_boolean_t mergeinfo_mod, + svn_node_kind_t node_kind, + svn_revnum_t copyfrom_rev, + const char *copyfrom_path, + apr_pool_t *scratch_pool) +{ + return svn_fs_x__add_change(fs, txn_id, + svn_fs__canonicalize_abspath(path, + scratch_pool), + noderev_id, change_kind, + text_mod, prop_mod, mergeinfo_mod, + node_kind, copyfrom_rev, copyfrom_path, + scratch_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. */ +static svn_error_t * +x_node_id(const svn_fs_id_t **id_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) +{ + svn_fs_x__id_t noderev_id; + + 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. */ + svn_fs_x__init_rev_root(&noderev_id, root->rev); + } + else + { + dag_node_t *node; + + SVN_ERR(get_dag(&node, root, path, pool)); + noderev_id = *svn_fs_x__dag_get_id(node); + } + + *id_p = svn_fs_x__id_create(svn_fs_x__id_create_context(root->fs, pool), + &noderev_id, pool); + + return SVN_NO_ERROR; +} + +static svn_error_t * +x_node_relation(svn_fs_node_relation_t *relation, + svn_fs_root_t *root_a, + const char *path_a, + svn_fs_root_t *root_b, + const char *path_b, + apr_pool_t *scratch_pool) +{ + dag_node_t *node; + svn_fs_x__id_t noderev_id_a, noderev_id_b, node_id_a, node_id_b; + + /* Root paths are a common special case. */ + svn_boolean_t a_is_root_dir + = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0')); + svn_boolean_t b_is_root_dir + = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0')); + + /* Path from different repository are always unrelated. */ + if (root_a->fs != root_b->fs) + { + *relation = svn_fs_node_unrelated; + return SVN_NO_ERROR; + } + + /* Are both (!) root paths? Then, they are related and we only test how + * direct the relation is. */ + if (a_is_root_dir && b_is_root_dir) + { + svn_boolean_t different_txn + = root_a->is_txn_root && root_b->is_txn_root + && strcmp(root_a->txn, root_b->txn); + + /* For txn roots, root->REV is the base revision of that TXN. */ + *relation = ( (root_a->rev == root_b->rev) + && (root_a->is_txn_root == root_b->is_txn_root) + && !different_txn) + ? svn_fs_node_unchanged + : svn_fs_node_common_ancestor; + return SVN_NO_ERROR; + } + + /* We checked for all separations between ID spaces (repos, txn). + * Now, we can simply test for the ID values themselves. */ + SVN_ERR(get_dag(&node, root_a, path_a, scratch_pool)); + noderev_id_a = *svn_fs_x__dag_get_id(node); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_a, node)); + + SVN_ERR(get_dag(&node, root_b, path_b, scratch_pool)); + noderev_id_b = *svn_fs_x__dag_get_id(node); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_b, node)); + + /* In FSX, even in-txn IDs are globally unique. + * So, we can simply compare them. */ + if (svn_fs_x__id_eq(&noderev_id_a, &noderev_id_b)) + *relation = svn_fs_node_unchanged; + else if (svn_fs_x__id_eq(&node_id_a, &node_id_b)) + *relation = svn_fs_node_common_ancestor; + else + *relation = svn_fs_node_unrelated; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__node_created_rev(svn_revnum_t *revision, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + dag_node_t *node; + + SVN_ERR(get_dag(&node, root, path, scratch_pool)); + *revision = svn_fs_x__dag_get_revision(node); + + return SVN_NO_ERROR; +} + + +/* Set *CREATED_PATH to the path at which PATH under ROOT was created. + Return a string allocated in POOL. */ +static svn_error_t * +x_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, pool)); + *created_path = svn_fs_x__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 SCRATCH_POOL. */ +static svn_error_t * +node_kind(svn_node_kind_t *kind_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + dag_node_t *node; + + /* Get the node id. */ + SVN_ERR(get_dag(&node, root, path, scratch_pool)); + + /* Use the node id to get the real kind. */ + *kind_p = svn_fs_x__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 + SCRATCH_POOL for temporary allocation. */ +svn_error_t * +svn_fs_x__check_path(svn_node_kind_t *kind_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = node_kind(kind_p, root, path, scratch_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 * +x_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; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + SVN_ERR(get_dag(&node, root, path, pool)); + SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, node, pool, scratch_pool)); + *value_p = NULL; + if (proplist) + *value_p = svn_hash_gets(proplist, propname); + + svn_pool_destroy(scratch_pool); + 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 * +x_node_proplist(apr_hash_t **table_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) +{ + dag_node_t *node; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + SVN_ERR(get_dag(&node, root, path, pool)); + SVN_ERR(svn_fs_x__dag_get_proplist(table_p, node, pool, scratch_pool)); + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + +static svn_error_t * +x_node_has_props(svn_boolean_t *has_props, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + apr_hash_t *props; + + SVN_ERR(x_node_proplist(&props, root, path, scratch_pool)); + + *has_props = (0 < apr_hash_count(props)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +increment_mergeinfo_up_tree(parent_path_t *pp, + apr_int64_t increment, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + for (; pp; pp = pp->parent) + { + svn_pool_clear(iterpool); + SVN_ERR(svn_fs_x__dag_increment_mergeinfo_count(pp->node, + increment, + iterpool)); + } + + svn_pool_destroy(iterpool); + 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 SCRATCH_POOL. */ +static svn_error_t * +x_change_node_prop(svn_fs_root_t *root, + const char *path, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + parent_path_t *parent_path; + apr_hash_t *proplist; + svn_fs_x__txn_id_t txn_id; + svn_boolean_t mergeinfo_mod = FALSE; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + if (! root->is_txn_root) + return SVN_FS__NOT_TXN(root); + txn_id = root_txn_id(root); + + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool)); + + /* 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_x__allow_locked_operation(path, root->fs, FALSE, FALSE, + subpool)); + + SVN_ERR(make_path_mutable(root, parent_path, path, subpool, subpool)); + SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, parent_path->node, subpool, + subpool)); + + /* 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(subpool); + + if (strcmp(name, SVN_PROP_MERGEINFO) == 0) + { + apr_int64_t increment = 0; + svn_boolean_t had_mergeinfo; + SVN_ERR(svn_fs_x__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, subpool)); + SVN_ERR(svn_fs_x__dag_set_has_mergeinfo(parent_path->node, + (value != NULL), subpool)); + } + + mergeinfo_mod = TRUE; + } + + /* Set the property. */ + svn_hash_sets(proplist, name, value); + + /* Overwrite the node's proplist. */ + SVN_ERR(svn_fs_x__dag_set_proplist(parent_path->node, proplist, + subpool)); + + /* Make a record of this modification in the changes table. */ + SVN_ERR(add_change(root->fs, txn_id, path, + svn_fs_x__dag_get_id(parent_path->node), + svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod, + svn_fs_x__dag_node_kind(parent_path->node), + SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + +/* 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 * +x_props_changed(svn_boolean_t *changed_p, + svn_fs_root_t *root1, + const char *path1, + svn_fs_root_t *root2, + const char *path2, + svn_boolean_t strict, + apr_pool_t *scratch_pool) +{ + dag_node_t *node1, *node2; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + /* 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, subpool)); + SVN_ERR(get_dag(&node2, root2, path2, subpool)); + SVN_ERR(svn_fs_x__dag_things_different(changed_p, NULL, node1, node2, + strict, subpool)); + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + + + +/* 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, "/", 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); +} + +/* Compare the directory representations at nodes LHS and RHS in FS and set + * *CHANGED to TRUE, if at least one entry has been added or removed them. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +compare_dir_structure(svn_boolean_t *changed, + svn_fs_t *fs, + dag_node_t *lhs, + dag_node_t *rhs, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *lhs_entries; + apr_array_header_t *rhs_entries; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_fs_x__dag_dir_entries(&lhs_entries, lhs, scratch_pool, + iterpool)); + SVN_ERR(svn_fs_x__dag_dir_entries(&rhs_entries, rhs, scratch_pool, + iterpool)); + + /* different number of entries -> some addition / removal */ + if (lhs_entries->nelts != rhs_entries->nelts) + { + svn_pool_destroy(iterpool); + *changed = TRUE; + + return SVN_NO_ERROR; + } + + /* Since directories are sorted by name, we can simply compare their + entries one-by-one without binary lookup etc. */ + for (i = 0; i < lhs_entries->nelts; ++i) + { + svn_fs_x__dirent_t *lhs_entry + = APR_ARRAY_IDX(lhs_entries, i, svn_fs_x__dirent_t *); + svn_fs_x__dirent_t *rhs_entry + = APR_ARRAY_IDX(rhs_entries, i, svn_fs_x__dirent_t *); + + if (strcmp(lhs_entry->name, rhs_entry->name) == 0) + { + svn_boolean_t same_history; + dag_node_t *lhs_node, *rhs_node; + + /* Unchanged entry? */ + if (!svn_fs_x__id_eq(&lhs_entry->id, &rhs_entry->id)) + continue; + + /* We get here rarely. */ + svn_pool_clear(iterpool); + + /* Modified but not copied / replaced or anything? */ + SVN_ERR(svn_fs_x__dag_get_node(&lhs_node, fs, &lhs_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&rhs_node, fs, &rhs_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__dag_same_line_of_history(&same_history, + lhs_node, rhs_node)); + if (same_history) + continue; + } + + /* This is a different entry. */ + *changed = TRUE; + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; + } + + svn_pool_destroy(iterpool); + *changed = FALSE; + + return SVN_NO_ERROR; +} + +/* 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, + svn_fs_x__txn_id_t txn_id, + apr_int64_t *mergeinfo_increment_out, + apr_pool_t *pool) +{ + const svn_fs_x__id_t *source_id, *target_id, *ancestor_id; + apr_array_header_t *s_entries, *t_entries, *a_entries; + int i, s_idx = -1, t_idx = -1; + svn_fs_t *fs; + apr_pool_t *iterpool; + apr_int64_t mergeinfo_increment = 0; + + /* Make sure everyone comes from the same filesystem. */ + fs = svn_fs_x__dag_get_fs(ancestor); + if ((fs != svn_fs_x__dag_get_fs(source)) + || (fs != svn_fs_x__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_x__dag_get_id(source); + target_id = svn_fs_x__dag_get_id(target); + ancestor_id = svn_fs_x__dag_get_id(ancestor); + + /* It's improper to call this function with ancestor == target. */ + if (svn_fs_x__id_eq(ancestor_id, target_id)) + { + svn_string_t *id_str = svn_fs_x__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_x__id_eq(ancestor_id, source_id) + || (svn_fs_x__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_x__dag_node_kind(source) != svn_node_dir) + || (svn_fs_x__dag_node_kind(target) != svn_node_dir) + || (svn_fs_x__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. + */ + { + svn_fs_x__noderev_t *tgt_nr, *anc_nr, *src_nr; + svn_boolean_t same; + apr_pool_t *scratch_pool; + + /* Get node revisions for our id's. */ + scratch_pool = svn_pool_create(pool); + SVN_ERR(svn_fs_x__get_node_revision(&tgt_nr, fs, target_id, + pool, scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_x__get_node_revision(&anc_nr, fs, ancestor_id, + pool, scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_x__get_node_revision(&src_nr, fs, source_id, + pool, scratch_pool)); + svn_pool_destroy(scratch_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. */ + SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, src_nr, anc_nr, TRUE, pool)); + if (! same) + return conflict_err(conflict_p, target_path); + + /* The directory entries got changed in the repository but the directory + properties did not. */ + SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, tgt_nr, anc_nr, TRUE, pool)); + if (! same) + { + /* There is an incoming prop change for this directory. + We will accept it only if the directory changes were mere updates + to its entries, i.e. there were no additions or removals. + Those could cause update problems to the working copy. */ + svn_boolean_t changed; + SVN_ERR(compare_dir_structure(&changed, fs, source, ancestor, pool)); + + if (changed) + 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... */ + iterpool = svn_pool_create(pool); + SVN_ERR(svn_fs_x__dag_dir_entries(&s_entries, source, pool, iterpool)); + SVN_ERR(svn_fs_x__dag_dir_entries(&t_entries, target, pool, iterpool)); + SVN_ERR(svn_fs_x__dag_dir_entries(&a_entries, ancestor, pool, iterpool)); + + /* for each entry E in a_entries... */ + for (i = 0; i < a_entries->nelts; ++i) + { + svn_fs_x__dirent_t *s_entry, *t_entry, *a_entry; + svn_pool_clear(iterpool); + + a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_x__dirent_t *); + s_entry = svn_fs_x__find_dir_entry(s_entries, a_entry->name, &s_idx); + t_entry = svn_fs_x__find_dir_entry(t_entries, a_entry->name, &t_idx); + + /* No changes were made to this entry while the transaction was + in progress, so do nothing to the target. */ + if (s_entry && svn_fs_x__id_eq(&a_entry->id, &s_entry->id)) + continue; + + /* 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_x__id_eq(&a_entry->id, &t_entry->id)) + { + apr_int64_t mergeinfo_start; + apr_int64_t mergeinfo_end; + + dag_node_t *t_ent_node; + SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, &t_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__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_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool, iterpool)); + + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_end, + s_ent_node)); + mergeinfo_increment += mergeinfo_end; + + SVN_ERR(svn_fs_x__dag_set_entry(target, a_entry->name, + &s_entry->id, + s_entry->kind, + txn_id, + iterpool)); + } + else + { + SVN_ERR(svn_fs_x__dag_delete(target, a_entry->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; + svn_boolean_t s_a_same, t_a_same; + + /* 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)); + + /* Fetch DAG nodes to efficiently access ID parts. */ + SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, &t_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&a_ent_node, fs, &a_entry->id, + iterpool, iterpool)); + + /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct + modification of ANCESTOR-ENTRY, declare a conflict. */ + SVN_ERR(svn_fs_x__dag_same_line_of_history(&s_a_same, s_ent_node, + a_ent_node)); + SVN_ERR(svn_fs_x__dag_same_line_of_history(&t_a_same, t_ent_node, + a_ent_node)); + if (!s_a_same || !t_a_same) + 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. */ + 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)); + mergeinfo_increment += sub_mergeinfo_increment; + } + } + + /* For each entry E in source but not in ancestor */ + for (i = 0; i < s_entries->nelts; ++i) + { + svn_fs_x__dirent_t *a_entry, *s_entry, *t_entry; + dag_node_t *s_ent_node; + apr_int64_t mergeinfo_s; + + svn_pool_clear(iterpool); + + s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_x__dirent_t *); + a_entry = svn_fs_x__find_dir_entry(a_entries, s_entry->name, &s_idx); + t_entry = svn_fs_x__find_dir_entry(t_entries, s_entry->name, &t_idx); + + /* Process only entries in source that are NOT in ancestor. */ + if (a_entry) + continue; + + /* 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_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool, iterpool)); + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_s, s_ent_node)); + mergeinfo_increment += mergeinfo_s; + + SVN_ERR(svn_fs_x__dag_set_entry + (target, s_entry->name, &s_entry->id, s_entry->kind, + txn_id, iterpool)); + } + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_x__dag_update_ancestry(target, source, pool)); + + SVN_ERR(svn_fs_x__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 *scratch_pool) +{ + dag_node_t *txn_root_node; + svn_fs_t *fs = txn->fs; + svn_fs_x__txn_id_t txn_id = svn_fs_x__txn_get_id(txn); + svn_boolean_t related; + + SVN_ERR(svn_fs_x__dag_txn_root(&txn_root_node, fs, txn_id, scratch_pool, + scratch_pool)); + + if (ancestor_node == NULL) + { + svn_revnum_t base_rev; + SVN_ERR(svn_fs_x__get_base_rev(&base_rev, fs, txn_id, scratch_pool)); + SVN_ERR(svn_fs_x__dag_revision_root(&ancestor_node, fs, base_rev, + scratch_pool, scratch_pool)); + } + + SVN_ERR(svn_fs_x__dag_related_node(&related, ancestor_node, txn_root_node)); + if (!related) + { + /* 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, scratch_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_x__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; + svn_fs_x__data_t *ffd = fs->fsap_data; + + /* 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_x__youngest_rev(&youngish_rev, fs, iterpool)); + SVN_ERR(svn_fs_x__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_x__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_x__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_pool_destroy(iterpool); + + SVN_ERR(err); + + if (ffd->pack_after_commit) + { + SVN_ERR(svn_fs_x__pack(fs, NULL, NULL, NULL, NULL, pool)); + } + + return SVN_NO_ERROR; +} + + +/* 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 * +x_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 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_x__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_x__deltify(svn_fs_t *fs, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + /* Deltify is a no-op for fs_x. */ + + 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_svn_fs_x__dirent_t + structures. Allocate the table and its contents in POOL. */ +static svn_error_t * +x_dir_entries(apr_hash_t **table_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) +{ + dag_node_t *node; + apr_hash_t *hash = svn_hash__make(pool); + apr_array_header_t *table; + int i; + svn_fs_x__id_context_t *context = NULL; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + /* Get the entries for this path in the caller's pool. */ + SVN_ERR(get_dag(&node, root, path, scratch_pool)); + SVN_ERR(svn_fs_x__dag_dir_entries(&table, node, scratch_pool, + scratch_pool)); + + if (table->nelts) + context = svn_fs_x__id_create_context(root->fs, pool); + + /* Convert directory array to hash. */ + for (i = 0; i < table->nelts; ++i) + { + svn_fs_x__dirent_t *entry + = APR_ARRAY_IDX(table, i, svn_fs_x__dirent_t *); + apr_size_t len = strlen(entry->name); + + svn_fs_dirent_t *api_dirent = apr_pcalloc(pool, sizeof(*api_dirent)); + api_dirent->name = apr_pstrmemdup(pool, entry->name, len); + api_dirent->kind = entry->kind; + api_dirent->id = svn_fs_x__id_create(context, &entry->id, pool); + + apr_hash_set(hash, api_dirent->name, len, api_dirent); + } + + *table_p = hash; + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} + +static svn_error_t * +x_dir_optimal_order(apr_array_header_t **ordered_p, + svn_fs_root_t *root, + apr_hash_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *ordered_p = svn_fs_x__order_dir_entries(root->fs, entries, result_pool, + scratch_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 SCRATCH_POOL. */ +static svn_error_t * +x_make_dir(svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + parent_path_t *parent_path; + dag_node_t *sub_dir; + svn_fs_x__txn_id_t txn_id = root_txn_id(root); + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, + TRUE, subpool)); + + /* 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_x__allow_locked_operation(path, root->fs, TRUE, FALSE, + subpool)); + + /* 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, subpool, + subpool)); + SVN_ERR(svn_fs_x__dag_make_dir(&sub_dir, + parent_path->parent->node, + parent_path_path(parent_path->parent, + subpool), + parent_path->entry, + txn_id, + subpool, subpool)); + + /* Add this directory to the path cache. */ + SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool), + sub_dir, subpool)); + + /* Make a record of this modification in the changes table. */ + SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(sub_dir), + svn_fs_path_change_add, FALSE, FALSE, FALSE, + svn_node_dir, SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + +/* Delete the node at PATH under ROOT. ROOT must be a transaction + root. Perform temporary allocations in SCRATCH_POOL. */ +static svn_error_t * +x_delete_node(svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + parent_path_t *parent_path; + svn_fs_x__txn_id_t txn_id; + apr_int64_t mergeinfo_count = 0; + svn_node_kind_t kind; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + if (! root->is_txn_root) + return SVN_FS__NOT_TXN(root); + + txn_id = root_txn_id(root); + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool)); + kind = svn_fs_x__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_x__allow_locked_operation(path, root->fs, TRUE, FALSE, + subpool)); + + /* Make the parent directory mutable, and do the deletion. */ + SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool, + subpool)); + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_count, + parent_path->node)); + SVN_ERR(svn_fs_x__dag_delete(parent_path->parent->node, + parent_path->entry, + txn_id, subpool)); + + /* Remove this node and any children from the path cache. */ + SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, + subpool), + subpool)); + + /* Update mergeinfo counts for parents */ + if (mergeinfo_count > 0) + SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent, + -mergeinfo_count, + subpool)); + + /* Make a record of this modification in the changes table. */ + SVN_ERR(add_change(root->fs, txn_id, path, + svn_fs_x__dag_get_id(parent_path->node), + svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind, + SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + +/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE. + Use SCRATCH_POOL for temporary allocation only. + Note: this code is duplicated between libsvn_fs_x and libsvn_fs_base. */ +static svn_error_t * +x_same_p(svn_boolean_t *same_p, + svn_fs_t *fs1, + svn_fs_t *fs2, + apr_pool_t *scratch_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 SCRATCH_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 *scratch_pool) +{ + dag_node_t *from_node; + parent_path_t *to_parent_path; + svn_fs_x__txn_id_t txn_id = root_txn_id(to_root); + 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(x_same_p(&same_p, from_root->fs, to_root->fs, scratch_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); + + /* more things that we can't do ATM */ + if (from_root->is_txn_root) + return svn_error_create + (SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Copy from mutable tree not currently supported")); + + if (! to_root->is_txn_root) + return svn_error_create + (SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Copy immutable tree not supported")); + + /* Get the NODE for FROM_PATH in FROM_ROOT.*/ + SVN_ERR(get_dag(&from_node, from_root, from_path, scratch_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, TRUE, scratch_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_x__allow_locked_operation(to_path, to_root->fs, + TRUE, FALSE, scratch_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_x__id_eq(svn_fs_x__dag_get_id(from_node), + svn_fs_x__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; + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_start, + to_parent_path->node)); + } + else + { + kind = svn_fs_path_change_add; + mergeinfo_start = 0; + } + + SVN_ERR(svn_fs_x__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, scratch_pool, scratch_pool)); + + /* Canonicalize the copyfrom path. */ + from_canonpath = svn_fs__canonicalize_abspath(from_path, scratch_pool); + + SVN_ERR(svn_fs_x__dag_copy(to_parent_path->parent->node, + to_parent_path->entry, + from_node, + preserve_history, + from_root->rev, + from_canonpath, + txn_id, scratch_pool)); + + if (kind != svn_fs_path_change_add) + SVN_ERR(dag_node_cache_invalidate(to_root, + parent_path_path(to_parent_path, + scratch_pool), + scratch_pool)); + + if (mergeinfo_start != mergeinfo_end) + SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent, + mergeinfo_end - mergeinfo_start, + scratch_pool)); + + /* Make a record of this modification in the changes table. */ + SVN_ERR(get_dag(&new_node, to_root, to_path, scratch_pool)); + SVN_ERR(add_change(to_root->fs, txn_id, to_path, + svn_fs_x__dag_get_id(new_node), kind, FALSE, + FALSE, FALSE, svn_fs_x__dag_node_kind(from_node), + from_root->rev, from_canonpath, scratch_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 SCRATCH_POOL.*/ +static svn_error_t * +x_copy(svn_fs_root_t *from_root, + const char *from_path, + svn_fs_root_t *to_root, + const char *to_path, + apr_pool_t *scratch_pool) +{ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + SVN_ERR(copy_helper(from_root, + svn_fs__canonicalize_abspath(from_path, subpool), + to_root, + svn_fs__canonicalize_abspath(to_path, subpool), + TRUE, subpool)); + + svn_pool_destroy(subpool); + + 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. No history is + preserved. Temporary allocations are from SCRATCH_POOL. */ +static svn_error_t * +x_revision_link(svn_fs_root_t *from_root, + svn_fs_root_t *to_root, + const char *path, + apr_pool_t *scratch_pool) +{ + apr_pool_t *subpool; + + if (! to_root->is_txn_root) + return SVN_FS__NOT_TXN(to_root); + + subpool = svn_pool_create(scratch_pool); + + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(copy_helper(from_root, path, to_root, path, FALSE, subpool)); + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + + +/* 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 * +x_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; + + /* There is no cached entry, look it up the old-fashioned + way. */ + SVN_ERR(get_dag(&node, root, path, pool)); + SVN_ERR(svn_fs_x__dag_get_copyfrom_rev(rev_p, node)); + SVN_ERR(svn_fs_x__dag_get_copyfrom_path(path_p, node)); + + return SVN_NO_ERROR; +} + + + +/* Files. */ + +/* Create the empty file PATH under ROOT. Temporary allocations are + in SCRATCH_POOL. */ +static svn_error_t * +x_make_file(svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + parent_path_t *parent_path; + dag_node_t *child; + svn_fs_x__txn_id_t txn_id = root_txn_id(root); + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, + TRUE, subpool)); + + /* 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_x__allow_locked_operation(path, root->fs, FALSE, FALSE, + subpool)); + + /* Create the file. */ + SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool, + subpool)); + SVN_ERR(svn_fs_x__dag_make_file(&child, + parent_path->parent->node, + parent_path_path(parent_path->parent, + subpool), + parent_path->entry, + txn_id, + subpool, subpool)); + + /* Add this file to the path cache. */ + SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool), + child, subpool)); + + /* Make a record of this modification in the changes table. */ + SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(child), + svn_fs_path_change_add, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + +/* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary + allocations are in SCRATCH_POOL. */ +static svn_error_t * +x_file_length(svn_filesize_t *length_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + dag_node_t *file; + + /* First create a dag_node_t from the root/path pair. */ + SVN_ERR(get_dag(&file, root, path, scratch_pool)); + + /* Now fetch its length */ + return svn_fs_x__dag_file_length(length_p, file); +} + + +/* 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 * +x_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, pool)); + return svn_fs_x__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 * +x_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, pool)); + + /* Then create a readable stream from the dag_node_t. */ + SVN_ERR(svn_fs_x__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 * +x_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, pool)); + + return svn_fs_x__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; + + /* 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; + + +/* 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)); + + /* Is the window NULL? If so, we're done. The stream has already been + closed by the interpreter. */ + if (! window) + SVN_ERR(svn_fs_x__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 *scratch_pool) +{ + txdelta_baton_t *tb = (txdelta_baton_t *) baton; + parent_path_t *parent_path; + svn_fs_x__txn_id_t txn_id = root_txn_id(tb->root); + + /* 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, TRUE, scratch_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_x__allow_locked_operation(tb->path, tb->root->fs, + FALSE, FALSE, scratch_pool)); + + /* Now, make sure this path is mutable. */ + SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, scratch_pool, + scratch_pool)); + tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool); + + 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_x__dag_file_checksum(&checksum, tb->node, + tb->base_checksum->kind, + scratch_pool)); + if (!svn_checksum_match(tb->base_checksum, checksum)) + return svn_checksum_mismatch_err(tb->base_checksum, checksum, + scratch_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_x__dag_get_contents(&(tb->source_stream), + tb->node, tb->pool)); + + /* Make a writable "target" stream */ + SVN_ERR(svn_fs_x__dag_get_edit_stream(&(tb->target_stream), tb->node, + tb->pool)); + + /* Now, create a custom window handler that uses our two streams. */ + svn_txdelta_apply(tb->source_stream, + tb->target_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_x__dag_get_id(tb->node), + svn_fs_path_change_modify, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, scratch_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 * +x_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) +{ + apr_pool_t *scratch_pool = svn_pool_create(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, scratch_pool)); + + *contents_p = window_consumer; + *contents_baton_p = tb; + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + +/* --- End machinery for svn_fs_apply_textdelta() --- */ + +/* --- Machinery for svn_fs_apply_text() --- */ + +/* Baton for svn_fs_apply_text(). */ +typedef 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; +} text_baton_t; + + +/* A wrapper around svn_fs_x__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) +{ + 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) +{ + 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_x__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 *scratch_pool) +{ + text_baton_t *tb = baton; + parent_path_t *parent_path; + svn_fs_x__txn_id_t txn_id = root_txn_id(tb->root); + + /* 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, TRUE, scratch_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_x__allow_locked_operation(tb->path, tb->root->fs, + FALSE, FALSE, scratch_pool)); + + /* Now, make sure this path is mutable. */ + SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, scratch_pool, + scratch_pool)); + tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool); + + /* Make a writable stream for replacing the file's text. */ + SVN_ERR(svn_fs_x__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_x__dag_get_id(tb->node), + svn_fs_path_change_modify, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, scratch_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 * +x_apply_text(svn_stream_t **contents_p, + svn_fs_root_t *root, + const char *path, + svn_checksum_t *result_checksum, + apr_pool_t *pool) +{ + apr_pool_t *scratch_pool = svn_pool_create(pool); + 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, scratch_pool)); + + *contents_p = tb->stream; + + svn_pool_destroy(scratch_pool); + 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 * +x_contents_changed(svn_boolean_t *changed_p, + svn_fs_root_t *root1, + const char *path1, + svn_fs_root_t *root2, + const char *path2, + svn_boolean_t strict, + apr_pool_t *scratch_pool) +{ + dag_node_t *node1, *node2; + apr_pool_t *subpool = svn_pool_create(scratch_pool); + + /* 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_x__check_path(&kind, root1, path1, subpool)); + if (kind != svn_node_file) + return svn_error_createf + (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1); + + SVN_ERR(svn_fs_x__check_path(&kind, root2, path2, subpool)); + 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, subpool)); + SVN_ERR(get_dag(&node2, root2, path2, subpool)); + SVN_ERR(svn_fs_x__dag_things_different(NULL, changed_p, node1, node2, + strict, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + + + +/* Public interface to computing file text deltas. */ + +static svn_error_t * +x_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; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + if (source_root && source_path) + SVN_ERR(get_dag(&source_node, source_root, source_path, scratch_pool)); + else + source_node = NULL; + SVN_ERR(get_dag(&target_node, target_root, target_path, scratch_pool)); + + /* Create a delta stream that turns the source into the target. */ + SVN_ERR(svn_fs_x__dag_get_file_delta_stream(stream_p, source_node, + target_node, pool, + scratch_pool)); + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + + + +/* Finding Changes */ + +/* Copy CHANGE into a FS API object allocated in RESULT_POOL and return + it in *RESULT_P. Pass CONTEXT to the ID API object being created. */ +static svn_error_t * +construct_fs_path_change(svn_fs_path_change2_t **result_p, + svn_fs_x__id_context_t *context, + svn_fs_x__change_t *change, + apr_pool_t *result_pool) +{ + const svn_fs_id_t *id + = svn_fs_x__id_create(context, &change->noderev_id, result_pool); + svn_fs_path_change2_t *result + = svn_fs__path_change_create_internal(id, change->change_kind, + result_pool); + + result->text_mod = change->text_mod; + result->prop_mod = change->prop_mod; + result->node_kind = change->node_kind; + + result->copyfrom_known = change->copyfrom_known; + result->copyfrom_rev = change->copyfrom_rev; + result->copyfrom_path = change->copyfrom_path; + + result->mergeinfo_mod = change->mergeinfo_mod; + + *result_p = result; + + return SVN_NO_ERROR; +} + +/* 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 * +x_paths_changed(apr_hash_t **changed_paths_p, + svn_fs_root_t *root, + apr_pool_t *pool) +{ + apr_hash_t *changed_paths; + svn_fs_path_change2_t *path_change; + svn_fs_x__id_context_t *context + = svn_fs_x__id_create_context(root->fs, pool); + + if (root->is_txn_root) + { + apr_hash_index_t *hi; + SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, root->fs, + root_txn_id(root), pool)); + for (hi = apr_hash_first(pool, changed_paths); + hi; + hi = apr_hash_next(hi)) + { + svn_fs_x__change_t *change = apr_hash_this_val(hi); + SVN_ERR(construct_fs_path_change(&path_change, context, change, + pool)); + apr_hash_set(changed_paths, + apr_hash_this_key(hi), apr_hash_this_key_len(hi), + path_change); + } + } + else + { + apr_array_header_t *changes; + int i; + + SVN_ERR(svn_fs_x__get_changes(&changes, root->fs, root->rev, pool)); + + changed_paths = svn_hash__make(pool); + for (i = 0; i < changes->nelts; ++i) + { + svn_fs_x__change_t *change = APR_ARRAY_IDX(changes, i, + svn_fs_x__change_t *); + SVN_ERR(construct_fs_path_change(&path_change, context, change, + pool)); + apr_hash_set(changed_paths, change->path.data, change->path.len, + path_change); + } + } + + *changed_paths_p = changed_paths; + + return SVN_NO_ERROR; +} + + + +/* 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 *result_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 * +x_node_history(svn_fs_history_t **history_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *result_pool, + apr_pool_t *scratch_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_x__check_path(&kind, root, path, scratch_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, path, root->rev, FALSE, NULL, + SVN_INVALID_REVNUM, result_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. */ +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) +{ + 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)); + + /* Find our copyroot. */ + SVN_ERR(svn_fs_x__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 * +x_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_boolean_t related; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + /* Initialize return values. */ + *root_p = NULL; + *path_p = NULL; + + path = svn_fs__canonicalize_abspath(path, scratch_pool); + SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_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(©_dst_rev, ©_dst_path, + fs, parent_path)); + if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */ + { + svn_pool_destroy(scratch_pool); + 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_x__revision_root(©_dst_root, fs, copy_dst_rev, pool)); + SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path, + open_path_node_only | open_path_allow_null, FALSE, + scratch_pool)); + if (copy_dst_parent_path == NULL) + { + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; + } + + copy_dst_node = copy_dst_parent_path->node; + SVN_ERR(svn_fs_x__dag_related_node(&related, copy_dst_node, + parent_path->node)); + if (!related) + { + svn_pool_destroy(scratch_pool); + 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. + */ + created_rev = svn_fs_x__dag_get_revision(copy_dst_node); + if (created_rev == copy_dst_rev) + { + svn_fs_x__id_t pred; + SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred, copy_dst_node)); + if (!svn_fs_x__id_used(&pred)) + { + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; + } + } + + /* The copy destination checks out. Return it. */ + *root_p = copy_dst_root; + *path_p = apr_pstrdup(pool, copy_dst_path); + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; +} + + +static svn_error_t * +x_node_origin_rev(svn_revnum_t *revision, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool) +{ + svn_fs_x__id_t node_id; + dag_node_t *node; + + path = svn_fs__canonicalize_abspath(path, scratch_pool); + + SVN_ERR(get_dag(&node, root, path, scratch_pool)); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id, node)); + + *revision = svn_fs_x__get_revnum(node_id.change_set); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +history_prev(svn_fs_history_t **prev_history, + svn_fs_history_t *history, + svn_boolean_t cross_copies, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + 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; + 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 (! 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_x__revision_root(&root, fs, revision, scratch_pool)); + + /* Open PATH/REVISION, and get its node and a bunch of other + goodies. */ + SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool)); + node = parent_path->node; + commit_path = svn_fs_x__dag_get_created_path(node); + commit_rev = svn_fs_x__dag_get_revision(node); + + /* 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, commit_path, + commit_rev, TRUE, NULL, + SVN_INVALID_REVNUM, result_pool); + 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!). */ + svn_fs_x__id_t pred_id; + + SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, node)); + if (!svn_fs_x__id_used(&pred_id)) + return SVN_NO_ERROR; + + /* Replace NODE and friends with the information from its + predecessor. */ + SVN_ERR(svn_fs_x__dag_get_node(&node, fs, &pred_id, scratch_pool, + scratch_pool)); + commit_path = svn_fs_x__dag_get_created_path(node); + commit_rev = svn_fs_x__dag_get_revision(node); + } + } + + /* Find the youngest copyroot in the path of this node, including + itself. */ + SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs, + parent_path)); + + /* 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_x__revision_root(©root_root, fs, copyroot_rev, + scratch_pool)); + SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool)); + copy_dst = svn_fs_x__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_x__dag_get_copyfrom_rev(&src_rev, node)); + SVN_ERR(svn_fs_x__dag_get_copyfrom_path(©_src, node)); + + dst_rev = copyroot_rev; + src_path = svn_fspath__join(copy_src, remainder_path, scratch_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, path, dst_rev, ! retry, + src_path, src_rev, result_pool); + } + else + { + *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE, + NULL, SVN_INVALID_REVNUM, result_pool); + } + + 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 *result_pool, + apr_pool_t *scratch_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, + result_pool); + else if (fhd->revision > 0) + prev_history = assemble_history(fs, "/", fhd->revision - 1, + 1, NULL, SVN_INVALID_REVNUM, + result_pool); + } + else + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + prev_history = history; + + while (1) + { + svn_pool_clear(iterpool); + SVN_ERR(history_prev(&prev_history, prev_history, cross_copies, + result_pool, iterpool)); + + if (! prev_history) + break; + fhd = prev_history->fsap_data; + if (fhd->is_interesting) + break; + } + + svn_pool_destroy(iterpool); + } + + *prev_history_p = prev_history; + return SVN_NO_ERROR; +} + + +/* Set *PATH and *REVISION to the path and revision for the HISTORY + object. Allocate *PATH in RESULT_POOL. */ +static svn_error_t * +fs_history_location(const char **path, + svn_revnum_t *revision, + svn_fs_history_t *history, + apr_pool_t *result_pool) +{ + fs_history_data_t *fhd = history->fsap_data; + + *path = apr_pstrdup(result_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 RESULT_POOL, and with its members set to the + values of the parameters provided. Note that PATH and PATH_HINT get + normalized and duplicated in RESULT_POOL. */ +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 *result_pool) +{ + svn_fs_history_t *history = apr_pcalloc(result_pool, sizeof(*history)); + fs_history_data_t *fhd = apr_pcalloc(result_pool, sizeof(*fhd)); + fhd->path = svn_fs__canonicalize_abspath(path, result_pool); + fhd->revision = revision; + fhd->is_interesting = is_interesting; + fhd->path_hint = path_hint + ? svn_fs__canonicalize_abspath(path_hint, result_pool) + : NULL; + 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_array_header_t *entries; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_fs_x__dag_dir_entries(&entries, dir_dag, scratch_pool, + iterpool)); + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); + 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, iterpool)); + + SVN_ERR(svn_fs_x__dag_has_mergeinfo(&has_mergeinfo, kid_dag)); + SVN_ERR(svn_fs_x__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_x__dag_get_proplist(&proplist, kid_dag, iterpool, + iterpool)); + mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO); + if (!mergeinfo_string) + { + svn_string_t *idstr + = svn_fs_x__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 RESULT_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 *result_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_x__combine_number_and_string(number, path, result_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, FALSE, 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_x__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_x__dag_get_proplist(&proplist, nearest_ancestor->node, + scratch_pool, 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) +{ + svn_fs_x__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, scratch_pool)); + SVN_ERR(svn_fs_x__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 * +x_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) +{ + /* We require a revision root. */ + if (root->is_txn_root) + return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); + + /* 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 = { + x_paths_changed, + svn_fs_x__check_path, + x_node_history, + x_node_id, + x_node_relation, + svn_fs_x__node_created_rev, + x_node_origin_rev, + x_node_created_path, + x_delete_node, + x_copy, + x_revision_link, + x_copied_from, + x_closest_copy, + x_node_prop, + x_node_proplist, + x_node_has_props, + x_change_node_prop, + x_props_changed, + x_dir_entries, + x_dir_optimal_order, + x_make_dir, + x_file_length, + x_file_checksum, + x_file_contents, + x_try_process_file_contents, + x_make_file, + x_apply_textdelta, + x_apply_text, + x_contents_changed, + x_get_file_delta_stream, + x_merge, + x_get_mergeinfo, +}; + +/* Construct a new root object in FS, allocated from RESULT_POOL. */ +static svn_fs_root_t * +make_root(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + svn_fs_root_t *root = apr_pcalloc(result_pool, sizeof(*root)); + + root->fs = fs; + root->pool = result_pool; + root->vtable = &root_vtable; + + return root; +} + + +/* Construct a root object referring to the root of revision REV in FS. + Create the new root in RESULT_POOL. */ +static svn_fs_root_t * +make_revision_root(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + svn_fs_root_t *root = make_root(fs, result_pool); + + root->is_txn_root = FALSE; + root->rev = rev; + + 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 RESULT_POOL. */ +static svn_error_t * +make_txn_root(svn_fs_root_t **root_p, + svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + svn_revnum_t base_rev, + apr_uint32_t flags, + apr_pool_t *result_pool) +{ + svn_fs_root_t *root = make_root(fs, result_pool); + fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd)); + frd->txn_id = txn_id; + + root->is_txn_root = TRUE; + root->txn = svn_fs_x__txn_name(txn_id, root->pool); + root->txn_flags = flags; + root->rev = base_rev; + + /* 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_x__dag_serialize, + svn_fs_x__dag_deserialize, + APR_HASH_KEY_STRING, + 32, 20, FALSE, + root->txn, + root->pool)); + + root->fsap_data = frd; + + *root_p = root; + return SVN_NO_ERROR; +} + + + +/* Verify. */ +static const char * +stringify_node(dag_node_t *node, + apr_pool_t *result_pool) +{ + /* ### TODO: print some PATH@REV to it, too. */ + return svn_fs_x__id_unparse(svn_fs_x__dag_get_id(node), result_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. To detect cycles, + provide all parent dag_node_t * in PARENT_NODES. */ +static svn_error_t * +verify_node(dag_node_t *node, + svn_revnum_t rev, + apr_array_header_t *parent_nodes, + apr_pool_t *scratch_pool) +{ + svn_boolean_t has_mergeinfo; + apr_int64_t mergeinfo_count; + svn_fs_x__id_t pred_id; + svn_fs_t *fs = svn_fs_x__dag_get_fs(node); + int pred_count; + svn_node_kind_t kind; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + /* Detect (non-)DAG cycles. */ + for (i = 0; i < parent_nodes->nelts; ++i) + { + dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *); + if (svn_fs_x__id_eq(svn_fs_x__dag_get_id(parent), + svn_fs_x__dag_get_id(node))) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Node is its own direct or indirect parent '%s'", + stringify_node(node, iterpool)); + } + + /* Fetch some data. */ + SVN_ERR(svn_fs_x__dag_has_mergeinfo(&has_mergeinfo, node)); + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_count, node)); + SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, node)); + SVN_ERR(svn_fs_x__dag_get_predecessor_count(&pred_count, node)); + kind = svn_fs_x__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 (svn_fs_x__id_used(&pred_id)) + { + dag_node_t *pred; + int pred_pred_count; + SVN_ERR(svn_fs_x__dag_get_node(&pred, fs, &pred_id, iterpool, + iterpool)); + SVN_ERR(svn_fs_x__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_array_header_t *entries; + apr_int64_t children_mergeinfo = 0; + APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node; + + SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool, + iterpool)); + + /* Compute CHILDREN_MERGEINFO. */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__dirent_t *dirent + = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); + dag_node_t *child; + apr_int64_t child_mergeinfo; + + svn_pool_clear(iterpool); + + /* Compute CHILD_REV. */ + if (svn_fs_x__get_revnum(dirent->id.change_set) == rev) + { + SVN_ERR(svn_fs_x__dag_get_node(&child, fs, &dirent->id, + iterpool, iterpool)); + SVN_ERR(verify_node(child, rev, parent_nodes, iterpool)); + SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&child_mergeinfo, + child)); + } + else + { + SVN_ERR(svn_fs_x__get_mergeinfo_count(&child_mergeinfo, fs, + &dirent->id, iterpool)); + } + + 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); + + /* If we don't make it here, there was an error / corruption. + * In that case, nobody will need PARENT_NODES anymore. */ + apr_array_pop(parent_nodes); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__verify_root(svn_fs_root_t *root, + apr_pool_t *scratch_pool) +{ + dag_node_t *root_dir; + apr_array_header_t *parent_nodes; + + /* 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_FSX_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. */ + SVN_ERR(root_node(&root_dir, root, scratch_pool, scratch_pool)); + + /* Recursively verify ROOT_DIR. */ + parent_nodes = apr_array_make(scratch_pool, 16, sizeof(dag_node_t *)); + SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, scratch_pool)); + + /* Verify explicitly the predecessor of the root. */ + { + svn_fs_x__id_t pred_id; + svn_boolean_t has_predecessor; + + /* Only r0 should have no predecessor. */ + SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, root_dir)); + has_predecessor = svn_fs_x__id_used(&pred_id); + if (!root->is_txn_root && has_predecessor != !!root->rev) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "r%ld's root node's predecessor is " + "unexpectedly '%s'", + root->rev, + (has_predecessor + ? svn_fs_x__id_unparse(&pred_id, + scratch_pool)->data + : "(null)")); + if (root->is_txn_root && !has_predecessor) + 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 (has_predecessor) + { + svn_revnum_t pred_rev = svn_fs_x__get_revnum(pred_id.change_set); + 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; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/tree.h b/contrib/subversion/subversion/libsvn_fs_x/tree.h new file mode 100644 index 000000000..9c5d44aba --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/tree.h @@ -0,0 +1,112 @@ +/* tree.h : internal interface to tree node functions + * + * ==================================================================== + * 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_FS_TREE_H +#define SVN_LIBSVN_FS_TREE_H + +#include "fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* In RESULT_POOL, create an instance of a DAG node 1st level cache. */ +svn_fs_x__dag_cache_t* +svn_fs_x__create_dag_cache(apr_pool_t *result_pool); + +/* Set *ROOT_P to the root directory of revision REV in filesystem FS. + Allocate the structure in POOL. */ +svn_error_t * +svn_fs_x__revision_root(svn_fs_root_t **root_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool); + +/* Does nothing, but included for Subversion 1.0.x compatibility. */ +svn_error_t * +svn_fs_x__deltify(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *scratch_pool); + +/* Commit the transaction TXN as a new revision. Return the new + revision in *NEW_REV. If the transaction conflicts with other + changes return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a string + that details the cause of the conflict. */ +svn_error_t * +svn_fs_x__commit_txn(const char **conflict_p, + svn_revnum_t *new_rev, + svn_fs_txn_t *txn, + apr_pool_t *pool); + +/* Set ROOT_P to the root directory of transaction TXN. Allocate the + structure in POOL. */ +svn_error_t * +svn_fs_x__txn_root(svn_fs_root_t **root_p, + svn_fs_txn_t *txn, + apr_pool_t *pool); + + +/* Set KIND_P to the node kind of the node at PATH in ROOT. + Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__check_path(svn_node_kind_t *kind_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool); + +/* Set *REVISION to the revision in which PATH under ROOT was created. + Use SCRATCH_POOL for any temporary allocations. If PATH is in an + uncommitted transaction, *REVISION will be set to + SVN_INVALID_REVNUM. */ +svn_error_t * +svn_fs_x__node_created_rev(svn_revnum_t *revision, + svn_fs_root_t *root, + const char *path, + apr_pool_t *scratch_pool); + +/* Verify metadata for ROOT. + ### Currently only implemented for revision roots. */ +svn_error_t * +svn_fs_x__verify_root(svn_fs_root_t *root, + apr_pool_t *scratch_pool); + +svn_error_t * +svn_fs_x__info_format(int *fs_format, + svn_version_t **supports_version, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +svn_error_t * +svn_fs_x__info_config_files(apr_array_header_t **files, + svn_fs_t *fs, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_FS_TREE_H */ diff --git a/contrib/subversion/subversion/libsvn_fs_x/util.c b/contrib/subversion/subversion/libsvn_fs_x/util.c new file mode 100644 index 000000000..da004ad8f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/util.c @@ -0,0 +1,777 @@ +/* util.c --- utility functions for FSX repo access + * + * ==================================================================== + * 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 + +#include "svn_ctype.h" +#include "svn_dirent_uri.h" +#include "private/svn_string_private.h" + +#include "fs_x.h" +#include "id.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +/* Following are defines that specify the textual elements of the + native filesystem directories and revision files. */ + +/* 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. + +*/ + +/* Pathname helper functions */ + +/* Return TRUE is REV is packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_x__is_packed_rev(svn_fs_t *fs, svn_revnum_t rev) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + return (rev < ffd->min_unpacked_rev); +} + +/* Return TRUE is REV is packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_x__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + /* rev 0 will not be packed */ + return (rev < ffd->min_unpacked_rev) && (rev != 0); +} + +svn_revnum_t +svn_fs_x__packed_base_rev(svn_fs_t *fs, svn_revnum_t rev) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + return rev < ffd->min_unpacked_rev + ? rev - (rev % ffd->max_files_per_dir) + : rev; +} + +svn_revnum_t +svn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + + return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1; +} + +const char * +svn_fs_x__path_format(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_FORMAT, result_pool); +} + +const char * +svn_fs_x__path_uuid(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_UUID, result_pool); +} + +const char * +svn_fs_x__path_current(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_CURRENT, result_pool); +} + +const char * +svn_fs_x__path_txn_current(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT, + result_pool); +} + +const char * +svn_fs_x__path_txn_current_lock(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool); +} + +const char * +svn_fs_x__path_lock(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool); +} + +const char * +svn_fs_x__path_pack_lock(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool); +} + +const char * +svn_fs_x__path_revprop_generation(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool); +} + +/* Return the full path of the file FILENAME within revision REV's shard in + * FS. If FILENAME is NULL, return the shard directory directory itself. + * REVPROPS indicates the parent of the shard parent folder ("revprops" or + * "revs"). PACKED says whether we want the packed shard's name. + * + * Allocate the result in RESULT_POOL. + */static const char* +construct_shard_sub_path(svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t revprops, + svn_boolean_t packed, + const char *filename, + apr_pool_t *result_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 }; + + /* Select the appropriate parent path constant. */ + const char *parent = revprops ? PATH_REVPROPS_DIR : PATH_REVS_DIR; + + /* String containing the shard number. */ + apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir); + + /* Append the suffix. Limit it to the buffer size (should never hit it). */ + if (packed) + strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1); + + /* This will also work for NULL FILENAME as well. */ + return svn_dirent_join_many(result_pool, fs->path, parent, buffer, + filename, SVN_VA_NULL); +} + +const char * +svn_fs_x__path_rev_packed(svn_fs_t *fs, + svn_revnum_t rev, + const char *kind, + apr_pool_t *result_pool) +{ + assert(svn_fs_x__is_packed_rev(fs, rev)); + return construct_shard_sub_path(fs, rev, FALSE, TRUE, kind, result_pool); +} + +const char * +svn_fs_x__path_rev_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + return construct_shard_sub_path(fs, rev, FALSE, FALSE, NULL, result_pool); +} + +const char * +svn_fs_x__path_rev(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + svn__i64toa(buffer, rev); + + assert(! svn_fs_x__is_packed_rev(fs, rev)); + return construct_shard_sub_path(fs, rev, FALSE, FALSE, buffer, result_pool); +} + +const char * +svn_fs_x__path_rev_absolute(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + return svn_fs_x__is_packed_rev(fs, rev) + ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool) + : svn_fs_x__path_rev(fs, rev, result_pool); +} + +const char * +svn_fs_x__path_revprops_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + return construct_shard_sub_path(fs, rev, TRUE, FALSE, NULL, result_pool); +} + +const char * +svn_fs_x__path_revprops_pack_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + return construct_shard_sub_path(fs, rev, TRUE, TRUE, NULL, result_pool); +} + +const char * +svn_fs_x__path_revprops(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + svn__i64toa(buffer, rev); + + assert(! svn_fs_x__is_packed_revprop(fs, rev)); + return construct_shard_sub_path(fs, rev, TRUE, FALSE, buffer, result_pool); +} + +const char * +svn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + char *p = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE); + svn__ui64tobase36(p, txn_id); + return p; +} + +svn_error_t * +svn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id, + const char *txn_name) +{ + const char *next; + apr_uint64_t id = svn__base36toui64(&next, txn_name); + if (next == NULL || *next != 0 || *txn_name == 0) + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "Malformed TXN name '%s'", txn_name); + + *txn_id = id; + return SVN_NO_ERROR; +} + +const char * +svn_fs_x__path_txns_dir(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_TXNS_DIR, result_pool); +} + +/* Return the full path of the file FILENAME within transaction TXN_ID's + * transaction directory in FS. If FILENAME is NULL, return the transaction + * directory itself. + * + * Allocate the result in RESULT_POOL. + */ +static const char * +construct_txn_path(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const char *filename, + apr_pool_t *result_pool) +{ + /* Construct the transaction directory name without temp. allocations. */ + char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_TXN)] = { 0 }; + apr_size_t len = svn__ui64tobase36(buffer, txn_id); + strncpy(buffer + len, PATH_EXT_TXN, sizeof(buffer) - len - 1); + + /* If FILENAME is NULL, it will terminate the list of segments + to concatenate. */ + return svn_dirent_join_many(result_pool, fs->path, PATH_TXNS_DIR, + buffer, filename, SVN_VA_NULL); +} + +const char * +svn_fs_x__path_txn_dir(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, NULL, result_pool); +} + +/* Return the name of the sha1->rep mapping file in transaction TXN_ID + * within FS for the given SHA1 checksum. Use POOL for allocations. + */ +const char * +svn_fs_x__path_txn_sha1(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const unsigned char *sha1, + apr_pool_t *pool) +{ + svn_checksum_t checksum; + checksum.digest = sha1; + checksum.kind = svn_checksum_sha1; + + return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, pool), + svn_checksum_to_cstring(&checksum, pool), + pool); +} + +const char * +svn_fs_x__path_txn_changes(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_CHANGES, result_pool); +} + +const char * +svn_fs_x__path_txn_props(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_TXN_PROPS, result_pool); +} + +const char * +svn_fs_x__path_txn_props_final(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_TXN_PROPS_FINAL, result_pool); +} + +const char* +svn_fs_x__path_l2p_proto_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX, + result_pool); +} + +const char* +svn_fs_x__path_p2l_proto_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX, + result_pool); +} + +const char * +svn_fs_x__path_txn_next_ids(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool); +} + +const char * +svn_fs_x__path_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool); +} + +const char * +svn_fs_x__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *result_pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool); +} + +const char * +svn_fs_x__path_txn_item_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool); +} + +/* Return the full path of the proto-rev file / lock file for transaction + * TXN_ID in FS. The SUFFIX determines what file (rev / lock) it will be. + * + * Allocate the result in RESULT_POOL. + */ +static const char * +construct_proto_rev_path(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const char *suffix, + apr_pool_t *result_pool) +{ + /* Construct the file name without temp. allocations. */ + char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 }; + apr_size_t len = svn__ui64tobase36(buffer, txn_id); + strncpy(buffer + len, suffix, sizeof(buffer) - len - 1); + + /* If FILENAME is NULL, it will terminate the list of segments + to concatenate. */ + return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR, + buffer, SVN_VA_NULL); +} + +const char * +svn_fs_x__path_txn_proto_rev(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV, result_pool); +} + +const char * +svn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool) +{ + return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool); +} + +/* Return the full path of the noderev-related file with the extension SUFFIX + * for noderev *ID in transaction TXN_ID in FS. + * + * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. + */ +static const char * +construct_txn_node_path(svn_fs_t *fs, + const svn_fs_x__id_t *id, + const char *suffix, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *filename = svn_fs_x__id_unparse(id, result_pool)->data; + apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set); + + return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool), + apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s", + filename, suffix), + result_pool); +} + +const char * +svn_fs_x__path_txn_node_rev(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return construct_txn_node_path(fs, id, "", result_pool, scratch_pool); +} + +const char * +svn_fs_x__path_txn_node_props(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool, + scratch_pool); +} + +const char * +svn_fs_x__path_txn_node_children(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool, + scratch_pool); +} + +svn_error_t * +svn_fs_x__check_file_buffer_numeric(const char *buf, + apr_off_t offset, + const char *path, + const char *title, + apr_pool_t *scratch_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, scratch_pool), *p, buf); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + char buf[80]; + apr_file_t *file; + apr_size_t len; + + SVN_ERR(svn_io_file_open(&file, + svn_fs_x__path_min_unpacked_rev(fs, scratch_pool), + APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, + scratch_pool)); + len = sizeof(buf); + SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + + SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__update_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, + scratch_pool); +} + +/* 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(). */ +svn_error_t * +svn_fs_x__write_min_unpacked_rev(svn_fs_t *fs, + svn_revnum_t revnum, + apr_pool_t *scratch_pool) +{ + const char *final_path; + char buf[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__i64toa(buf, revnum); + buf[len] = '\n'; + + final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool); + + SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1, + final_path /* copy_perms */, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_current(svn_revnum_t *rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + const char *str; + svn_stringbuf_t *content; + SVN_ERR(svn_fs_x__read_content(&content, + svn_fs_x__path_current(fs, scratch_pool), + scratch_pool)); + SVN_ERR(svn_revnum_parse(rev, content->data, &str)); + if (*str != '\n') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + + return SVN_NO_ERROR; +} + +/* Atomically update the 'current' file to hold the specifed REV. + Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__write_current(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *scratch_pool) +{ + char *buf; + const char *tmp_name, *name; + + /* Now we can just write out this line. */ + buf = apr_psprintf(scratch_pool, "%ld\n", rev); + + name = svn_fs_x__path_current(fs, scratch_pool); + SVN_ERR(svn_io_write_unique(&tmp_name, + svn_dirent_dirname(name, scratch_pool), + buf, strlen(buf), + svn_io_file_del_none, scratch_pool)); + + return svn_fs_x__move_into_place(tmp_name, name, name, scratch_pool); +} + + +svn_error_t * +svn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content, + svn_boolean_t *missing, + const char *path, + svn_boolean_t last_attempt, + apr_pool_t *result_pool) +{ + svn_error_t *err = svn_stringbuf_from_file2(content, path, result_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); +} + +/* Fetch the current offset of FILE into *OFFSET_P. */ +svn_error_t * +svn_fs_x__get_file_offset(apr_off_t *offset_p, + apr_file_t *file, + apr_pool_t *scratch_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, scratch_pool)); + *offset_p = offset; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_content(svn_stringbuf_t **content, + const char *fname, + apr_pool_t *result_pool) +{ + int i; + *content = NULL; + + for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i) + SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL, + fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT, + result_pool)); + + if (!*content) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Can't read '%s'"), + svn_dirent_local_style(fname, result_pool)); + + return SVN_NO_ERROR; +} + +/* 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. + */ +svn_error_t * +svn_fs_x__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; +} + + +/* 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 SCRATCH_POOL. + + This function almost duplicates svn_io_file_move(), but it tries to + guarantee a flush. */ +svn_error_t * +svn_fs_x__move_into_place(const char *old_filename, + const char *new_filename, + const char *perms_reference, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + + SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool)); + + /* Move the file into place. */ + err = svn_io_file_rename(old_filename, new_filename, scratch_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, + scratch_pool)); + + /* Flush the target of the copy to disk. */ + SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ, + APR_OS_DEFAULT, scratch_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, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_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, scratch_pool); + SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, + scratch_pool)); + SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + } +#endif + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/util.h b/contrib/subversion/subversion/libsvn_fs_x/util.h new file mode 100644 index 000000000..0010723ed --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/util.h @@ -0,0 +1,476 @@ +/* util.h --- utility functions for FSX repo access + * + * ==================================================================== + * 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_FS__UTIL_H +#define SVN_LIBSVN_FS__UTIL_H + +#include "svn_fs.h" +#include "id.h" + +/* 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 fsx 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 SVN_FS_X__RECOVERABLE_RETRY_COUNT + * iterations (though, realistically, the second try will succeed). + */ + +#define SVN_FS_X__RECOVERABLE_RETRY_COUNT 10 + +/* Pathname helper functions */ + +/* Return TRUE is REV is packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_x__is_packed_rev(svn_fs_t *fs, + svn_revnum_t rev); + +/* Return TRUE is REV is packed in FS, FALSE otherwise. */ +svn_boolean_t +svn_fs_x__is_packed_revprop(svn_fs_t *fs, + svn_revnum_t rev); + +/* Return the first revision in the pack / rev file containing REV in + * filesystem FS. For non-packed revs, this will simply be REV. */ +svn_revnum_t +svn_fs_x__packed_base_rev(svn_fs_t *fs, + svn_revnum_t rev); + +/* Return the number of revisions in the pack / rev file in FS that contains + * revision REV. */ +svn_revnum_t +svn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev); + +/* Return the full path of the "format" file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_format(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the path to the 'current' file in FS. + Perform allocation in RESULT_POOL. */ +const char * +svn_fs_x__path_current(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the "uuid" file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_uuid(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the "txn-current" file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_current(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the "txn-current-lock" file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_current_lock(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the global write lock file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_lock(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the pack operation lock file in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_pack_lock(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the full path of the revprop generation file in FS. + * Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_revprop_generation(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the path of the pack-related file that for revision REV in FS. + * KIND specifies the file name base, e.g. "pack". + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_rev_packed(svn_fs_t *fs, + svn_revnum_t rev, + const char *kind, + apr_pool_t *result_pool); + +/* Return the full path of the rev shard directory that will contain + * revision REV in FS. Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_rev_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Return the full path of the non-packed rev file containing revision REV + * in FS. Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_rev(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Set *PATH to the path of REV in FS, whether in a pack file or not. + Allocate *PATH in RESULT_POOL. + + Note: If the caller does not have the write lock on FS, then the path is + not guaranteed to be correct or to remain correct after the function + returns, because the revision might become packed before or after this + call. If a file exists at that path, then it is correct; if not, then + the caller should call update_min_unpacked_rev() and re-try once. */ +const char * +svn_fs_x__path_rev_absolute(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Return the full path of the revision properties shard directory that + * will contain the properties of revision REV in FS. + * Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_revprops_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Return the full path of the revision properties pack shard directory + * that will contain the packed properties of revision REV in FS. + * Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_revprops_pack_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Return the full path of the non-packed revision properties file that + * contains the props for revision REV in FS. + * Allocate the result in RESULT_POOL. + */ +const char * +svn_fs_x__path_revprops(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Convert the TXN_ID into a string, allocated from RESULT_POOL. + */ +const char * +svn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Convert TXN_NAME into an ID and return it in *TXN_ID. */ +svn_error_t * +svn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id, + const char *txn_name); + +/* Return the path of the directory containing the transaction TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_dir(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the 'transactions' directory in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txns_dir(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the name of the sha1->rep mapping file in transaction TXN_ID + * within FS for the given SHA1 checksum. Use POOL for allocations. + */ +const char * +svn_fs_x__path_txn_sha1(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + const unsigned char *sha1, + apr_pool_t *pool); + +/* Return the path of the 'txn-protorevs' directory in FS, even if that + * folder may not exist in FS. The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the path of the changes file for transaction TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_changes(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the log-to-phys index for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char* +svn_fs_x__path_l2p_proto_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the phys-to-log index for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char* +svn_fs_x__path_p2l_proto_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the transaction properties for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_props(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the "final" transaction + * properties for the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_props_final(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the node and copy ID counters for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_next_ids(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file storing the oldest non-packed revision in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *result_pool); + +/* Return the path of the file containing item_index counter for + * the transaction identified by TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_item_index(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the proto-revision file for transaction TXN_ID in FS. + * The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_proto_rev(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the proto-revision lock file for transaction TXN_ID + * in FS. The result will be allocated in RESULT_POOL. + */ +const char * +svn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs, + svn_fs_x__txn_id_t txn_id, + apr_pool_t *result_pool); + +/* Return the path of the file containing the in-transaction node revision + * identified by ID in FS. + * The result will be allocated in RESULT_POOL, temporaries in SCRATCH_POOL. + */ +const char * +svn_fs_x__path_txn_node_rev(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return the path of the file containing the in-transaction properties of + * the node identified by ID in FS. + * The result will be allocated in RESULT_POOL, temporaries in SCRATCH_POOL. + */ +const char * +svn_fs_x__path_txn_node_props(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Return the path of the file containing the directory entries of the + * in-transaction directory node identified by ID in FS. + * The result will be allocated in RESULT_POOL, temporaries in SCRATCH_POOL. + */ +const char * +svn_fs_x__path_txn_node_children(svn_fs_t *fs, + const svn_fs_x__id_t *id, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* 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 SCRATCH_POOL for temporary allocation. */ +svn_error_t * +svn_fs_x__check_file_buffer_numeric(const char *buf, + apr_off_t offset, + const char *path, + const char *title, + apr_pool_t *scratch_pool); + +/* Set *MIN_UNPACKED_REV to the integer value read from the file returned + * by #svn_fs_fs__path_min_unpacked_rev() for FS. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Re-read the MIN_UNPACKED_REV member of FS from disk. + * Use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__update_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Atomically update the 'min-unpacked-rev' file in FS to hold the specifed + * REVNUM. Perform temporary allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_x__write_min_unpacked_rev(svn_fs_t *fs, + svn_revnum_t revnum, + apr_pool_t *scratch_pool); + +/* Set *REV to the value read from the 'current' file. Perform temporary + * allocations in SCRATCH_POOL. + */ +svn_error_t * +svn_fs_x__read_current(svn_revnum_t *rev, + svn_fs_t *fs, + apr_pool_t *scratch_pool); + +/* Atomically update the 'current' file to hold the specifed REV. + Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__write_current(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *scratch_pool); + +/* Read the file at PATH and return its content in *CONTENT, allocated in + * RESULT_POOL. *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. + */ +svn_error_t * +svn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content, + svn_boolean_t *missing, + const char *path, + svn_boolean_t last_attempt, + apr_pool_t *result_pool); + +/* Fetch the current offset of FILE into *OFFSET_P. + * Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_fs_x__get_file_offset(apr_off_t *offset_p, + apr_file_t *file, + apr_pool_t *scratch_pool); + +/* Read the file FNAME and store the contents in *BUF. + Allocations are performed in RESULT_POOL. */ +svn_error_t * +svn_fs_x__read_content(svn_stringbuf_t **content, + const char *fname, + apr_pool_t *result_pool); + +/* 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. + */ +svn_error_t * +svn_fs_x__read_number_from_stream(apr_int64_t *result, + svn_boolean_t *hit_eof, + svn_stream_t *stream, + apr_pool_t *scratch_pool); + +/* 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 SCRATCH_POOL. + + This function almost duplicates svn_io_file_move(), but it tries to + guarantee a flush. */ +svn_error_t * +svn_fs_x__move_into_place(const char *old_filename, + const char *new_filename, + const char *perms_reference, + apr_pool_t *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_fs_x/verify.c b/contrib/subversion/subversion/libsvn_fs_x/verify.c new file mode 100644 index 000000000..4ea0728f7 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/verify.c @@ -0,0 +1,850 @@ +/* verify.c --- verification of FSX filesystems + * + * ==================================================================== + * 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 "verify.h" +#include "fs_x.h" +#include "svn_time.h" +#include "private/svn_subr_private.h" + +#include "cached_data.h" +#include "rep-cache.h" +#include "util.h" +#include "index.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + + +/** Verifying. **/ + +/* Baton type expected by verify_walker(). The purpose is to limit the + * number of notifications sent. + */ +typedef struct verify_walker_baton_t +{ + /* number of calls to verify_walker() since the last clean */ + int iteration_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; +} verify_walker_baton_t; + +/* Used by svn_fs_x__verify(). + Implements svn_fs_x__walk_rep_reference().walker. */ +static svn_error_t * +verify_walker(svn_fs_x__representation_t *rep, + void *baton, + svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + verify_walker_baton_t *walker_baton = baton; + + /* notify and free resources periodically */ + if (walker_baton->iteration_count > 1000) + { + svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + if ( walker_baton->notify_func + && revision != walker_baton->last_notified_revision) + { + walker_baton->notify_func(revision, + walker_baton->notify_baton, + scratch_pool); + walker_baton->last_notified_revision = revision; + } + + walker_baton->iteration_count = 0; + } + + /* access the repo data */ + SVN_ERR(svn_fs_x__check_rep(rep, fs, scratch_pool)); + + /* update resource usage counters */ + walker_baton->iteration_count++; + + return SVN_NO_ERROR; +} + +/* Verify the rep cache DB's consistency with our rev / pack data. + * The function signature is similar to svn_fs_x__verify. + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_rep_cache(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 *scratch_pool) +{ + svn_boolean_t exists; + + /* rep-cache verification. */ + SVN_ERR(svn_fs_x__exists_rep_cache(&exists, fs, scratch_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(scratch_pool, sizeof(*baton)); + + 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, scratch_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_x__walk_rep_reference(fs, start, end, + verify_walker, baton, + cancel_func, cancel_baton, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Verify that the MD5 checksum of the data between offsets START and END + * in FILE matches the EXPECTED checksum. If there is a mismatch use the + * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC + * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */ +static svn_error_t * +verify_index_checksum(apr_file_t *file, + const char *name, + apr_off_t start, + apr_off_t end, + svn_checksum_t *expected, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[SVN__STREAM_CHUNK_SIZE]; + apr_off_t size = end - start; + svn_checksum_t *actual; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + + /* Calculate the index checksum. */ + SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool)); + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL, + scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + SVN_ERR(svn_checksum_final(&actual, context, scratch_pool)); + + /* Verify that it matches the expected checksum. */ + if (!svn_checksum_match(expected, actual)) + { + const char *file_name; + + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool, + _("%s checksum mismatch in file %s"), + name, file_name)); + } + + return SVN_NO_ERROR; +} + +/* Verify the MD5 checksums of the index data in the rev / pack file + * containing revision START in FS. If given, invoke CANCEL_FUNC with + * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +verify_index_checksums(svn_fs_t *fs, + svn_revnum_t start, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__revision_file_t *rev_file; + + /* Open the rev / pack file and read the footer */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, + scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); + + /* Verify the index contents against the checksum from the footer. */ + SVN_ERR(verify_index_checksum(rev_file->file, "L2P index", + rev_file->l2p_offset, rev_file->p2l_offset, + rev_file->l2p_checksum, + cancel_func, cancel_baton, scratch_pool)); + SVN_ERR(verify_index_checksum(rev_file->file, "P2L index", + rev_file->p2l_offset, rev_file->footer_offset, + rev_file->p2l_checksum, + cancel_func, cancel_baton, scratch_pool)); + + /* Done. */ + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Verify that for all log-to-phys index entries for revisions START to + * START + COUNT-1 in FS there is a consistent entry in the phys-to-log + * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular + * intervals. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +compare_l2p_to_p2l_index(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_revnum_t i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_array_header_t *max_ids; + + /* common file access structure */ + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool, + iterpool)); + + /* determine the range of items to check for each revision */ + SVN_ERR(svn_fs_x__l2p_get_max_ids(&max_ids, fs, start, count, scratch_pool, + iterpool)); + + /* check all items in all revisions if the given range */ + for (i = 0; i < max_ids->nelts; ++i) + { + apr_uint64_t k; + apr_uint64_t max_id = APR_ARRAY_IDX(max_ids, i, apr_uint64_t); + svn_revnum_t revision = start + i; + + for (k = 0; k < max_id; ++k) + { + apr_off_t offset; + apr_uint32_t sub_item; + svn_fs_x__id_t l2p_item; + svn_fs_x__id_t *p2l_item; + + l2p_item.change_set = svn_fs_x__change_set_by_rev(revision); + l2p_item.number = k; + + /* get L2P entry. Ignore unused entries. */ + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, + &l2p_item, iterpool)); + if (offset == -1) + continue; + + /* find the corresponding P2L entry */ + SVN_ERR(svn_fs_x__p2l_item_lookup(&p2l_item, fs, rev_file, + revision, offset, sub_item, + iterpool, iterpool)); + + if (p2l_item == NULL) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index entry not found for " + "PHYS o%s:s%ld returned by " + "l2p index for LOG r%ld:i%ld"), + apr_off_t_toa(scratch_pool, offset), + (long)sub_item, revision, (long)k); + + if (!svn_fs_x__id_eq(&l2p_item, p2l_item)) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index info LOG r%ld:i%ld" + " does not match " + "l2p index for LOG r%ld:i%ld"), + svn_fs_x__get_revnum(p2l_item->change_set), + (long)p2l_item->number, revision, + (long)k); + + svn_pool_clear(iterpool); + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Verify that for all phys-to-log index entries for revisions START to + * START + COUNT-1 in FS there is a consistent entry in the log-to-phys + * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular + * intervals. Use SCRATCH_POOL for temporary allocations. + * + * Please note that we can only check on pack / rev file granularity and + * must only be called for a single rev / pack file. + */ +static svn_error_t * +compare_p2l_to_l2p_index(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *iterpool2 = svn_pool_create(scratch_pool); + apr_off_t max_offset; + apr_off_t offset = 0; + + /* common file access structure */ + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool, + iterpool)); + + /* get the size of the rev / pack file as covered by the P2L index */ + SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset, fs, rev_file, start, + scratch_pool)); + + /* for all offsets in the file, get the P2L index entries and check + them against the L2P index */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + svn_fs_x__p2l_entry_t *last_entry; + int i; + + svn_pool_clear(iterpool); + + /* get all entries for the current block */ + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, rev_file, start, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + if (entries->nelts == 0) + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, + NULL, + _("p2l does not cover offset %s" + " for revision %ld"), + apr_off_t_toa(scratch_pool, offset), start); + + /* process all entries (and later continue with the next block) */ + last_entry + = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_x__p2l_entry_t); + offset = last_entry->offset + last_entry->size; + + for (i = 0; i < entries->nelts; ++i) + { + apr_uint32_t k; + svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t); + + /* check all sub-items for consist entries in the L2P index */ + for (k = 0; k < entry->item_count; ++k) + { + apr_off_t l2p_offset; + apr_uint32_t sub_item; + svn_fs_x__id_t *p2l_item = &entry->items[k]; + svn_revnum_t revision + = svn_fs_x__get_revnum(p2l_item->change_set); + + svn_pool_clear(iterpool2); + SVN_ERR(svn_fs_x__item_offset(&l2p_offset, &sub_item, fs, + rev_file, p2l_item, iterpool2)); + + if (sub_item != k || l2p_offset != entry->offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("l2p index entry PHYS o%s:s%ld " + "does not match p2l index value " + "LOG r%ld:i%ld for PHYS o%s:s%ld"), + apr_off_t_toa(scratch_pool, + l2p_offset), + (long)sub_item, + revision, + (long)p2l_item->number, + apr_off_t_toa(scratch_pool, + entry->offset), + (long)k); + } + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool2); + svn_pool_destroy(iterpool); + + SVN_ERR(svn_fs_x__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + +/* Items smaller than this can be read at once into a buffer and directly + * be checksummed. Larger items require stream processing. + * Must be a multiple of 8. */ +#define STREAM_THRESHOLD 4096 + +/* Verify that the next SIZE bytes read from FILE are NUL. SIZE must not + * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +expect_buffer_nul(apr_file_t *file, + apr_off_t size, + apr_pool_t *scratch_pool) +{ + union + { + unsigned char buffer[STREAM_THRESHOLD]; + apr_uint64_t chunks[STREAM_THRESHOLD / sizeof(apr_uint64_t)]; + } data; + + apr_size_t i; + SVN_ERR_ASSERT(size <= STREAM_THRESHOLD); + + /* read the whole data block; error out on failure */ + data.chunks[(size - 1)/ sizeof(apr_uint64_t)] = 0; + SVN_ERR(svn_io_file_read_full2(file, data.buffer, size, NULL, NULL, + scratch_pool)); + + /* chunky check */ + for (i = 0; i < size / sizeof(apr_uint64_t); ++i) + if (data.chunks[i] != 0) + break; + + /* byte-wise check upon mismatch or at the end of the block */ + for (i *= sizeof(apr_uint64_t); i < size; ++i) + if (data.buffer[i] != 0) + { + const char *file_name; + apr_off_t offset; + + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool)); + offset -= size - i; + + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Empty section in file %s contains " + "non-NUL data at offset %s"), + file_name, + apr_off_t_toa(scratch_pool, offset)); + } + + return SVN_NO_ERROR; +} + +/* Verify that the next SIZE bytes read from FILE are NUL. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +read_all_nul(apr_file_t *file, + apr_off_t size, + apr_pool_t *scratch_pool) +{ + for (; size >= STREAM_THRESHOLD; size -= STREAM_THRESHOLD) + SVN_ERR(expect_buffer_nul(file, STREAM_THRESHOLD, scratch_pool)); + + if (size) + SVN_ERR(expect_buffer_nul(file, size, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Compare the ACTUAL checksum with the one expected by ENTRY. + * Return an error in case of mismatch. Use the name of FILE + * in error message. Allocate temporary data in SCRATCH_POOL. + */ +static svn_error_t * +expected_checksum(apr_file_t *file, + svn_fs_x__p2l_entry_t *entry, + apr_uint32_t actual, + apr_pool_t *scratch_pool) +{ + if (actual != entry->fnv1_checksum) + { + const char *file_name; + + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Checksum mismatch in item at offset %s of " + "length %s bytes in file %s"), + apr_off_t_toa(scratch_pool, entry->offset), + apr_off_t_toa(scratch_pool, entry->size), + file_name); + } + + return SVN_NO_ERROR; +} + +/* Verify that the FNV checksum over the next ENTRY->SIZE bytes read + * from FILE will match ENTRY's expected checksum. SIZE must not + * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +expected_buffered_checksum(apr_file_t *file, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[STREAM_THRESHOLD]; + SVN_ERR_ASSERT(entry->size <= STREAM_THRESHOLD); + + SVN_ERR(svn_io_file_read_full2(file, buffer, (apr_size_t)entry->size, + NULL, NULL, scratch_pool)); + SVN_ERR(expected_checksum(file, entry, + svn__fnv1a_32x4(buffer, (apr_size_t)entry->size), + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Verify that the FNV checksum over the next ENTRY->SIZE bytes read from + * FILE will match ENTRY's expected checksum. + * Use SCRATCH_POOL for temporary allocations. + */ +static svn_error_t * +expected_streamed_checksum(apr_file_t *file, + svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[STREAM_THRESHOLD]; + svn_checksum_t *checksum; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, scratch_pool); + apr_off_t size = entry->size; + + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL, + scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + } + + SVN_ERR(svn_checksum_final(&checksum, context, scratch_pool)); + SVN_ERR(expected_checksum(file, entry, + ntohl(*(const apr_uint32_t *)checksum->digest), + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Verify that for all phys-to-log index entries for revisions START to + * START + COUNT-1 in FS match the actual pack / rev file contents. + * If given, invoke CANCEL_FUNC with CANCEL_BATON at regular intervals. + * Use SCRATCH_POOL for temporary allocations. + * + * Please note that we can only check on pack / rev file granularity and + * must only be called for a single rev / pack file. + */ +static svn_error_t * +compare_p2l_to_rev(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t count, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_off_t max_offset; + apr_off_t offset = 0; + svn_fs_x__revision_file_t *rev_file; + + /* open the pack / rev file that is covered by the p2l index */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool, + iterpool)); + + /* check file size vs. range covered by index */ + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); + SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset, fs, rev_file, start, + scratch_pool)); + + if (rev_file->l2p_offset != max_offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, + _("File size of %s for revision r%ld does " + "not match p2l index size of %s"), + apr_off_t_toa(scratch_pool, + rev_file->l2p_offset), + start, + apr_off_t_toa(scratch_pool, + max_offset)); + + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, 0, + scratch_pool)); + + /* for all offsets in the file, get the P2L index entries and check + them against the L2P index */ + for (offset = 0; offset < max_offset; ) + { + apr_array_header_t *entries; + int i; + + svn_pool_clear(iterpool); + + /* get all entries for the current block */ + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, rev_file, start, + offset, ffd->p2l_page_size, + iterpool, iterpool)); + + /* The above might have moved the file pointer. + * Ensure we actually start reading at OFFSET. */ + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, + NULL, offset, iterpool)); + + /* process all entries (and later continue with the next block) */ + for (i = 0; i < entries->nelts; ++i) + { + svn_fs_x__p2l_entry_t *entry + = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t); + + /* skip bits we previously checked */ + if (i == 0 && entry->offset < offset) + continue; + + /* skip zero-sized entries */ + if (entry->size == 0) + continue; + + /* p2l index must cover all rev / pack file offsets exactly once */ + if (entry->offset != offset) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("p2l index entry for revision r%ld" + " is non-contiguous between offsets " + " %s and %s"), + start, + apr_off_t_toa(scratch_pool, offset), + apr_off_t_toa(scratch_pool, + entry->offset)); + + /* empty sections must contain NUL bytes only */ + if (entry->type == SVN_FS_X__ITEM_TYPE_UNUSED) + { + /* skip filler entry at the end of the p2l index */ + if (entry->offset != max_offset) + SVN_ERR(read_all_nul(rev_file->file, entry->size, iterpool)); + } + else + { + if (entry->size < STREAM_THRESHOLD) + SVN_ERR(expected_buffered_checksum(rev_file->file, entry, + iterpool)); + else + SVN_ERR(expected_streamed_checksum(rev_file->file, entry, + iterpool)); + } + + /* advance offset */ + offset += entry->size; + } + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Verify that the revprops of the revisions START to END in FS can be + * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals. + * + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_revprops(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_revnum_t revision; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + for (revision = start; revision < end; ++revision) + { + svn_string_t *date; + apr_time_t timetemp; + + svn_pool_clear(iterpool); + + /* Access the svn:date revprop. + * This implies parsing all revprops for that revision. */ + SVN_ERR(svn_fs_x__revision_prop(&date, fs, revision, + SVN_PROP_REVISION_DATE, + iterpool, iterpool)); + + /* The time stamp is the only revprop that, if given, needs to + * have a valid content. */ + if (date) + SVN_ERR(svn_time_from_cstring(&timetemp, date->data, iterpool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Verify that on-disk representation has not been tempered with (in a way + * that leaves the repository in a corrupted state). This compares log-to- + * phys with phys-to-log indexes, verifies the low-level checksums and + * checks that all revprops are available. The function signature is + * similar to svn_fs_x__verify. + * + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_metadata_consistency(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 *scratch_pool) +{ + svn_error_t *err; + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_revnum_t revision, next_revision; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + for (revision = start; revision <= end; revision = next_revision) + { + svn_revnum_t count = svn_fs_x__packed_base_rev(fs, revision); + svn_revnum_t pack_start = count; + svn_revnum_t pack_end = pack_start + svn_fs_x__pack_size(fs, revision); + + svn_pool_clear(iterpool); + + if (notify_func && (pack_start % ffd->max_files_per_dir == 0)) + notify_func(pack_start, notify_baton, iterpool); + + /* Check for external corruption to the indexes. */ + err = verify_index_checksums(fs, pack_start, cancel_func, + cancel_baton, iterpool); + + /* two-way index check */ + if (!err) + err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + if (!err) + err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + + /* verify in-index checksums and types vs. actual rev / pack files */ + if (!err) + err = compare_p2l_to_rev(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); + + /* ensure that revprops are available and accessible */ + if (!err) + err = verify_revprops(fs, pack_start, pack_end, + cancel_func, cancel_baton, iterpool); + + /* concurrent packing is one of the reasons why verification may fail. + Make sure, we operate on up-to-date information. */ + if (err) + SVN_ERR(svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, + fs, scratch_pool)); + + /* retry the whole shard if it got packed in the meantime */ + if (err && count != svn_fs_x__pack_size(fs, revision)) + { + svn_error_clear(err); + + /* We could simply assign revision here but the code below is + more intuitive to maintainers. */ + next_revision = svn_fs_x__packed_base_rev(fs, revision); + } + else + { + SVN_ERR(err); + next_revision = pack_end; + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__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 *scratch_pool) +{ + svn_fs_x__data_t *ffd = fs->fsap_data; + svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */ + + /* Input validation. */ + if (! SVN_IS_VALID_REVNUM(start)) + start = 0; + if (! SVN_IS_VALID_REVNUM(end)) + end = youngest; + SVN_ERR(svn_fs_x__ensure_revision_exists(start, fs, scratch_pool)); + SVN_ERR(svn_fs_x__ensure_revision_exists(end, fs, scratch_pool)); + + /* log/phys index consistency. We need to check them first to make + sure we can access the rev / pack files in format7. */ + SVN_ERR(verify_metadata_consistency(fs, start, end, + notify_func, notify_baton, + cancel_func, cancel_baton, + scratch_pool)); + + /* rep cache consistency */ + SVN_ERR(verify_rep_cache(fs, start, end, notify_func, notify_baton, + cancel_func, cancel_baton, scratch_pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_fs_x/verify.h b/contrib/subversion/subversion/libsvn_fs_x/verify.h new file mode 100644 index 000000000..805f65496 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_fs_x/verify.h @@ -0,0 +1,43 @@ +/* verify.h : verification interface of the native filesystem layer + * + * ==================================================================== + * 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_FS__VERIFY_H +#define SVN_LIBSVN_FS__VERIFY_H + +#include "fs.h" + +/* Verify metadata in fsx filesystem FS. Limit the checks to revisions + * START to END where possible. Indicate progress via the optional + * NOTIFY_FUNC callback using NOTIFY_BATON. The optional CANCEL_FUNC + * will periodically be called with CANCEL_BATON to allow for preemption. + * Use SCRATCH_POOL for temporary allocations. */ +svn_error_t * +svn_fs_x__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 *scratch_pool); + +#endif diff --git a/contrib/subversion/subversion/libsvn_ra/compat.c b/contrib/subversion/subversion/libsvn_ra/compat.c index b16bbefe9..f7307bf55 100644 --- a/contrib/subversion/subversion/libsvn_ra/compat.c +++ b/contrib/subversion/subversion/libsvn_ra/compat.c @@ -35,6 +35,7 @@ #include "svn_props.h" #include "private/svn_fspath.h" +#include "private/svn_sorts_private.h" #include "ra_loader.h" #include "svn_private_config.h" @@ -315,6 +316,7 @@ svn_ra__locations_from_log(svn_ra_session_t *session, svn_revnum_t youngest_requested, oldest_requested, youngest, oldest; svn_node_kind_t kind; const char *fs_path; + apr_array_header_t *sorted_location_revisions; /* Fetch the absolute FS path associated with PATH. */ SVN_ERR(get_fs_path(&fs_path, session, path, pool)); @@ -336,11 +338,11 @@ svn_ra__locations_from_log(svn_ra_session_t *session, /* Figure out the youngest and oldest revs (amongst the set of requested revisions + the peg revision) so we can avoid unnecessary log parsing. */ - qsort(location_revisions->elts, location_revisions->nelts, - location_revisions->elt_size, compare_revisions); - oldest_requested = APR_ARRAY_IDX(location_revisions, 0, svn_revnum_t); - youngest_requested = APR_ARRAY_IDX(location_revisions, - location_revisions->nelts - 1, + sorted_location_revisions = apr_array_copy(pool, location_revisions); + svn_sort__array(sorted_location_revisions, compare_revisions); + oldest_requested = APR_ARRAY_IDX(sorted_location_revisions, 0, svn_revnum_t); + youngest_requested = APR_ARRAY_IDX(sorted_location_revisions, + sorted_location_revisions->nelts - 1, svn_revnum_t); youngest = peg_revision; youngest = (oldest_requested > youngest) ? oldest_requested : youngest; @@ -352,7 +354,7 @@ svn_ra__locations_from_log(svn_ra_session_t *session, /* Populate most of our log receiver baton structure. */ lrb.kind = kind; lrb.last_path = fs_path; - lrb.location_revisions = apr_array_copy(pool, location_revisions); + lrb.location_revisions = apr_array_copy(pool, sorted_location_revisions); lrb.peg_revision = peg_revision; lrb.peg_path = NULL; lrb.locations = locations; @@ -378,9 +380,9 @@ svn_ra__locations_from_log(svn_ra_session_t *session, if (lrb.last_path) { int i; - for (i = 0; i < location_revisions->nelts; i++) + for (i = 0; i < sorted_location_revisions->nelts; i++) { - svn_revnum_t rev = APR_ARRAY_IDX(location_revisions, i, + svn_revnum_t rev = APR_ARRAY_IDX(sorted_location_revisions, i, svn_revnum_t); if (! apr_hash_get(locations, &rev, sizeof(rev))) apr_hash_set(locations, apr_pmemdup(pool, &rev, sizeof(rev)), @@ -920,9 +922,9 @@ svn_ra__get_inherited_props_walk(svn_ra_session_t *session, 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); - svn_string_t *value = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(hi); + svn_string_t *value = apr_hash_this_val(hi); if (svn_property_kind2(name) == svn_prop_regular_kind) { @@ -940,7 +942,7 @@ svn_ra__get_inherited_props_walk(svn_ra_session_t *session, parent_url, result_pool); new_iprop->prop_hash = final_hash; - svn_sort__array_insert(&new_iprop, *inherited_props, 0); + svn_sort__array_insert(*inherited_props, &new_iprop, 0); } } diff --git a/contrib/subversion/subversion/libsvn_ra/libsvn_ra.pc.in b/contrib/subversion/subversion/libsvn_ra/libsvn_ra.pc.in new file mode 100644 index 000000000..b7ef13136 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra/libsvn_ra.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_ra +Description: Subversion General Repository Access Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_ra +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_ra/ra_loader.c b/contrib/subversion/subversion/libsvn_ra/ra_loader.c index d9cb96b9e..3a782f239 100644 --- a/contrib/subversion/subversion/libsvn_ra/ra_loader.c +++ b/contrib/subversion/subversion/libsvn_ra/ra_loader.c @@ -35,6 +35,7 @@ #include "svn_hash.h" #include "svn_version.h" +#include "svn_time.h" #include "svn_types.h" #include "svn_error.h" #include "svn_error_codes.h" @@ -51,6 +52,7 @@ #include "ra_loader.h" #include "deprecated.h" +#include "private/svn_auth_private.h" #include "private/svn_ra_private.h" #include "svn_private_config.h" @@ -230,6 +232,11 @@ check_ra_version(const svn_version_t *ra_version, const char *scheme) svn_error_t *svn_ra_initialize(apr_pool_t *pool) { +#if defined(SVN_USE_DSO) && APR_HAS_DSO + /* Ensure that DSO subsystem is initialized early as possible if + we're going to use it. */ + SVN_ERR(svn_dso_initialize2()); +#endif return SVN_NO_ERROR; } @@ -259,25 +266,17 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, apr_pool_t *pool) { apr_pool_t *sesspool = svn_pool_create(pool); + apr_pool_t *scratch_pool = svn_pool_create(sesspool); svn_ra_session_t *session; const struct ra_lib_defn *defn; const svn_ra__vtable_t *vtable = NULL; - svn_config_t *servers = NULL; - const char *server_group; apr_uri_t repos_URI; apr_status_t apr_err; + svn_error_t *err; #ifdef CHOOSABLE_DAV_MODULE const char *http_library = DEFAULT_HTTP_LIBRARY; #endif - /* Auth caching parameters. */ - svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; - svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; - const char *store_plaintext_passwords - = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS; - svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP; - const char *store_pp_plaintext - = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT; - const char *corrected_url; + svn_auth_baton_t *auth_baton; /* Initialize the return variable. */ *session_p = NULL; @@ -293,100 +292,31 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, repos_URL); if (callbacks->auth_baton) - { - /* The 'store-passwords' and 'store-auth-creds' parameters used to - * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility, - * if values for these parameters have already been set by our - * callers, we use those values as defaults. - * - * Note that we can only catch the case where users explicitly set - * "store-passwords = no" or 'store-auth-creds = no". - * - * However, since the default value for both these options is - * currently (and has always been) "yes", users won't know - * the difference if they set "store-passwords = yes" or - * "store-auth-creds = yes" -- they'll get the expected behaviour. - */ - - if (svn_auth_get_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL) - store_passwords = FALSE; - - if (svn_auth_get_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL) - store_auth_creds = FALSE; - } + SVN_ERR(svn_auth__make_session_auth(&auth_baton, + callbacks->auth_baton, config, + repos_URI.hostname, + sesspool, scratch_pool)); + else + auth_baton = NULL; +#ifdef CHOOSABLE_DAV_MODULE if (config) { + svn_config_t *servers = NULL; + const char *server_group = NULL; + /* Grab the 'servers' config. */ servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS); if (servers) { /* First, look in the global section. */ - SVN_ERR(svn_config_get_bool - (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_STORE_PASSWORDS, - store_passwords)); - - SVN_ERR(svn_config_get_yes_no_ask - (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, - SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); - - SVN_ERR(svn_config_get_bool - (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, - store_pp)); - - SVN_ERR(svn_config_get_yes_no_ask - (servers, &store_pp_plaintext, - SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, - SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT)); - - SVN_ERR(svn_config_get_bool - (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_STORE_AUTH_CREDS, - store_auth_creds)); - /* Find out where we're about to connect to, and * try to pick a server group based on the destination. */ server_group = svn_config_find_group(servers, repos_URI.hostname, SVN_CONFIG_SECTION_GROUPS, sesspool); - if (server_group) - { - /* Override global auth caching parameters with the ones - * for the server group, if any. */ - SVN_ERR(svn_config_get_bool(servers, &store_auth_creds, - server_group, - SVN_CONFIG_OPTION_STORE_AUTH_CREDS, - store_auth_creds)); - - SVN_ERR(svn_config_get_bool(servers, &store_passwords, - server_group, - SVN_CONFIG_OPTION_STORE_PASSWORDS, - store_passwords)); - - SVN_ERR(svn_config_get_yes_no_ask - (servers, &store_plaintext_passwords, server_group, - SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, - store_plaintext_passwords)); - - SVN_ERR(svn_config_get_bool - (servers, &store_pp, - server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, - store_pp)); - - SVN_ERR(svn_config_get_yes_no_ask - (servers, &store_pp_plaintext, server_group, - SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, - store_pp_plaintext)); - } -#ifdef CHOOSABLE_DAV_MODULE /* Now, which DAV-based RA method do we want to use today? */ http_library = svn_config_get_server_setting(servers, @@ -399,34 +329,9 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, _("Invalid config: unknown HTTP library " "'%s'"), http_library); -#endif } } - - if (callbacks->auth_baton) - { - /* Save auth caching parameters in the auth parameter hash. */ - if (! store_passwords) - svn_auth_set_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); - - svn_auth_set_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS, - store_plaintext_passwords); - - if (! store_pp) - svn_auth_set_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP, - ""); - - svn_auth_set_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, - store_pp_plaintext); - - if (! store_auth_creds) - svn_auth_set_parameter(callbacks->auth_baton, - SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); - } +#endif /* Find the library. */ for (defn = ra_libraries; defn->ra_name != NULL; ++defn) @@ -445,16 +350,16 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, if (! initfunc) SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, - sesspool)); + scratch_pool)); if (! initfunc) /* Library not found. */ continue; - SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool)); + SVN_ERR(initfunc(svn_ra_version(), &vtable, scratch_pool)); SVN_ERR(check_ra_version(vtable->get_version(), scheme)); - if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL)) + if (! has_scheme_of(vtable->get_schemes(scratch_pool), repos_URL)) /* Library doesn't support the scheme at runtime. */ continue; @@ -476,31 +381,32 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, session->pool = sesspool; /* Ask the library to open the session. */ - SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL, - callbacks, callback_baton, config, sesspool), - apr_psprintf(pool, "Unable to connect to a repository at URL '%s'", - repos_URL)); + err = vtable->open_session(session, corrected_url_p, + repos_URL, + callbacks, callback_baton, auth_baton, + config, sesspool, scratch_pool); + + if (err) + { + svn_pool_destroy(sesspool); /* Includes scratch_pool */ + if (err->apr_err == SVN_ERR_RA_SESSION_URL_MISMATCH) + return svn_error_trace(err); + + return svn_error_createf( + SVN_ERR_RA_CANNOT_CREATE_SESSION, err, + _("Unable to connect to a repository at URL '%s'"), + repos_URL); + } /* If the session open stuff detected a server-provided URL correction (a 301 or 302 redirect response during the initial OPTIONS request), then kill the session so the caller can decide what to do. */ - if (corrected_url_p && corrected_url) + if (corrected_url_p && *corrected_url_p) { - if (! svn_path_is_url(corrected_url)) - { - /* RFC1945 and RFC2616 state that the Location header's - value (from whence this CORRECTED_URL ultimately comes), - if present, must be an absolute URI. But some Apache - versions (those older than 2.2.11, it seems) transmit - only the path portion of the URI. See issue #3775 for - details. */ - apr_uri_t corrected_URI = repos_URI; - corrected_URI.path = (char *)corrected_url; - corrected_url = apr_uri_unparse(pool, &corrected_URI, 0); - } - *corrected_url_p = svn_uri_canonicalize(corrected_url, pool); - svn_pool_destroy(sesspool); + /* *session_p = NULL; */ + *corrected_url_p = apr_pstrdup(pool, *corrected_url_p); + svn_pool_destroy(sesspool); /* Includes scratch_pool */ return SVN_NO_ERROR; } @@ -514,7 +420,7 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, { /* Duplicate the uuid as it is allocated in sesspool */ repository_uuid = apr_pstrdup(pool, repository_uuid); - svn_pool_destroy(sesspool); + svn_pool_destroy(sesspool); /* includes scratch_pool */ return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL, _("Repository UUID '%s' doesn't match " "expected UUID '%s'"), @@ -522,10 +428,50 @@ svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, } } + svn_pool_destroy(scratch_pool); *session_p = session; return SVN_NO_ERROR; } +svn_error_t * +svn_ra__dup_session(svn_ra_session_t **new_session, + svn_ra_session_t *old_session, + const char *session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_session_t *session; + + if (session_url) + { + const char *dummy; + + /* This verifies in new_session_url is in the repository */ + SVN_ERR(svn_ra_get_path_relative_to_root(old_session, + &dummy, + session_url, + scratch_pool)); + } + else + SVN_ERR(svn_ra_get_session_url(old_session, &session_url, scratch_pool)); + + /* Create the session object. */ + session = apr_pcalloc(result_pool, sizeof(*session)); + session->cancel_func = old_session->cancel_func; + session->cancel_baton = old_session->cancel_baton; + session->vtable = old_session->vtable; + session->pool = result_pool; + + SVN_ERR(old_session->vtable->dup_session(session, + old_session, + session_url, + result_pool, + scratch_pool)); + + *new_session = session; + return SVN_NO_ERROR; +} + svn_error_t *svn_ra_reparent(svn_ra_session_t *session, const char *url, apr_pool_t *pool) @@ -650,61 +596,6 @@ svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session, return session->vtable->rev_prop(session, rev, name, value, pool); } -struct ccw_baton -{ - svn_commit_callback2_t original_callback; - void *original_baton; - - svn_ra_session_t *session; -}; - -/* Wrapper which populates the repos_root field of the commit_info struct */ -static svn_error_t * -commit_callback_wrapper(const svn_commit_info_t *commit_info, - void *baton, - apr_pool_t *pool) -{ - struct ccw_baton *ccwb = baton; - svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool); - - SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool)); - - return ccwb->original_callback(ci, ccwb->original_baton, pool); -} - - -/* Some RA layers do not correctly fill in REPOS_ROOT in commit_info, or - they are third-party layers conforming to an older commit_info structure. - Interpose a utility function to ensure the field is valid. */ -static void -remap_commit_callback(svn_commit_callback2_t *callback, - void **callback_baton, - svn_ra_session_t *session, - svn_commit_callback2_t original_callback, - void *original_baton, - apr_pool_t *result_pool) -{ - if (original_callback == NULL) - { - *callback = NULL; - *callback_baton = NULL; - } - else - { - /* Allocate this in RESULT_POOL, since the callback will be called - long after this function has returned. */ - struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb)); - - ccwb->session = session; - ccwb->original_callback = original_callback; - ccwb->original_baton = original_baton; - - *callback = commit_callback_wrapper; - *callback_baton = ccwb; - } -} - - svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session, const svn_delta_editor_t **editor, void **edit_baton, @@ -715,10 +606,6 @@ svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session, svn_boolean_t keep_locks, apr_pool_t *pool) { - remap_commit_callback(&commit_callback, &commit_baton, - session, commit_callback, commit_baton, - pool); - return session->vtable->get_commit_editor(session, editor, edit_baton, revprop_table, commit_callback, commit_baton, @@ -925,8 +812,104 @@ svn_error_t *svn_ra_stat(svn_ra_session_t *session, svn_dirent_t **dirent, apr_pool_t *pool) { + svn_error_t *err; SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); - return session->vtable->stat(session, path, revision, dirent, pool); + err = session->vtable->stat(session, path, revision, dirent, pool); + + /* svnserve before 1.2 doesn't support the above, so fall back on + a far less efficient, but still correct method. */ + if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) + { + /* ### TODO: Find out if we can somehow move this code in libsvn_ra_svn. + */ + apr_pool_t *scratch_pool = svn_pool_create(pool); + svn_node_kind_t kind; + + svn_error_clear(err); + + SVN_ERR(svn_ra_check_path(session, path, revision, &kind, scratch_pool)); + + if (kind != svn_node_none) + { + const char *repos_root_url; + const char *session_url; + + SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, + scratch_pool)); + SVN_ERR(svn_ra_get_session_url(session, &session_url, + scratch_pool)); + + if (!svn_path_is_empty(path)) + session_url = svn_path_url_add_component2(session_url, path, + scratch_pool); + + if (strcmp(session_url, repos_root_url) != 0) + { + svn_ra_session_t *parent_session; + apr_hash_t *parent_ents; + const char *parent_url, *base_name; + + /* Open another session to the path's parent. This server + doesn't support svn_ra_reparent anyway, so don't try it. */ + svn_uri_split(&parent_url, &base_name, session_url, + scratch_pool); + + SVN_ERR(svn_ra__dup_session(&parent_session, session, parent_url, + scratch_pool, scratch_pool)); + + /* Get all parent's entries, no props. */ + SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL, + NULL, "", revision, SVN_DIRENT_ALL, + scratch_pool)); + + /* Get the relevant entry. */ + *dirent = svn_hash_gets(parent_ents, base_name); + + if (*dirent) + *dirent = svn_dirent_dup(*dirent, pool); + } + else + { + apr_hash_t *props; + const svn_string_t *val; + + /* We can't get the directory entry for the repository root, + but we can still get the information we want. + The created-rev of the repository root must, by definition, + be rev. */ + *dirent = apr_pcalloc(pool, sizeof(**dirent)); + (*dirent)->kind = kind; + (*dirent)->size = SVN_INVALID_FILESIZE; + + SVN_ERR(svn_ra_get_dir2(session, NULL, NULL, &props, + "", revision, 0 /* no dirent fields */, + scratch_pool)); + (*dirent)->has_props = (apr_hash_count(props) != 0); + + (*dirent)->created_rev = revision; + + SVN_ERR(svn_ra_rev_proplist(session, revision, &props, + scratch_pool)); + + val = svn_hash_gets(props, SVN_PROP_REVISION_DATE); + if (val) + SVN_ERR(svn_time_from_cstring(&(*dirent)->time, val->data, + scratch_pool)); + + val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR); + (*dirent)->last_author = val ? apr_pstrdup(pool, val->data) + : NULL; + } + } + else + *dirent = NULL; + + svn_pool_clear(scratch_pool); + } + else + SVN_ERR(err); + + return SVN_NO_ERROR; } svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session, @@ -1030,7 +1013,7 @@ svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session, if (include_merged_revisions) SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool)); - if (start > end) + if (start > end || !SVN_IS_VALID_REVNUM(start)) SVN_ERR( svn_ra__assert_capable_server(session, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, @@ -1040,7 +1023,8 @@ svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session, err = session->vtable->get_file_revs(session, path, start, end, include_merged_revisions, handler, handler_baton, pool); - if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) + if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) + && !include_merged_revisions) { svn_error_clear(err); @@ -1048,7 +1032,7 @@ svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session, err = svn_ra__file_revs_from_log(session, path, start, end, handler, handler_baton, pool); } - return err; + return svn_error_trace(err); } svn_error_t *svn_ra_lock(svn_ra_session_t *session, @@ -1063,7 +1047,7 @@ svn_error_t *svn_ra_lock(svn_ra_session_t *session, for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); } @@ -1088,7 +1072,7 @@ svn_error_t *svn_ra_unlock(svn_ra_session_t *session, for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); } @@ -1320,27 +1304,24 @@ svn_ra_get_inherited_props(svn_ra_session_t *session, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_boolean_t iprop_capable; - + svn_error_t *err; /* Path must be relative. */ SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); - SVN_ERR(svn_ra_has_capability(session, &iprop_capable, - SVN_RA_CAPABILITY_INHERITED_PROPS, - scratch_pool)); + err = session->vtable->get_inherited_props(session, iprops, path, + revision, result_pool, + scratch_pool); - if (iprop_capable) - { - SVN_ERR(session->vtable->get_inherited_props(session, iprops, path, - revision, result_pool, - scratch_pool)); - } - else + if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { + svn_error_clear(err); + /* Fallback for legacy servers. */ SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops, result_pool, scratch_pool)); } + else + SVN_ERR(err); return SVN_NO_ERROR; } @@ -1365,11 +1346,6 @@ svn_ra__get_commit_ev2(svn_editor_t **editor, /* The specific RA layer does not have an implementation. Use our default shim over the normal commit editor. */ - /* Remap for RA layers exposing Ev1. */ - remap_commit_callback(&commit_callback, &commit_baton, - session, commit_callback, commit_baton, - result_pool); - return svn_error_trace(svn_ra__use_commit_shim( editor, session, diff --git a/contrib/subversion/subversion/libsvn_ra/ra_loader.h b/contrib/subversion/subversion/libsvn_ra/ra_loader.h index 227730aec..f371f273f 100644 --- a/contrib/subversion/subversion/libsvn_ra/ra_loader.h +++ b/contrib/subversion/subversion/libsvn_ra/ra_loader.h @@ -61,8 +61,16 @@ typedef struct svn_ra__vtable_t { const char *session_URL, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Backs svn_ra_dup_session */ + svn_error_t * (*dup_session)(svn_ra_session_t *new_session, + svn_ra_session_t *old_session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* See svn_ra_reparent(). */ /* URL is guaranteed to have what get_repos_root() returns as a prefix. */ svn_error_t *(*reparent)(svn_ra_session_t *session, diff --git a/contrib/subversion/subversion/libsvn_ra/wrapper_template.h b/contrib/subversion/subversion/libsvn_ra/wrapper_template.h index cee3fb3fe..0585deda8 100644 --- a/contrib/subversion/subversion/libsvn_ra/wrapper_template.h +++ b/contrib/subversion/subversion/libsvn_ra/wrapper_template.h @@ -91,7 +91,9 @@ static svn_error_t *compat_open(void **session_baton, callbacks2->progress_baton = NULL; SVN_ERR(VTBL.open_session(sess, &session_url, repos_URL, - callbacks2, callback_baton, config, sesspool)); + callbacks2, callback_baton, + callbacks ? callbacks->auth_baton : NULL, + config, sesspool, sesspool)); if (strcmp(repos_URL, session_url) != 0) { diff --git a/contrib/subversion/subversion/libsvn_ra_local/libsvn_ra_local.pc.in b/contrib/subversion/subversion/libsvn_ra_local/libsvn_ra_local.pc.in new file mode 100644 index 000000000..1333df0b7 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_local/libsvn_ra_local.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_ra_local +Description: Subversion Local Repository Access Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_repos libsvn_fs libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_ra_local +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_ra_local/ra_local.h b/contrib/subversion/subversion/libsvn_ra_local/ra_local.h index df455da3d..344b68e2f 100644 --- a/contrib/subversion/subversion/libsvn_ra_local/ra_local.h +++ b/contrib/subversion/subversion/libsvn_ra_local/ra_local.h @@ -62,6 +62,11 @@ typedef struct svn_ra_local__session_baton_t /* Callbacks/baton passed to svn_ra_open. */ const svn_ra_callbacks2_t *callbacks; void *callback_baton; + + /* Slave auth baton */ + svn_auth_baton_t *auth_baton; + + const char *useragent; } svn_ra_local__session_baton_t; diff --git a/contrib/subversion/subversion/libsvn_ra_local/ra_plugin.c b/contrib/subversion/subversion/libsvn_ra_local/ra_plugin.c index 4ccc86ddc..bb09162f0 100644 --- a/contrib/subversion/subversion/libsvn_ra_local/ra_plugin.c +++ b/contrib/subversion/subversion/libsvn_ra_local/ra_plugin.c @@ -87,7 +87,7 @@ get_username(svn_ra_session_t *session, { /* Get a username somehow, so we have some svn:author property to attach to a commit. */ - if (sess->callbacks->auth_baton) + if (sess->auth_baton) { void *creds; svn_auth_cred_username_t *username_creds; @@ -96,7 +96,7 @@ get_username(svn_ra_session_t *session, SVN_ERR(svn_auth_first_credentials(&creds, &iterstate, SVN_AUTH_CRED_USERNAME, sess->uuid, /* realmstring */ - sess->callbacks->auth_baton, + sess->auth_baton, scratch_pool)); /* No point in calling next_creds(), since that assumes that the @@ -241,7 +241,7 @@ reporter_link_path(void *reporter_baton, if (relpath[0] == '\0') fs_path = "/"; else - fs_path = apr_pstrcat(pool, "/", relpath, (char *)NULL); + fs_path = apr_pstrcat(pool, "/", relpath, SVN_VA_NULL); return svn_repos_link_path3(rbaton->report_baton, path, fs_path, revision, depth, start_empty, lock_token, pool); @@ -328,7 +328,7 @@ make_reporter(svn_ra_session_t *session, "'%s'"), other_url, sess->repos_url); other_fs_path = apr_pstrcat(scratch_pool, "/", other_relpath, - (char *)NULL); + SVN_VA_NULL); } /* Pass back our reporter */ @@ -360,8 +360,13 @@ make_reporter(svn_ra_session_t *session, edit_baton, NULL, NULL, - 1024 * 1024, /* process-local transfers - should be fast */ + 0, /* Disable zero-copy codepath, because + RA API users are unaware about the + zero-copy code path limitation (do + not access FSFS data structures + and, hence, caches). See notes + to svn_repos_begin_report3() for + additional details. */ result_pool)); /* Wrap the report baton given us by the repos layer with our own @@ -410,28 +415,29 @@ deltify_etc(const svn_commit_info_t *commit_info, /* Maybe unlock the paths. */ if (deb->lock_tokens) { - apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *subpool = svn_pool_create(scratch_pool); + apr_hash_t *targets = apr_hash_make(subpool); apr_hash_index_t *hi; - for (hi = apr_hash_first(scratch_pool, deb->lock_tokens); hi; + for (hi = apr_hash_first(subpool, deb->lock_tokens); hi; hi = apr_hash_next(hi)) { - const void *relpath = svn__apr_hash_index_key(hi); - const char *token = svn__apr_hash_index_val(hi); + const void *relpath = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); const char *fspath; - svn_pool_clear(iterpool); - - fspath = svn_fspath__join(deb->fspath_base, relpath, iterpool); - - /* We may get errors here if the lock was broken or stolen - after the commit succeeded. This is fine and should be - ignored. */ - svn_error_clear(svn_repos_fs_unlock(deb->repos, fspath, token, - FALSE, iterpool)); + fspath = svn_fspath__join(deb->fspath_base, relpath, subpool); + svn_hash_sets(targets, fspath, token); } - svn_pool_destroy(iterpool); + /* We may get errors here if the lock was broken or stolen + after the commit succeeded. This is fine and should be + ignored. */ + svn_error_clear(svn_repos_fs_unlock_many(deb->repos, targets, FALSE, + NULL, NULL, + subpool, subpool)); + + svn_pool_destroy(subpool); } /* But, deltification shouldn't be stopped just because someone's @@ -475,8 +481,8 @@ apply_lock_tokens(svn_fs_t *fs, for (hi = apr_hash_first(scratch_pool, lock_tokens); hi; hi = apr_hash_next(hi)) { - const void *relpath = svn__apr_hash_index_key(hi); - const char *token = svn__apr_hash_index_val(hi); + const void *relpath = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); const char *fspath; /* The path needs to live as long as ACCESS_CTX. */ @@ -536,23 +542,31 @@ ignore_warnings(void *baton, svn_error_t *err) { #ifdef SVN_DEBUG - SVN_DBG(("Ignoring FS warning %d\n", err ? err->apr_err : 0)); + SVN_DBG(("Ignoring FS warning %s\n", + svn_error_symbolic_name(err ? err->apr_err : 0))); #endif return; } +#define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \ + " ra_local" + static svn_error_t * svn_ra_local__open(svn_ra_session_t *session, const char **corrected_url, const char *repos_URL, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { + const char *client_string; svn_ra_local__session_baton_t *sess; const char *fs_path; static volatile svn_atomic_t cache_init_state = 0; + apr_pool_t *pool = result_pool; /* Initialise the FSFS memory cache size. We can only do this once so one CONFIG will win the race and all others will be ignored @@ -567,16 +581,16 @@ svn_ra_local__open(svn_ra_session_t *session, sess = apr_pcalloc(pool, sizeof(*sess)); sess->callbacks = callbacks; sess->callback_baton = callback_baton; + sess->auth_baton = auth_baton; /* Look through the URL, figure out which part points to the repository, and which part is the path *within* the repository. */ - SVN_ERR_W(svn_ra_local__split_URL(&(sess->repos), - &(sess->repos_url), - &fs_path, - repos_URL, - session->pool), - _("Unable to open an ra_local session to URL")); + SVN_ERR(svn_ra_local__split_URL(&(sess->repos), + &(sess->repos_url), + &fs_path, + repos_URL, + session->pool)); sess->fs_path = svn_stringbuf_create(fs_path, session->pool); /* Cache the filesystem object from the repos here for @@ -592,10 +606,69 @@ svn_ra_local__open(svn_ra_session_t *session, /* Be sure username is NULL so we know to look it up / ask for it */ sess->username = NULL; + if (sess->callbacks->get_client_string != NULL) + SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton, + &client_string, pool)); + else + client_string = NULL; + + if (client_string) + sess->useragent = apr_pstrcat(pool, USER_AGENT " ", + client_string, SVN_VA_NULL); + else + sess->useragent = USER_AGENT; + session->priv = sess; return SVN_NO_ERROR; } +static svn_error_t * +svn_ra_local__dup_session(svn_ra_session_t *new_session, + svn_ra_session_t *session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_local__session_baton_t *old_sess = session->priv; + svn_ra_local__session_baton_t *new_sess; + const char *fs_path; + + /* Allocate and stash the session_sess args we have already. */ + new_sess = apr_pcalloc(result_pool, sizeof(*new_sess)); + new_sess->callbacks = old_sess->callbacks; + new_sess->callback_baton = old_sess->callback_baton; + + /* ### Re-use existing FS handle? */ + + /* Reuse existing code */ + SVN_ERR(svn_ra_local__split_URL(&(new_sess->repos), + &(new_sess->repos_url), + &fs_path, + new_session_url, + result_pool)); + + new_sess->fs_path = svn_stringbuf_create(fs_path, result_pool); + + /* Cache the filesystem object from the repos here for + convenience. */ + new_sess->fs = svn_repos_fs(new_sess->repos); + + /* Ignore FS warnings. */ + svn_fs_set_warning_func(new_sess->fs, ignore_warnings, NULL); + + /* Cache the repository UUID as well */ + new_sess->uuid = apr_pstrdup(result_pool, old_sess->uuid); + + new_sess->username = old_sess->username + ? apr_pstrdup(result_pool, old_sess->username) + : NULL; + + new_sess->useragent = apr_pstrdup(result_pool, old_sess->useragent); + new_session->priv = new_sess; + + return SVN_NO_ERROR; +} + static svn_error_t * svn_ra_local__reparent(svn_ra_session_t *session, const char *url, @@ -728,6 +801,62 @@ svn_ra_local__rev_prop(svn_ra_session_t *session, NULL, NULL, pool); } +struct ccw_baton +{ + svn_commit_callback2_t original_callback; + void *original_baton; + + svn_ra_session_t *session; +}; + +/* Wrapper which populates the repos_root field of the commit_info struct */ +static svn_error_t * +commit_callback_wrapper(const svn_commit_info_t *commit_info, + void *baton, + apr_pool_t *scratch_pool) +{ + struct ccw_baton *ccwb = baton; + svn_commit_info_t *ci = svn_commit_info_dup(commit_info, scratch_pool); + + SVN_ERR(svn_ra_local__get_repos_root(ccwb->session, &ci->repos_root, + scratch_pool)); + + return svn_error_trace(ccwb->original_callback(ci, ccwb->original_baton, + scratch_pool)); +} + + +/* The repository layer does not correctly fill in REPOS_ROOT in + commit_info, as it doesn't know the url that is used to access + it. This hooks the callback to fill in the missing pieces. */ +static void +remap_commit_callback(svn_commit_callback2_t *callback, + void **callback_baton, + svn_ra_session_t *session, + svn_commit_callback2_t original_callback, + void *original_baton, + apr_pool_t *result_pool) +{ + if (original_callback == NULL) + { + *callback = NULL; + *callback_baton = NULL; + } + else + { + /* Allocate this in RESULT_POOL, since the callback will be called + long after this function has returned. */ + struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb)); + + ccwb->session = session; + ccwb->original_callback = original_callback; + ccwb->original_baton = original_baton; + + *callback = commit_callback_wrapper; + *callback_baton = ccwb; + } +} + static svn_error_t * svn_ra_local__get_commit_editor(svn_ra_session_t *session, const svn_delta_editor_t **editor, @@ -742,6 +871,10 @@ svn_ra_local__get_commit_editor(svn_ra_session_t *session, svn_ra_local__session_baton_t *sess = session->priv; struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb)); + /* Set repos_root_url in commit info */ + remap_commit_callback(&callback, &callback_baton, session, + callback, callback_baton, pool); + /* Prepare the baton for deltify_etc() */ deb->fs = sess->fs; deb->repos = sess->repos; @@ -765,6 +898,8 @@ svn_ra_local__get_commit_editor(svn_ra_session_t *session, svn_string_create(sess->username, pool)); svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION, svn_string_create(SVN_VER_NUMBER, pool)); + svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT, + svn_string_create(sess->useragent, pool)); /* Get the repos commit-editor */ return svn_repos_get_commit_editor5 @@ -1054,12 +1189,12 @@ svn_ra_local__stat(svn_ra_session_t *session, +/* Obtain the properties for a node, including its 'entry props */ static svn_error_t * get_node_props(apr_hash_t **props, - apr_array_header_t **inherited_props, - svn_ra_local__session_baton_t *sess, svn_fs_root_t *root, const char *path, + const char *uuid, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -1067,39 +1202,25 @@ get_node_props(apr_hash_t **props, const char *cmt_date, *cmt_author; /* Create a hash with props attached to the fs node. */ - if (props) - { - SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool)); - } - - /* Get inherited properties if requested. */ - if (inherited_props) - { - SVN_ERR(svn_repos_fs_get_inherited_props(inherited_props, root, path, - NULL, NULL, NULL, - result_pool, scratch_pool)); - } + SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool)); /* Now add some non-tweakable metadata to the hash as well... */ - if (props) - { - /* The so-called 'entryprops' with info about CR & friends. */ - SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date, - &cmt_author, root, path, - scratch_pool)); - - svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, - svn_string_createf(result_pool, "%ld", cmt_rev)); - svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ? - svn_string_create(cmt_date, result_pool) :NULL); - svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ? - svn_string_create(cmt_author, result_pool) :NULL); - svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, - svn_string_create(sess->uuid, result_pool)); - - /* We have no 'wcprops' in ra_local, but might someday. */ - } + /* The so-called 'entryprops' with info about CR & friends. */ + SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date, + &cmt_author, root, path, + scratch_pool)); + + svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, + svn_string_createf(result_pool, "%ld", cmt_rev)); + svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ? + svn_string_create(cmt_date, result_pool) : NULL); + svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ? + svn_string_create(cmt_author, result_pool) : NULL); + svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, + svn_string_create(uuid, result_pool)); + + /* We have no 'wcprops' in ra_local, but might someday. */ return SVN_NO_ERROR; } @@ -1171,7 +1292,7 @@ svn_ra_local__get_file(svn_ra_session_t *session, /* Handle props if requested. */ if (props) - SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool)); + SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool)); return SVN_NO_ERROR; } @@ -1194,7 +1315,6 @@ svn_ra_local__get_dir(svn_ra_session_t *session, apr_hash_t *entries; apr_hash_index_t *hi; svn_ra_local__session_baton_t *sess = session->priv; - apr_pool_t *subpool; const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); /* Open the revision's root. */ @@ -1210,29 +1330,28 @@ svn_ra_local__get_dir(svn_ra_session_t *session, if (dirents) { + apr_pool_t *iterpool = svn_pool_create(pool); /* Get the dir's entries. */ SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool)); /* Loop over the fs dirents, and build a hash of general svn_dirent_t's. */ *dirents = apr_hash_make(pool); - subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { const void *key; void *val; - apr_hash_t *prophash; const char *datestring, *entryname, *fullpath; svn_fs_dirent_t *fs_entry; svn_dirent_t *entry = svn_dirent_create(pool); - svn_pool_clear(subpool); + svn_pool_clear(iterpool); apr_hash_this(hi, &key, NULL, &val); entryname = (const char *) key; fs_entry = (svn_fs_dirent_t *) val; - fullpath = svn_dirent_join(abs_path, entryname, subpool); + fullpath = svn_dirent_join(abs_path, entryname, iterpool); if (dirent_fields & SVN_DIRENT_KIND) { @@ -1247,15 +1366,15 @@ svn_ra_local__get_dir(svn_ra_session_t *session, entry->size = 0; else SVN_ERR(svn_fs_file_length(&(entry->size), root, - fullpath, subpool)); + fullpath, iterpool)); } if (dirent_fields & SVN_DIRENT_HAS_PROPS) { /* has_props? */ - SVN_ERR(svn_fs_node_proplist(&prophash, root, fullpath, - subpool)); - entry->has_props = (apr_hash_count(prophash) != 0); + SVN_ERR(svn_fs_node_has_props(&entry->has_props, + root, fullpath, + iterpool)); } if ((dirent_fields & SVN_DIRENT_TIME) @@ -1266,7 +1385,7 @@ svn_ra_local__get_dir(svn_ra_session_t *session, SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev), &datestring, &(entry->last_author), - root, fullpath, subpool)); + root, fullpath, iterpool)); if (datestring) SVN_ERR(svn_time_from_cstring(&(entry->time), datestring, pool)); @@ -1277,12 +1396,12 @@ svn_ra_local__get_dir(svn_ra_session_t *session, /* Store. */ svn_hash_sets(*dirents, entryname, entry); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); } /* Handle props if requested. */ if (props) - SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool)); + SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool)); return SVN_NO_ERROR; } @@ -1322,6 +1441,35 @@ svn_ra_local__get_location_segments(svn_ra_session_t *session, NULL, NULL, pool); } +struct lock_baton_t { + svn_ra_lock_callback_t lock_func; + void *lock_baton; + const char *fs_path; + svn_boolean_t is_lock; + svn_error_t *cb_err; +}; + +/* Implements svn_fs_lock_callback_t. Used by svn_ra_local__lock and + svn_ra_local__unlock to forward to supplied callback and record any + callback error. */ +static svn_error_t * +lock_cb(void *lock_baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + struct lock_baton_t *b = lock_baton; + + if (b && !b->cb_err && b->lock_func) + { + path = svn_fspath__skip_ancestor(b->fs_path, path); + b->cb_err = b->lock_func(b->lock_baton, path, b->is_lock, lock, fs_err, + pool); + } + + return SVN_NO_ERROR; +} static svn_error_t * svn_ra_local__lock(svn_ra_session_t *session, @@ -1333,51 +1481,44 @@ svn_ra_local__lock(svn_ra_session_t *session, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; + apr_hash_t *targets = apr_hash_make(pool); apr_hash_index_t *hi; - apr_pool_t *iterpool = svn_pool_create(pool); + svn_error_t *err; + struct lock_baton_t baton = {0}; /* A username is absolutely required to lock a path. */ SVN_ERR(get_username(session, pool)); for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) { - svn_lock_t *lock; - const void *key; - const char *path; - void *val; - svn_revnum_t *revnum; - const char *abs_path; - svn_error_t *err, *callback_err = NULL; - - svn_pool_clear(iterpool); - apr_hash_this(hi, &key, NULL, &val); - path = key; - revnum = val; - - abs_path = svn_fspath__join(sess->fs_path->data, path, iterpool); - - /* This wrapper will call pre- and post-lock hooks. */ - err = svn_repos_fs_lock(&lock, sess->repos, abs_path, NULL, comment, - FALSE /* not DAV comment */, - 0 /* no expiration */, *revnum, force, - iterpool); - - if (err && !SVN_ERR_IS_LOCK_ERROR(err)) - return err; - - if (lock_func) - callback_err = lock_func(lock_baton, path, TRUE, err ? NULL : lock, - err, iterpool); - - svn_error_clear(err); - - if (callback_err) - return callback_err; + const char *abs_path = svn_fspath__join(sess->fs_path->data, + apr_hash_this_key(hi), pool); + svn_revnum_t current_rev = *(svn_revnum_t *)apr_hash_this_val(hi); + svn_fs_lock_target_t *target = svn_fs_lock_target_create(NULL, + current_rev, + pool); + + svn_hash_sets(targets, abs_path, target); } - svn_pool_destroy(iterpool); + baton.lock_func = lock_func; + baton.lock_baton = lock_baton; + baton.fs_path = sess->fs_path->data; + baton.is_lock = TRUE; + baton.cb_err = SVN_NO_ERROR; - return SVN_NO_ERROR; + err = svn_repos_fs_lock_many(sess->repos, targets, comment, + FALSE /* not DAV comment */, + 0 /* no expiration */, force, + lock_cb, &baton, + pool, pool); + + if (err && baton.cb_err) + svn_error_compose(err, baton.cb_err); + else if (!err) + err = baton.cb_err; + + return svn_error_trace(err); } @@ -1390,51 +1531,38 @@ svn_ra_local__unlock(svn_ra_session_t *session, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; + apr_hash_t *targets = apr_hash_make(pool); apr_hash_index_t *hi; - apr_pool_t *iterpool = svn_pool_create(pool); + svn_error_t *err; + struct lock_baton_t baton = {0}; /* A username is absolutely required to unlock a path. */ SVN_ERR(get_username(session, pool)); for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) { - const void *key; - const char *path; - void *val; - const char *abs_path, *token; - svn_error_t *err, *callback_err = NULL; - - svn_pool_clear(iterpool); - apr_hash_this(hi, &key, NULL, &val); - path = key; - /* Since we can't store NULL values in a hash, we turn "" to - NULL here. */ - if (strcmp(val, "") != 0) - token = val; - else - token = NULL; - - abs_path = svn_fspath__join(sess->fs_path->data, path, iterpool); - - /* This wrapper will call pre- and post-unlock hooks. */ - err = svn_repos_fs_unlock(sess->repos, abs_path, token, force, - iterpool); + const char *abs_path = svn_fspath__join(sess->fs_path->data, + apr_hash_this_key(hi), pool); + const char *token = apr_hash_this_val(hi); - if (err && !SVN_ERR_IS_UNLOCK_ERROR(err)) - return err; - - if (lock_func) - callback_err = lock_func(lock_baton, path, FALSE, NULL, err, iterpool); + svn_hash_sets(targets, abs_path, token); + } - svn_error_clear(err); + baton.lock_func = lock_func; + baton.lock_baton = lock_baton; + baton.fs_path = sess->fs_path->data; + baton.is_lock = FALSE; + baton.cb_err = SVN_NO_ERROR; - if (callback_err) - return callback_err; - } + err = svn_repos_fs_unlock_many(sess->repos, targets, force, lock_cb, &baton, + pool, pool); - svn_pool_destroy(iterpool); + if (err && baton.cb_err) + svn_error_compose(err, baton.cb_err); + else if (!err) + err = baton.cb_err; - return SVN_NO_ERROR; + return svn_error_trace(err); } @@ -1597,8 +1725,11 @@ svn_ra_local__get_inherited_props(svn_ra_session_t *session, _("'%s' path not found"), abs_path); } - return svn_error_trace(get_node_props(NULL, iprops, sess, root, abs_path, - result_pool, scratch_pool)); + return svn_error_trace( + svn_repos_fs_get_inherited_props(iprops, root, abs_path, + NULL /* propname */, + NULL, NULL /* auth */, + result_pool, scratch_pool)); } static svn_error_t * @@ -1631,6 +1762,9 @@ svn_ra_local__get_commit_ev2(svn_editor_t **editor, svn_ra_local__session_baton_t *sess = session->priv; struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb)); + remap_commit_callback(&commit_cb, &commit_baton, session, + commit_cb, commit_baton, result_pool); + /* NOTE: the RA callbacks are ignored. We pass everything directly to the REPOS editor. */ @@ -1682,6 +1816,7 @@ static const svn_ra__vtable_t ra_local_vtable = svn_ra_local__get_description, svn_ra_local__get_schemes, svn_ra_local__open, + svn_ra_local__dup_session, svn_ra_local__reparent, svn_ra_local__get_session_url, svn_ra_local__get_latest_revnum, @@ -1749,8 +1884,8 @@ svn_ra_local__init(const svn_version_t *loader_version, SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal)); #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL - /* This assumes that POOL was the pool used to load the dso. */ - SVN_ERR(svn_fs_initialize(pool)); + /* This means the library was loaded as a DSO, so use the DSO pool. */ + SVN_ERR(svn_fs_initialize(svn_dso__pool())); #endif *vtable = &ra_local_vtable; diff --git a/contrib/subversion/subversion/libsvn_ra_local/split_url.c b/contrib/subversion/subversion/libsvn_ra_local/split_url.c index ec6cb68a9..5b17ef08e 100644 --- a/contrib/subversion/subversion/libsvn_ra_local/split_url.c +++ b/contrib/subversion/subversion/libsvn_ra_local/split_url.c @@ -30,7 +30,7 @@ svn_error_t * svn_ra_local__split_URL(svn_repos_t **repos, - const char **repos_url, + const char **repos_root_url, const char **fs_path, const char *URL, apr_pool_t *pool) @@ -50,7 +50,7 @@ svn_ra_local__split_URL(svn_repos_t **repos, _("Unable to open repository '%s'"), URL); /* Attempt to open a repository at URL. */ - err = svn_repos_open2(repos, repos_root_dirent, NULL, pool); + err = svn_repos_open3(repos, repos_root_dirent, NULL, pool, pool); if (err) return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err, _("Unable to open repository '%s'"), URL); @@ -96,7 +96,7 @@ svn_ra_local__split_URL(svn_repos_t **repos, svn_path_remove_components(urlbuf, svn_path_component_count(repos_dirent) - svn_path_component_count(repos_root_dirent)); - *repos_url = urlbuf->data; + *repos_root_url = urlbuf->data; /* Configure hook script environment variables. */ SVN_ERR(svn_repos_hooks_setenv(*repos, NULL, pool)); diff --git a/contrib/subversion/subversion/libsvn_ra_serf/README b/contrib/subversion/subversion/libsvn_ra_serf/README index d3baf3324..98a48a636 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/README +++ b/contrib/subversion/subversion/libsvn_ra_serf/README @@ -22,7 +22,7 @@ support for ra_serf: For more about how ra_serf/ra_neon talk WebDAV, consult notes/webdav-protocol. -Working copies are interchangable between ra_serf and ra_neon. (They both use +Working copies are interchangeable between ra_serf and ra_neon. (They both use the svn:wc:ra_dav:version-url property to store the latest revision of a file.) Completed tasks diff --git a/contrib/subversion/subversion/libsvn_ra_serf/blame.c b/contrib/subversion/subversion/libsvn_ra_serf/blame.c index b6f136a2b..d915a1910 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/blame.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/blame.c @@ -47,7 +47,7 @@ * This enum represents the current state of our XML parsing for a REPORT. */ typedef enum blame_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, FILE_REVS_REPORT, FILE_REV, REV_PROP, @@ -111,7 +111,6 @@ static const svn_ra_serf__xml_transition_t blame_ttable[] = { { 0 } }; - /* Conforms to svn_ra_serf__xml_opened_t */ static svn_error_t * blame_opened(svn_ra_serf__xml_estate_t *xes, @@ -140,17 +139,20 @@ blame_opened(svn_ra_serf__xml_estate_t *xes, apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV); const char *path; - const char *rev; + const char *rev_str; const char *merged_revision; svn_txdelta_window_handler_t txdelta; void *txdelta_baton; + apr_int64_t rev; path = svn_hash_gets(gathered, "path"); - rev = svn_hash_gets(gathered, "rev"); + rev_str = svn_hash_gets(gathered, "rev"); + + SVN_ERR(svn_cstring_atoi64(&rev, rev_str)); merged_revision = svn_hash_gets(gathered, "merged-revision"); SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, - path, SVN_STR_TO_REV(rev), + path, (svn_revnum_t)rev, blame_ctx->rev_props, merged_revision != NULL, &txdelta, &txdelta_baton, @@ -278,7 +280,8 @@ static svn_error_t * create_file_revs_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; blame_context_t *blame_ctx = baton; @@ -288,7 +291,7 @@ create_file_revs_body(serf_bucket_t **body_bkt, svn_ra_serf__add_open_tag_buckets(buckets, alloc, "S:file-revs-report", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "S:start-revision", apr_ltoa(pool, blame_ctx->start), @@ -300,9 +303,8 @@ create_file_revs_body(serf_bucket_t **body_bkt, if (blame_ctx->include_merged_revisions) { - svn_ra_serf__add_tag_buckets(buckets, - "S:include-merged-revisions", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:include-merged-revisions", SVN_VA_NULL); } svn_ra_serf__add_tag_buckets(buckets, @@ -331,7 +333,7 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session, svn_ra_serf__handler_t *handler; svn_ra_serf__xml_context_t *xmlctx; const char *req_url; - svn_error_t *err; + svn_revnum_t peg_rev; blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx)); blame_ctx->pool = pool; @@ -342,9 +344,16 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session, blame_ctx->end = end; blame_ctx->include_merged_revisions = include_merged_revisions; + /* Since Subversion 1.8 we allow retrieving blames backwards. So we can't + just unconditionally use end_rev as the peg revision as before */ + if (end > start) + peg_rev = end; + else + peg_rev = start; + SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, - session, NULL /* conn */, - NULL /* url */, end, + session, + NULL /* url */, peg_rev, pool, pool)); xmlctx = svn_ra_serf__xml_context_create(blame_ttable, @@ -353,23 +362,18 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session, blame_cdata, blame_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_type = "text/xml"; handler->body_delegate = create_file_revs_body; handler->body_delegate_baton = blame_ctx; - handler->conn = session->conns[0]; - handler->session = session; - err = svn_ra_serf__context_run_one(handler, pool); + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - err = svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location), - err); + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); - return svn_error_trace(err); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/blncache.c b/contrib/subversion/subversion/libsvn_ra_serf/blncache.c index d6abcdfe6..6fefa931f 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/blncache.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/blncache.c @@ -112,7 +112,7 @@ svn_ra_serf__blncache_set(svn_ra_serf__blncache_t *blncache, const char *baseline_url, svn_revnum_t revision, const char *bc_url, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { if (bc_url && SVN_IS_VALID_REVNUM(revision)) { @@ -147,11 +147,11 @@ svn_error_t * svn_ra_serf__blncache_get_bc_url(const char **bc_url_p, svn_ra_serf__blncache_t *blncache, svn_revnum_t revnum, - apr_pool_t *pool) + apr_pool_t *result_pool) { const char *value = apr_hash_get(blncache->revnum_to_bc, &revnum, sizeof(revnum)); - *bc_url_p = value ? apr_pstrdup(pool, value) : NULL; + *bc_url_p = value ? apr_pstrdup(result_pool, value) : NULL; return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/blncache.h b/contrib/subversion/subversion/libsvn_ra_serf/blncache.h index 5ad4ebae5..fe1479918 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/blncache.h +++ b/contrib/subversion/subversion/libsvn_ra_serf/blncache.h @@ -59,7 +59,7 @@ svn_ra_serf__blncache_set(svn_ra_serf__blncache_t *blncache, const char *baseline_url, svn_revnum_t revnum, const char *bc_url, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Sets *BC_URL_P with a pointer to baseline collection URL for the given * REVNUM. *BC_URL_P will be NULL if cache doesn't have information about @@ -69,7 +69,7 @@ svn_error_t * svn_ra_serf__blncache_get_bc_url(const char **bc_url_p, svn_ra_serf__blncache_t *blncache, svn_revnum_t revnum, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Sets *BC_URL_P with pointer to baseline collection URL and *REVISION_P * with revision number of baseline BASELINE_URL. *BC_URL_P will be NULL, diff --git a/contrib/subversion/subversion/libsvn_ra_serf/commit.c b/contrib/subversion/subversion/libsvn_ra_serf/commit.c index 9d48d41d3..b1e81c787 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/commit.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/commit.c @@ -51,7 +51,6 @@ typedef struct commit_context_t { apr_pool_t *pool; svn_ra_serf__session_t *session; - svn_ra_serf__connection_t *conn; apr_hash_t *revprop_table; @@ -83,15 +82,13 @@ typedef struct proppatch_context_t { const char *relpath; const char *path; - commit_context_t *commit; + commit_context_t *commit_ctx; - /* Changed and removed properties. */ - apr_hash_t *changed_props; - apr_hash_t *removed_props; + /* Changed properties. const char * -> svn_prop_t * */ + apr_hash_t *prop_changes; - /* Same, for the old value (*old_value_p). */ - apr_hash_t *previous_changed_props; - apr_hash_t *previous_removed_props; + /* Same, for the old value, or NULL. */ + apr_hash_t *old_props; /* In HTTP v2, this is the file/directory version we think we're changing. */ svn_revnum_t base_revision; @@ -103,7 +100,9 @@ typedef struct delete_context_t { svn_revnum_t revision; - commit_context_t *commit; + commit_context_t *commit_ctx; + + svn_boolean_t non_recursive_if; /* Only create a non-recursive If header */ } delete_context_t; /* Represents a directory. */ @@ -112,7 +111,7 @@ typedef struct dir_context_t { apr_pool_t *pool; /* The root commit we're in progress for. */ - commit_context_t *commit; + commit_context_t *commit_ctx; /* URL to operate against (used for CHECKOUT and PROPPATCH before HTTP v2, for PROPPATCH in HTTP v2). */ @@ -139,9 +138,8 @@ typedef struct dir_context_t { const char *copy_path; svn_revnum_t copy_revision; - /* Changed and removed properties */ - apr_hash_t *changed_props; - apr_hash_t *removed_props; + /* Changed properties (const char * -> svn_prop_t *) */ + apr_hash_t *prop_changes; /* The checked-out working resource for this directory. May be NULL; if so call checkout_dir() first. */ @@ -154,7 +152,7 @@ typedef struct file_context_t { apr_pool_t *pool; /* The root commit we're in progress for. */ - commit_context_t *commit; + commit_context_t *commit_ctx; /* Is this file being added? (Otherwise, just opened.) */ svn_boolean_t added; @@ -186,9 +184,8 @@ typedef struct file_context_t { /* 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; + /* Changed properties (const char * -> svn_prop_t *) */ + apr_hash_t *prop_changes; /* URL to PUT the file at. */ const char *url; @@ -198,39 +195,13 @@ typedef struct 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) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { const char *activity_url = baton; serf_bucket_t *body_bkt; @@ -240,9 +211,11 @@ create_checkout_body(serf_bucket_t **bkt, 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_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:activity-set", + SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", + SVN_VA_NULL); SVN_ERR_ASSERT(activity_url != NULL); svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc, @@ -251,7 +224,8 @@ create_checkout_body(serf_bucket_t **bkt, 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_empty_tag_buckets(body_bkt, alloc, + "D:apply-to-version", SVN_VA_NULL); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:checkout"); *bkt = body_bkt; @@ -285,32 +259,30 @@ checkout_node(const char **working_url, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_ra_serf__handler_t handler = { 0 }; + svn_ra_serf__handler_t *handler; 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 = svn_ra_serf__create_handler(commit_ctx->session, scratch_pool); - handler.body_delegate = create_checkout_body; - handler.body_delegate_baton = (/* const */ void *)commit_ctx->activity_url; - handler.body_type = "text/xml"; + 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->response_handler = svn_ra_serf__expect_empty_body; + handler->response_baton = handler; - handler.method = "CHECKOUT"; - handler.path = node_url; + handler->method = "CHECKOUT"; + handler->path = node_url; - SVN_ERR(svn_ra_serf__context_run_one(&handler, scratch_pool)); + 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->sline.code != 201) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); - if (handler.location == NULL) + if (handler->location == NULL) return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("No Location header received")); @@ -319,7 +291,7 @@ checkout_node(const char **working_url, '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); + 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")); @@ -365,11 +337,11 @@ retry_checkout_node(const char **working_url, error and retry a few times, asking for the latest baseline again. */ if (err && (err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)) - return err; + return svn_error_trace(err); } while (err && retry_count--); - return err; + return svn_error_trace(err); } @@ -377,8 +349,7 @@ 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; + dir_context_t *c_dir = dir; const char *checkout_url; const char **working; @@ -389,35 +360,35 @@ checkout_dir(dir_context_t *dir, /* Is this directory or one of our parent dirs newly added? * If so, we're already implicitly checked out. */ - while (p_dir) + while (c_dir) { - if (p_dir->added) + if (c_dir->added) { - /* Calculate the working_url by skipping the shared ancestor bewteen - * the parent->relpath and dir->relpath. This is safe since an + /* Calculate the working_url by skipping the shared ancestor between + * the c_dir_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_context_t *c_dir_parent = c_dir->parent_dir; + const char *relpath = svn_relpath_skip_ancestor(c_dir_parent->relpath, dir->relpath); /* Implicitly checkout this dir now. */ - SVN_ERR_ASSERT(parent->working_url); + SVN_ERR_ASSERT(c_dir_parent->working_url); dir->working_url = svn_path_url_add_component2( - parent->working_url, + c_dir_parent->working_url, relpath, dir->pool); return SVN_NO_ERROR; } - p_dir = p_dir->parent_dir; + c_dir = c_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) + if (!dir->parent_dir && !dir->commit_ctx->baseline_url) { - checkout_url = dir->commit->vcc_url; - working = &dir->commit->baseline_url; + checkout_url = dir->commit_ctx->vcc_url; + working = &dir->commit_ctx->baseline_url; } else { @@ -426,18 +397,9 @@ checkout_dir(dir_context_t *dir, } /* 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; + return svn_error_trace(retry_checkout_node(working, dir->commit_ctx, + checkout_url, + dir->pool, scratch_pool)); } @@ -493,7 +455,6 @@ get_version_url(const char **checked_in_url, else { const char *propfind_url; - svn_ra_serf__connection_t *conn = session->conns[0]; if (SVN_IS_VALID_REVNUM(base_revision)) { @@ -502,10 +463,9 @@ get_version_url(const char **checked_in_url, 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 */, + session, NULL /* url */, base_revision, scratch_pool, scratch_pool)); } @@ -514,8 +474,8 @@ get_version_url(const char **checked_in_url, propfind_url = session->session_url.path; } - SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout, - conn, propfind_url, base_revision, + SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout, session, + propfind_url, base_revision, "checked-in", scratch_pool, scratch_pool)); if (!root_checkout) @@ -536,7 +496,6 @@ 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; @@ -548,6 +507,7 @@ checkout_file(file_context_t *file, if (parent_dir->added) { /* Implicitly checkout this file now. */ + SVN_ERR_ASSERT(parent_dir->working_url); file->working_url = svn_path_url_add_component2( parent_dir->working_url, svn_relpath_skip_ancestor( @@ -559,23 +519,14 @@ checkout_file(file_context_t *file, } SVN_ERR(get_version_url(&checkout_url, - file->commit->session, + file->commit_ctx->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; + return svn_error_trace(retry_checkout_node(&file->working_url, + file->commit_ctx, checkout_url, + file->pool, scratch_pool)); } /* Helper function for proppatch_walker() below. */ @@ -612,101 +563,23 @@ get_encoding_and_cdata(const char **encoding_p, 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; -} - +/* Helper for create_proppatch_body. Writes per property xml to body */ static svn_error_t * -proppatch_walker(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) +write_prop_xml(const proppatch_context_t *proppatch, + serf_bucket_t *body_bkt, + serf_bucket_alloc_t *alloc, + const svn_prop_t *prop, + apr_pool_t *result_pool, + 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; + const svn_prop_t *old_prop; - 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)); + SVN_ERR(get_encoding_and_cdata(&encoding, &encoded_value, alloc, prop->value, + result_pool, scratch_pool)); if (encoded_value) { cdata_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value->data, @@ -720,29 +593,40 @@ proppatch_walker(void *baton, /* 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 (strncmp(prop->name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0) + { + prop_name = apr_pstrcat(result_pool, + "S:", prop->name + sizeof(SVN_PROP_PREFIX) - 1, + SVN_VA_NULL); + } + else + { + prop_name = apr_pstrcat(result_pool, + "C:", prop->name, + SVN_VA_NULL); + } if (cdata_bkt) svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name, "V:encoding", encoding, - NULL); + SVN_VA_NULL); else svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name, "V:" SVN_DAV__OLD_VALUE__ABSENT, "1", - NULL); + SVN_VA_NULL); - if (have_old_val) + old_prop = proppatch->old_props + ? svn_hash_gets(proppatch->old_props, prop->name) + : NULL; + if (old_prop) { 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)); + alloc, old_prop->value, + result_pool, scratch_pool)); if (encoded_value2) { @@ -759,12 +643,12 @@ proppatch_walker(void *baton, svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "V:" SVN_DAV__OLD_VALUE, "V:encoding", encoding2, - NULL); + SVN_VA_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); + SVN_VA_NULL); if (cdata_bkt2) serf_bucket_aggregate_append(body_bkt, cdata_bkt2); @@ -799,7 +683,7 @@ maybe_set_lock_token_header(serf_bucket_t *headers, { const char *token; - if (! (relpath && commit_ctx->lock_tokens)) + if (! (*relpath && commit_ctx->lock_tokens)) return SVN_NO_ERROR; if (! svn_hash_gets(commit_ctx->deleted_entries, relpath)) @@ -819,7 +703,7 @@ maybe_set_lock_token_header(serf_bucket_t *headers, token_uri = apr_uri_unparse(pool, &uri, 0); token_header = apr_pstrcat(pool, "<", token_uri, "> (<", token, ">)", - (char *)NULL); + SVN_VA_NULL); serf_bucket_headers_set(headers, "If", token_header); } } @@ -830,7 +714,8 @@ maybe_set_lock_token_header(serf_bucket_t *headers, static svn_error_t * setup_proppatch_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { proppatch_context_t *proppatch = baton; @@ -841,31 +726,26 @@ setup_proppatch_headers(serf_bucket_t *headers, proppatch->base_revision)); } - SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit, - proppatch->relpath, pool)); + if (proppatch->relpath && proppatch->commit_ctx) + SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit_ctx, + 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 *pool /* request pool */, apr_pool_t *scratch_pool) { - struct proppatch_body_baton_t *pbb = baton; - proppatch_context_t *ctx = pbb->proppatch; + proppatch_context_t *ctx = baton; serf_bucket_t *body_bkt; - walker_baton_t wb = { 0 }; + svn_boolean_t opened = FALSE; + apr_hash_index_t *hi; body_bkt = serf_bucket_aggregate_create(alloc); @@ -875,58 +755,66 @@ create_proppatch_body(serf_bucket_t **bkt, "xmlns:V", SVN_DAV_PROP_NS_DAV, "xmlns:C", SVN_DAV_PROP_NS_CUSTOM, "xmlns:S", SVN_DAV_PROP_NS_SVN, - NULL); + SVN_VA_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) + /* First we write property SETs */ + for (hi = apr_hash_first(scratch_pool, ctx->prop_changes); + hi; + hi = apr_hash_next(hi)) { - 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); + svn_prop_t *prop = apr_hash_this_val(hi); - 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)); + if (prop->value + || (ctx->old_props && svn_hash_gets(ctx->old_props, prop->name))) + { + if (!opened) + { + opened = TRUE; + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", + SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", + SVN_VA_NULL); + } - svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop"); - svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set"); + SVN_ERR(write_prop_xml(ctx, body_bkt, alloc, prop, + pool, scratch_pool)); + } } - if (apr_hash_count(ctx->removed_props) > 0) + if (opened) { - 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) + /* And then property REMOVEs */ + opened = FALSE; + + for (hi = apr_hash_first(scratch_pool, ctx->prop_changes); + hi; + hi = apr_hash_next(hi)) { - 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); + svn_prop_t *prop = apr_hash_this_val(hi); - 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)); + if (!prop->value + && !(ctx->old_props && svn_hash_gets(ctx->old_props, prop->name))) + { + if (!opened) + { + opened = TRUE; + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:remove", + SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", + SVN_VA_NULL); + } + SVN_ERR(write_prop_xml(ctx, body_bkt, alloc, prop, + pool, scratch_pool)); + } + } + + if (opened) + { svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop"); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:remove"); } @@ -938,45 +826,47 @@ create_proppatch_body(serf_bucket_t **bkt, } static svn_error_t* -proppatch_resource(proppatch_context_t *proppatch, - commit_context_t *commit, +proppatch_resource(svn_ra_serf__session_t *session, + proppatch_context_t *proppatch, apr_pool_t *pool) { svn_ra_serf__handler_t *handler; - struct proppatch_body_baton_t pbb; + svn_error_t *err; + + handler = svn_ra_serf__create_handler(session, pool); - 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->body_delegate_baton = proppatch; + handler->body_type = "text/xml"; handler->response_handler = svn_ra_serf__handle_multistatus_only; handler->response_baton = handler; - SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); + err = svn_ra_serf__context_run_one(handler, pool); - if (handler->sline.code != 207 - || (handler->server_error != NULL - && handler->server_error->error != NULL)) + if (!err && handler->sline.code != 207) + err = svn_error_trace(svn_ra_serf__unexpected_status(handler)); + + /* Use specific error code for property handling errors. + Use loop to provide the right result with tracing */ + if (err && err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED) { - return svn_error_create( - SVN_ERR_RA_DAV_PROPPATCH_FAILED, - return_response_err(handler), - _("At least one property change failed; repository" - " is unchanged")); + svn_error_t *e = err; + + while (e && e->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED) + { + e->apr_err = SVN_ERR_RA_DAV_PROPPATCH_FAILED; + e = e->child; + } } - return SVN_NO_ERROR; + return svn_error_trace(err); } /* Implements svn_ra_serf__request_body_delegate_t */ @@ -984,7 +874,8 @@ static svn_error_t * create_put_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { file_context_t *ctx = baton; apr_off_t offset; @@ -998,12 +889,10 @@ create_put_body(serf_bucket_t **body_bkt, * 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) + SVN_ERR(svn_io_file_flush(ctx->svndiff, pool)); apr_file_buffer_set(ctx->svndiff, NULL, 0); -#endif offset = 0; - apr_file_seek(ctx->svndiff, APR_SET, &offset); + SVN_ERR(svn_io_file_seek(ctx->svndiff, APR_SET, &offset, pool)); *body_bkt = serf_bucket_file_create(ctx->svndiff, alloc); return SVN_NO_ERROR; @@ -1014,7 +903,8 @@ static svn_error_t * create_empty_put_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { *body_bkt = SERF_BUCKET_SIMPLE_STRING("", alloc); return SVN_NO_ERROR; @@ -1023,7 +913,8 @@ create_empty_put_body(serf_bucket_t **body_bkt, static svn_error_t * setup_put_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { file_context_t *ctx = baton; @@ -1045,7 +936,7 @@ setup_put_headers(serf_bucket_t *headers, ctx->result_checksum); } - SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit, + SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit_ctx, ctx->relpath, pool)); return APR_SUCCESS; @@ -1054,21 +945,21 @@ setup_put_headers(serf_bucket_t *headers, static svn_error_t * setup_copy_file_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_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 = file->commit_ctx->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"); + serf_bucket_headers_setn(headers, "Overwrite", "F"); return SVN_NO_ERROR; } @@ -1102,7 +993,7 @@ setup_if_header_recursive(svn_boolean_t *added, hi; hi = apr_hash_next(hi)) { - const char *relpath = svn__apr_hash_index_key(hi); + const char *relpath = apr_hash_this_key(hi); apr_uri_t uri; if (!svn_relpath_skip_ancestor(rq_relpath, relpath)) @@ -1132,7 +1023,7 @@ setup_if_header_recursive(svn_boolean_t *added, 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, apr_hash_this_val(hi)); svn_stringbuf_appendcstr(sb, ">)"); } @@ -1153,29 +1044,31 @@ setup_if_header_recursive(svn_boolean_t *added, static svn_error_t * setup_add_dir_common_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { dir_context_t *dir = baton; svn_boolean_t added; return svn_error_trace( - setup_if_header_recursive(&added, headers, dir->commit, dir->relpath, + setup_if_header_recursive(&added, headers, dir->commit_ctx, dir->relpath, pool)); } static svn_error_t * setup_copy_dir_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_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; + uri = dir->commit_ctx->session->session_url; - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { uri.path = (char *)dir->url; } @@ -1190,18 +1083,20 @@ setup_copy_dir_headers(serf_bucket_t *headers, serf_bucket_headers_set(headers, "Destination", absolute_uri); serf_bucket_headers_setn(headers, "Depth", "infinity"); - serf_bucket_headers_setn(headers, "Overwrite", "T"); + serf_bucket_headers_setn(headers, "Overwrite", "F"); /* 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)); + return svn_error_trace(setup_add_dir_common_headers(headers, baton, pool, + scratch_pool)); } static svn_error_t * setup_delete_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { delete_context_t *del = baton; svn_boolean_t added; @@ -1209,34 +1104,23 @@ setup_delete_headers(serf_bucket_t *headers, 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 (! del->non_recursive_if) + SVN_ERR(setup_if_header_recursive(&added, headers, del->commit_ctx, + del->relpath, pool)); + else + { + SVN_ERR(maybe_set_lock_token_header(headers, del->commit_ctx, + del->relpath, pool)); + added = TRUE; + } - if (added && del->commit->keep_locks) + if (added && del->commit_ctx->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 */ @@ -1244,7 +1128,8 @@ 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_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { apr_hash_t *revprops = baton; svn_skel_t *request_skel; @@ -1273,7 +1158,8 @@ create_txn_post_body(serf_bucket_t **body_bkt, static svn_error_t * setup_post_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { #ifdef SVN_DAV_SEND_VTXN_NAME /* Enable this to exercise the VTXN-NAME code based on a client @@ -1365,66 +1251,47 @@ open_root(void *edit_baton, apr_pool_t *dir_pool, void **root_baton) { - commit_context_t *ctx = edit_baton; + commit_context_t *commit_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; + apr_pool_t *scratch_pool = svn_pool_create(dir_pool); - if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session)) + if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(commit_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, + = (NULL != svn_hash_gets(commit_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 = svn_ra_serf__create_handler(commit_ctx->session, scratch_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; + post_with_revprops ? commit_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; + handler->path = commit_ctx->session->me_resource; - prc = apr_pcalloc(ctx->pool, sizeof(*prc)); + prc = apr_pcalloc(scratch_pool, sizeof(*prc)); prc->handler = handler; - prc->commit_ctx = ctx; + prc->commit_ctx = commit_ctx; handler->response_handler = post_response_handler; handler->response_baton = prc; - SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool)); + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_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_trace(svn_ra_serf__unexpected_status(handler)); - 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)) + if (! (commit_ctx->txn_root_url && commit_ctx->txn_url)) { return svn_error_createf( SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, @@ -1432,114 +1299,82 @@ open_root(void *edit_baton, } /* 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); + SVN_ERR(svn_ra_serf__get_relative_path( + &rel_path, + commit_ctx->session->session_url.path, + commit_ctx->session, + scratch_pool)); + commit_ctx->txn_root_url = svn_path_url_add_component2( + commit_ctx->txn_root_url, + rel_path, commit_ctx->pool); /* Build our directory baton. */ dir = apr_pcalloc(dir_pool, sizeof(*dir)); dir->pool = dir_pool; - dir->commit = ctx; + dir->commit_ctx = 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); + dir->prop_changes = apr_hash_make(dir->pool); + dir->url = apr_pstrdup(dir->pool, commit_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; + proppatch_target = post_with_revprops ? NULL : commit_ctx->txn_url; } else { - const char *activity_str = ctx->session->activity_collection_url; + const char *activity_str = commit_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")); - } + SVN_ERR(svn_ra_serf__v1_get_activity_collection( + &activity_str, + commit_ctx->session, + scratch_pool, scratch_pool)); - ctx->activity_url = - svn_path_url_add_component2(activity_str, svn_uuid_generate(ctx->pool), - ctx->pool); + commit_ctx->activity_url = svn_path_url_add_component2( + activity_str, + svn_uuid_generate(scratch_pool), + commit_ctx->pool); /* Create our activity URL now on the server. */ - handler = apr_pcalloc(ctx->pool, sizeof(*handler)); - handler->handler_pool = ctx->pool; + handler = svn_ra_serf__create_handler(commit_ctx->session, scratch_pool); + handler->method = "MKACTIVITY"; - handler->path = ctx->activity_url; - handler->conn = ctx->session->conns[0]; - handler->session = ctx->session; + handler->path = commit_ctx->activity_url; handler->response_handler = svn_ra_serf__expect_empty_body; handler->response_baton = handler; - SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool)); + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_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); - } + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); /* 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)); + SVN_ERR(svn_ra_serf__discover_vcc(&(commit_ctx->vcc_url), + commit_ctx->session, scratch_pool)); /* Build our directory baton. */ dir = apr_pcalloc(dir_pool, sizeof(*dir)); dir->pool = dir_pool; - dir->commit = ctx; + dir->commit_ctx = 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->prop_changes = apr_hash_make(dir->pool); - SVN_ERR(get_version_url(&dir->url, dir->commit->session, + SVN_ERR(get_version_url(&dir->url, dir->commit_ctx->session, dir->relpath, - dir->base_revision, ctx->checked_in_url, - dir->pool, dir->pool /* scratch_pool */)); - ctx->checked_in_url = dir->url; + dir->base_revision, commit_ctx->checked_in_url, + dir->pool, scratch_pool)); + commit_ctx->checked_in_url = apr_pstrdup(commit_ctx->pool, dir->url); /* Checkout our root dir */ - SVN_ERR(checkout_dir(dir, dir->pool /* scratch_pool */)); + SVN_ERR(checkout_dir(dir, scratch_pool)); - proppatch_target = ctx->baseline_url; + proppatch_target = commit_ctx->baseline_url; } /* Unless this is NULL -- which means we don't need to PROPPATCH the @@ -1547,44 +1382,58 @@ open_root(void *edit_baton, 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 = apr_pcalloc(scratch_pool, sizeof(*proppatch_ctx)); + proppatch_ctx->pool = scratch_pool; + proppatch_ctx->commit_ctx = NULL; /* No lock info */ 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->prop_changes = apr_hash_make(proppatch_ctx->pool); proppatch_ctx->base_revision = SVN_INVALID_REVNUM; - for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi; + for (hi = apr_hash_first(scratch_pool, commit_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; + svn_prop_t *prop = apr_palloc(scratch_pool, sizeof(*prop)); - 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; - } + prop->name = apr_hash_this_key(hi); + prop->value = apr_hash_this_val(hi); - svn_ra_serf__set_prop(proppatch_ctx->changed_props, - proppatch_ctx->path, - ns, name, value, proppatch_ctx->pool); + svn_hash_sets(proppatch_ctx->prop_changes, prop->name, prop); } - SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool)); + SVN_ERR(proppatch_resource(commit_ctx->session, + proppatch_ctx, scratch_pool)); } + svn_pool_destroy(scratch_pool); + *root_baton = dir; return SVN_NO_ERROR; } +/* Implements svn_ra_serf__request_body_delegate_t */ +static svn_error_t * +create_delete_body(serf_bucket_t **body_bkt, + void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + delete_context_t *ctx = baton; + serf_bucket_t *body; + + body = serf_bucket_aggregate_create(alloc); + + svn_ra_serf__add_xml_header_buckets(body, alloc); + + svn_ra_serf__merge_lock_token_list(ctx->commit_ctx->lock_tokens, + ctx->relpath, body, alloc, pool); + + *body_bkt = body; + return SVN_NO_ERROR; +} + static svn_error_t * delete_entry(const char *path, svn_revnum_t revision, @@ -1596,10 +1445,11 @@ delete_entry(const char *path, svn_ra_serf__handler_t *handler; const char *delete_target; - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { - delete_target = svn_path_url_add_component2(dir->commit->txn_root_url, - path, dir->pool); + delete_target = svn_path_url_add_component2( + dir->commit_ctx->txn_root_url, + path, dir->pool); } else { @@ -1615,12 +1465,9 @@ delete_entry(const char *path, delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx)); delete_ctx->relpath = apr_pstrdup(pool, path); delete_ctx->revision = revision; - delete_ctx->commit = dir->commit; + delete_ctx->commit_ctx = dir->commit_ctx; - handler = apr_pcalloc(pool, sizeof(*handler)); - handler->handler_pool = pool; - handler->session = dir->commit->session; - handler->conn = dir->commit->conn; + handler = svn_ra_serf__create_handler(dir->commit_ctx->session, pool); handler->response_handler = svn_ra_serf__expect_empty_body; handler->response_baton = handler; @@ -1630,17 +1477,43 @@ delete_entry(const char *path, handler->method = "DELETE"; handler->path = delete_target; + handler->no_fail_on_http_failure_status = TRUE; SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - /* 204 No Content: item successfully deleted */ - if (handler->sline.code != 204) + if (handler->sline.code == 400) { - return svn_error_trace(return_response_err(handler)); + /* Try again with non-standard body to overcome Apache Httpd + header limit */ + delete_ctx->non_recursive_if = TRUE; + + handler = svn_ra_serf__create_handler(dir->commit_ctx->session, pool); + + 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; + + handler->body_type = "text/xml"; + handler->body_delegate = create_delete_body; + handler->body_delegate_baton = delete_ctx; + + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); } - svn_hash_sets(dir->commit->deleted_entries, - apr_pstrdup(dir->commit->pool, path), (void *)1); + if (handler->server_error) + return svn_ra_serf__server_error_create(handler, pool); + + /* 204 No Content: item successfully deleted */ + if (handler->sline.code != 204) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + + svn_hash_sets(dir->commit_ctx->deleted_entries, + apr_pstrdup(dir->commit_ctx->pool, path), (void *)1); return SVN_NO_ERROR; } @@ -1663,19 +1536,18 @@ add_directory(const char *path, dir->pool = dir_pool; dir->parent_dir = parent; - dir->commit = parent->commit; + dir->commit_ctx = parent->commit_ctx; 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); + dir->prop_changes = apr_hash_make(dir->pool); - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { - dir->url = svn_path_url_add_component2(parent->commit->txn_root_url, + dir->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url, path, dir->pool); mkcol_target = dir->url; } @@ -1684,17 +1556,14 @@ add_directory(const char *path, /* 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->url = svn_path_url_add_component2(parent->commit_ctx->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 = svn_ra_serf__create_handler(dir->commit_ctx->session, dir->pool); handler->response_handler = svn_ra_serf__expect_empty_body; handler->response_baton = handler; @@ -1719,10 +1588,8 @@ add_directory(const char *path, 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 */, + dir->commit_ctx->session, uri.path, dir->copy_revision, dir_pool, dir_pool)); @@ -1732,26 +1599,13 @@ add_directory(const char *path, handler->header_delegate = setup_copy_dir_headers; handler->header_delegate_baton = dir; } - + /* We have the same problem as with DELETE here: if there are too many + locks, the request fails. But in this case there is no way to retry + with a non-standard request. #### How to fix? */ 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); - } + if (handler->sline.code != 201) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); *child_baton = dir; @@ -1773,26 +1627,25 @@ open_directory(const char *path, dir->pool = dir_pool; dir->parent_dir = parent; - dir->commit = parent->commit; + dir->commit_ctx = parent->commit_ctx; 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); + dir->prop_changes = apr_hash_make(dir->pool); - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { - dir->url = svn_path_url_add_component2(parent->commit->txn_root_url, + dir->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url, path, dir->pool); } else { SVN_ERR(get_version_url(&dir->url, - dir->commit->session, + dir->commit_ctx->session, dir->relpath, dir->base_revision, - dir->commit->checked_in_url, + dir->commit_ctx->checked_in_url, dir->pool, dir->pool /* scratch_pool */)); } *child_baton = dir; @@ -1804,48 +1657,23 @@ static svn_error_t * change_dir_prop(void *dir_baton, const char *name, const svn_string_t *value, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { dir_context_t *dir = dir_baton; - const char *ns; - const char *proppatch_target; + svn_prop_t *prop; - - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) - { - proppatch_target = dir->url; - } - else + if (! USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { /* Ensure we have a checked out dir. */ - SVN_ERR(checkout_dir(dir, pool /* scratch_pool */)); - - proppatch_target = dir->working_url; + SVN_ERR(checkout_dir(dir, scratch_pool)); } - 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; - } + prop = apr_palloc(dir->pool, sizeof(*prop)); - 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); - } + prop->name = apr_pstrdup(dir->pool, name); + prop->value = svn_string_dup(value, dir->pool); + + svn_hash_sets(dir->prop_changes, prop->name, prop); return SVN_NO_ERROR; } @@ -1861,20 +1689,18 @@ close_directory(void *dir_baton, */ /* PROPPATCH our prop change and pass it along. */ - if (apr_hash_count(dir->changed_props) || - apr_hash_count(dir->removed_props)) + if (apr_hash_count(dir->prop_changes)) { proppatch_context_t *proppatch_ctx; proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx)); proppatch_ctx->pool = pool; - proppatch_ctx->commit = dir->commit; + proppatch_ctx->commit_ctx = NULL /* No lock tokens necessary */; proppatch_ctx->relpath = dir->relpath; - proppatch_ctx->changed_props = dir->changed_props; - proppatch_ctx->removed_props = dir->removed_props; + proppatch_ctx->prop_changes = dir->prop_changes; proppatch_ctx->base_revision = dir->base_revision; - if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { proppatch_ctx->path = dir->url; } @@ -1883,7 +1709,8 @@ close_directory(void *dir_baton, proppatch_ctx->path = dir->working_url; } - SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, dir->pool)); + SVN_ERR(proppatch_resource(dir->commit_ctx->session, + proppatch_ctx, dir->pool)); } return SVN_NO_ERROR; @@ -1900,6 +1727,7 @@ add_file(const char *path, dir_context_t *dir = parent_baton; file_context_t *new_file; const char *deleted_parent = path; + apr_pool_t *scratch_pool = svn_pool_create(file_pool); new_file = apr_pcalloc(file_pool, sizeof(*new_file)); new_file->pool = file_pool; @@ -1907,28 +1735,27 @@ add_file(const char *path, dir->ref_count++; new_file->parent_dir = dir; - new_file->commit = dir->commit; + new_file->commit_ctx = dir->commit_ctx; 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); + new_file->prop_changes = 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)) + if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx)) { - new_file->url = svn_path_url_add_component2(dir->commit->txn_root_url, + new_file->url = svn_path_url_add_component2(dir->commit_ctx->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 */)); + SVN_ERR(checkout_dir(dir, scratch_pool)); new_file->url = svn_path_url_add_component2(dir->working_url, @@ -1937,49 +1764,78 @@ add_file(const char *path, while (deleted_parent && deleted_parent[0] != '\0') { - if (svn_hash_gets(dir->commit->deleted_entries, deleted_parent)) + if (svn_hash_gets(dir->commit_ctx->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'))) + if (copy_path) { svn_ra_serf__handler_t *handler; + apr_uri_t uri; + const char *req_url; + apr_status_t status; + + /* Create the copy directly as cheap 'does exist/out of date' + check. We update the copy (if needed) from close_file() */ + + status = apr_uri_parse(scratch_pool, copy_path, &uri); + if (status) + return svn_ra_serf__wrap_err(status, NULL); + + SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, + dir->commit_ctx->session, + uri.path, copy_revision, + scratch_pool, scratch_pool)); + + handler = svn_ra_serf__create_handler(dir->commit_ctx->session, + scratch_pool); + handler->method = "COPY"; + handler->path = req_url; - 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)); + handler->header_delegate = setup_copy_file_headers; + handler->header_delegate_baton = new_file; - if (handler->sline.code != 404) - { - if (handler->sline.code != 200) - { - svn_error_t *err; + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); - err = svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location); + if (handler->sline.code != 201) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + } + else if (! ((dir->added && !dir->copy_path) || + (deleted_parent && deleted_parent[0] != '\0'))) + { + svn_ra_serf__handler_t *handler; + svn_error_t *err; - SVN_ERR(err); - } + handler = svn_ra_serf__create_handler(dir->commit_ctx->session, + scratch_pool); + handler->method = "HEAD"; + handler->path = svn_path_url_add_component2( + dir->commit_ctx->session->session_url.path, + path, scratch_pool); + handler->response_handler = svn_ra_serf__expect_empty_body; + handler->response_baton = handler; + handler->no_dav_headers = TRUE; /* Read only operation outside txn */ + + err = svn_ra_serf__context_run_one(handler, scratch_pool); - return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, - _("File '%s' already exists"), path); + if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); /* Great. We can create a new file! */ } + else if (err) + return svn_error_trace(err); + else + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("File '%s' already exists"), path); } + svn_pool_destroy(scratch_pool); *file_baton = new_file; return SVN_NO_ERROR; @@ -2001,17 +1857,16 @@ open_file(const char *path, parent->ref_count++; new_file->parent_dir = parent; - new_file->commit = parent->commit; + new_file->commit_ctx = parent->commit_ctx; 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); + new_file->prop_changes = apr_hash_make(new_file->pool); - if (USING_HTTPV2_COMMIT_SUPPORT(parent->commit)) + if (USING_HTTPV2_COMMIT_SUPPORT(parent->commit_ctx)) { - new_file->url = svn_path_url_add_component2(parent->commit->txn_root_url, + new_file->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url, path, new_file->pool); } else @@ -2027,6 +1882,23 @@ open_file(const char *path, return SVN_NO_ERROR; } +/* Implements svn_stream_lazyopen_func_t for apply_textdelta */ +static svn_error_t * +delayed_commit_stream_open(svn_stream_t **stream, + void *baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + file_context_t *file_ctx = baton; + + SVN_ERR(svn_io_open_unique_file3(&file_ctx->svndiff, NULL, NULL, + svn_io_file_del_on_pool_cleanup, + file_ctx->pool, scratch_pool)); + + *stream = svn_stream_from_aprfile2(file_ctx->svndiff, TRUE, result_pool); + return SVN_NO_ERROR; +} + static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, @@ -2043,18 +1915,10 @@ apply_textdelta(void *file_baton, * 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); + ctx->stream = svn_stream_lazyopen_create(delayed_commit_stream_open, + ctx, FALSE, ctx->pool); svn_txdelta_to_svndiff3(handler, handler_baton, ctx->stream, 0, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); @@ -2072,33 +1936,14 @@ change_file_prop(void *file_baton, apr_pool_t *pool) { file_context_t *file = file_baton; - const char *ns; + svn_prop_t *prop; - name = apr_pstrdup(file->pool, name); + prop = apr_palloc(file->pool, sizeof(*prop)); - 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); + prop->name = apr_pstrdup(file->pool, name); + prop->value = svn_string_dup(value, file->pool); - svn_ra_serf__set_prop(file->removed_props, file->url, - ns, name, value, file->pool); - } + svn_hash_sets(file->prop_changes, prop->name, prop); return SVN_NO_ERROR; } @@ -2110,69 +1955,26 @@ close_file(void *file_baton, { 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)) + if ((!ctx->svndiff) && 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) + if (ctx->svndiff || put_empty_file) { svn_ra_serf__handler_t *handler; + int expected_result; + + handler = svn_ra_serf__create_handler(ctx->commit_ctx->session, + scratch_pool); - 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; @@ -2195,31 +1997,33 @@ close_file(void *file_baton, 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->added && ! ctx->copy_path) + expected_result = 201; /* Created */ + else + expected_result = 204; /* Updated */ + + if (handler->sline.code != expected_result) + return svn_error_trace(svn_ra_serf__unexpected_status(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)) + if (apr_hash_count(ctx->prop_changes)) { proppatch_context_t *proppatch; - proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch)); - proppatch->pool = ctx->pool; + proppatch = apr_pcalloc(scratch_pool, sizeof(*proppatch)); + proppatch->pool = scratch_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->commit_ctx = ctx->commit_ctx; + proppatch->prop_changes = ctx->prop_changes; proppatch->base_revision = ctx->base_revision; - SVN_ERR(proppatch_resource(proppatch, ctx->commit, ctx->pool)); + SVN_ERR(proppatch_resource(ctx->commit_ctx->session, + proppatch, scratch_pool)); } return SVN_NO_ERROR; @@ -2233,26 +2037,16 @@ close_edit(void *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, + SVN_ERR(svn_ra_serf__run_merge(&commit_info, 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. */ @@ -2264,12 +2058,10 @@ close_edit(void *edit_baton, { svn_ra_serf__handler_t *handler; - handler = apr_pcalloc(pool, sizeof(*handler)); - handler->handler_pool = pool; + handler = svn_ra_serf__create_handler(ctx->session, 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; @@ -2280,7 +2072,8 @@ close_edit(void *edit_baton, err, svn_ra_serf__context_run_one(handler, pool))); - SVN_ERR_ASSERT(handler->sline.code == 204); + if (handler->sline.code != 204) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); } SVN_ERR(err); @@ -2305,14 +2098,13 @@ abort_edit(void *edit_baton, serf_connection_reset(ctx->session->conns[0]->conn); /* DELETE our aborted activity */ - handler = apr_pcalloc(pool, sizeof(*handler)); - handler->handler_pool = pool; + handler = svn_ra_serf__create_handler(ctx->session, 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; + handler->no_fail_on_http_failure_status = TRUE; if (USING_HTTPV2_COMMIT_SUPPORT(ctx)) /* HTTP v2 */ handler->path = ctx->txn_url; @@ -2326,14 +2118,15 @@ abort_edit(void *edit_baton, 404 if the activity wasn't found. */ if (handler->sline.code != 204 && handler->sline.code != 403 - && handler->sline.code != 404 - ) + && 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_error_trace(svn_ra_serf__unexpected_status(handler)); } + /* Don't delete again if somebody aborts twice */ + ctx->activity_url = NULL; + ctx->txn_url = NULL; + return SVN_NO_ERROR; } @@ -2360,7 +2153,6 @@ svn_ra_serf__get_commit_editor(svn_ra_session_t *ra_session, ctx->pool = pool; ctx->session = session; - ctx->conn = session->conns[0]; ctx->revprop_table = svn_prop_hash_dup(revprop_table, pool); @@ -2427,28 +2219,43 @@ svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session, { 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; + const svn_string_t *tmp_old_value; + svn_boolean_t atomic_capable = FALSE; + svn_prop_t *prop; svn_error_t *err; + if (old_value_p || !value) + SVN_ERR(svn_ra_serf__has_capability(ra_session, &atomic_capable, + SVN_RA_CAPABILITY_ATOMIC_REVPROPS, + pool)); + 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); + SVN_ERR_ASSERT(atomic_capable); } + else if (! value && atomic_capable) + { + svn_string_t *old_value; + /* mod_dav_svn doesn't report a failure when a property delete fails. The + atomic revprop change behavior is a nice workaround, to allow getting + access to the error anyway. - commit = apr_pcalloc(pool, sizeof(*commit)); + Somehow the mod_dav maintainers think that returning an error from + mod_dav's property delete is an RFC violation. + See https://issues.apache.org/bugzilla/show_bug.cgi?id=53525 */ - commit->pool = pool; + SVN_ERR(svn_ra_serf__rev_prop(ra_session, rev, name, &old_value, + pool)); - commit->session = session; - commit->conn = session->conns[0]; + if (!old_value) + return SVN_NO_ERROR; /* Nothing to delete */ + + /* The api expects a double const pointer. Let's make one */ + tmp_old_value = old_value; + old_value_p = &tmp_old_value; + } if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) { @@ -2458,74 +2265,52 @@ svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session, { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, commit->session, - commit->conn, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); SVN_ERR(svn_ra_serf__fetch_dav_prop(&proppatch_target, - commit->conn, vcc_url, rev, - "href", + session, 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->commit_ctx = NULL; /* No lock headers */ 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->prop_changes = apr_hash_make(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) + if (old_value_p) { - svn_string_t *dummy_value = svn_string_create_empty(proppatch_ctx->pool); + prop = apr_palloc(pool, sizeof (*prop)); - svn_ra_serf__set_prop(proppatch_ctx->previous_removed_props, - proppatch_ctx->path, - ns, name, dummy_value, proppatch_ctx->pool); - } + prop->name = name; + prop->value = *old_value_p; - if (value) - { - svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path, - ns, name, value, proppatch_ctx->pool); + proppatch_ctx->old_props = apr_hash_make(pool); + svn_hash_sets(proppatch_ctx->old_props, prop->name, prop); } - else + + prop = apr_palloc(pool, sizeof (*prop)); + + prop->name = name; + prop->value = value; + svn_hash_sets(proppatch_ctx->prop_changes, prop->name, prop); + + err = proppatch_resource(session, proppatch_ctx, pool); + + /* Use specific error code for old property value mismatch. + Use loop to provide the right result with tracing */ + if (err && err->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED) { - value = svn_string_create_empty(proppatch_ctx->pool); + svn_error_t *e = err; - svn_ra_serf__set_prop(proppatch_ctx->removed_props, proppatch_ctx->path, - ns, name, value, proppatch_ctx->pool); + while (e && e->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED) + { + e->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH; + e = e->child; + } } - 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; + return svn_error_trace(err); } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/eagain_bucket.c b/contrib/subversion/subversion/libsvn_ra_serf/eagain_bucket.c new file mode 100644 index 000000000..16387be91 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/eagain_bucket.c @@ -0,0 +1,122 @@ +/* + * eagain_bucket.c : a serf bucket that handles slowing down data + * for specific readers that would have unwanted + * behavior if they read everything too fast + * ==================================================================== + * 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 +#include + +#include "svn_private_config.h" + +#include "ra_serf.h" + +typedef struct eagain_baton_t +{ + const char *data; + apr_size_t remaining; +} eagain_baton_t; + +static apr_status_t +eagain_bucket_read(serf_bucket_t *bucket, + apr_size_t requested, + const char **data, + apr_size_t *len) +{ + eagain_baton_t *eab = bucket->data; + + if (eab->remaining > 0) + { + *data = eab->data; + if (requested > eab->remaining || requested == SERF_READ_ALL_AVAIL) + { + *len = eab->remaining; + eab->data = NULL; + eab->remaining = 0; + } + else + { + *len = requested; + eab->data += requested; + eab->remaining -= requested; + } + + if (eab->remaining) + return APR_SUCCESS; + } + + return APR_EAGAIN; +} + + +static apr_status_t +eagain_bucket_readline(serf_bucket_t *bucket, + int acceptable, + int *found, + const char **data, + apr_size_t *len) +{ + /* ### for now, we know callers won't use this function. */ + svn_error_clear(svn_error__malfunction(TRUE, __FILE__, __LINE__, + "Not implemented.")); + return APR_ENOTIMPL; +} + + +static apr_status_t +eagain_bucket_peek(serf_bucket_t *bucket, + const char **data, + apr_size_t *len) +{ + const eagain_baton_t *eab = bucket->data; + + *data = eab->data ? eab->data : ""; + *len = eab->remaining; + + return APR_SUCCESS; +} + + +static const serf_bucket_type_t delay_bucket_vtable = { + "BUF-EAGAIN", + eagain_bucket_read, + eagain_bucket_readline, + serf_default_read_iovec, + serf_default_read_for_sendfile, + serf_default_read_bucket, + eagain_bucket_peek, + serf_default_destroy_and_data, +}; + + +serf_bucket_t * +svn_ra_serf__create_bucket_with_eagain(const char *data, + apr_size_t len, + serf_bucket_alloc_t *allocator) +{ + eagain_baton_t *eab; + + eab = serf_bucket_mem_alloc(allocator, sizeof(*eab)); + eab->data = data; + eab->remaining = len; + + return serf_bucket_create(&delay_bucket_vtable, allocator, eab); +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/get_deleted_rev.c b/contrib/subversion/subversion/libsvn_ra_serf/get_deleted_rev.c index 40f6b1d9f..624854d7b 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/get_deleted_rev.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/get_deleted_rev.c @@ -36,7 +36,7 @@ * This enum represents the current state of our XML parsing for a REPORT. */ enum drev_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, REPORT, VERSION_NAME }; @@ -75,11 +75,13 @@ getdrev_closed(svn_ra_serf__xml_estate_t *xes, apr_pool_t *scratch_pool) { drev_context_t *drev_ctx = baton; + apr_int64_t rev; SVN_ERR_ASSERT(leaving_state == VERSION_NAME); SVN_ERR_ASSERT(cdata != NULL); - *drev_ctx->revision_deleted = SVN_STR_TO_REV(cdata->data); + SVN_ERR(svn_cstring_atoi64(&rev, cdata->data)); + *drev_ctx->revision_deleted = (svn_revnum_t)rev; return SVN_NO_ERROR; } @@ -90,7 +92,8 @@ static svn_error_t * create_getdrev_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; drev_context_t *drev_ctx = baton; @@ -101,7 +104,7 @@ create_getdrev_body(serf_bucket_t **body_bkt, "S:get-deleted-rev-report", "xmlns:S", SVN_XML_NAMESPACE, "xmlns:D", "DAV:", - NULL, NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "S:path", drev_ctx->path, @@ -146,23 +149,20 @@ svn_ra_serf__get_deleted_rev(svn_ra_session_t *session, drev_ctx->revision_deleted = revision_deleted; SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, - ras, NULL /* conn */, - NULL /* url */, peg_revision, + ras, NULL /* url */, peg_revision, pool, pool)); xmlctx = svn_ra_serf__xml_context_create(getdrev_ttable, NULL, getdrev_closed, NULL, drev_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(ras, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_type = "text/xml"; handler->body_delegate = create_getdrev_body; handler->body_delegate_baton = drev_ctx; - handler->conn = ras->conns[0]; - handler->session = ras; err = svn_ra_serf__context_run_one(handler, pool); diff --git a/contrib/subversion/subversion/libsvn_ra_serf/get_file.c b/contrib/subversion/subversion/libsvn_ra_serf/get_file.c new file mode 100644 index 000000000..cb63b7dd4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/get_file.c @@ -0,0 +1,425 @@ +/* + * get_file.c : entry point for update 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. + * ==================================================================== + */ + + + +#define APR_WANT_STRFUNC +#include +#include + +#include + +#include + +#include "svn_private_config.h" +#include "svn_hash.h" +#include "svn_pools.h" +#include "svn_ra.h" +#include "svn_delta.h" +#include "svn_path.h" +#include "svn_props.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_string_private.h" + +#include "ra_serf.h" +#include "../libsvn_ra/ra_loader.h" + + + + +/* + * This structure represents a single request to GET (fetch) a file with + * its associated Serf session/connection. + */ +typedef struct stream_ctx_t { + + /* The handler representing this particular fetch. */ + svn_ra_serf__handler_t *handler; + + /* Have we read our response headers yet? */ + svn_boolean_t read_headers; + + svn_boolean_t using_compression; + + /* This flag is set when our response is aborted before we reach the + * end and we decide to requeue this request. + */ + svn_boolean_t aborted_read; + apr_off_t aborted_read_size; + + /* This is the amount of data that we have read so far. */ + apr_off_t read_size; + + /* If we're writing this file to a stream, this will be non-NULL. */ + svn_stream_t *result_stream; + +} stream_ctx_t; + + + +/** Routines called when we are fetching a file */ + +static svn_error_t * +headers_fetch(serf_bucket_t *headers, + void *baton, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + stream_ctx_t *fetch_ctx = baton; + + if (fetch_ctx->using_compression) + { + serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip"); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +cancel_fetch(serf_request_t *request, + serf_bucket_t *response, + int status_code, + void *baton) +{ + stream_ctx_t *fetch_ctx = baton; + + /* Uh-oh. Our connection died on us. + * + * The core ra_serf layer will requeue our request - we just need to note + * that we got cut off in the middle of our song. + */ + if (!response) + { + /* If we already started the fetch and opened the file handle, we need + * to hold subsequent read() ops until we get back to where we were + * before the close and we can then resume the textdelta() calls. + */ + if (fetch_ctx->read_headers) + { + if (!fetch_ctx->aborted_read && fetch_ctx->read_size) + { + fetch_ctx->aborted_read = TRUE; + fetch_ctx->aborted_read_size = fetch_ctx->read_size; + } + fetch_ctx->read_size = 0; + } + + return SVN_NO_ERROR; + } + + /* We have no idea what went wrong. */ + SVN_ERR_MALFUNCTION(); +} + + +/* Helper svn_ra_serf__get_file(). Attempts to fetch file contents + * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is + * present in PROPS. + * + * Sets *FOUND_P to TRUE if file contents was successfuly fetched. + * + * Performs all temporary allocations in POOL. + */ +static svn_error_t * +try_get_wc_contents(svn_boolean_t *found_p, + svn_ra_serf__session_t *session, + const char *sha1_checksum_prop, + svn_stream_t *dst_stream, + apr_pool_t *pool) +{ + svn_checksum_t *checksum; + svn_stream_t *wc_stream; + svn_error_t *err; + + /* No contents found by default. */ + *found_p = FALSE; + + if (!session->wc_callbacks->get_wc_contents + || sha1_checksum_prop == NULL) + { + /* Nothing to do. */ + return SVN_NO_ERROR; + } + + SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, + sha1_checksum_prop, pool)); + + err = session->wc_callbacks->get_wc_contents( + session->wc_callback_baton, &wc_stream, checksum, pool); + + if (err) + { + svn_error_clear(err); + + /* Ignore errors for now. */ + return SVN_NO_ERROR; + } + + if (wc_stream) + { + SVN_ERR(svn_stream_copy3(wc_stream, + svn_stream_disown(dst_stream, pool), + NULL, NULL, pool)); + *found_p = TRUE; + } + + return SVN_NO_ERROR; +} + +/* ----------------------------------------------------------------------- + svn_ra_get_file() specific */ + +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +handle_stream(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + stream_ctx_t *fetch_ctx = handler_baton; + apr_status_t status; + + if (fetch_ctx->handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(fetch_ctx->handler)); + + while (1) + { + const char *data; + apr_size_t len; + + status = serf_bucket_read(response, 8000, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + { + return svn_ra_serf__wrap_err(status, NULL); + } + + fetch_ctx->read_size += len; + + if (fetch_ctx->aborted_read) + { + apr_off_t skip; + + /* We haven't caught up to where we were before. */ + if (fetch_ctx->read_size < fetch_ctx->aborted_read_size) + { + /* Eek. What did the file shrink or something? */ + if (APR_STATUS_IS_EOF(status)) + { + SVN_ERR_MALFUNCTION(); + } + + /* Skip on to the next iteration of this loop. */ + if (APR_STATUS_IS_EAGAIN(status)) + { + return svn_ra_serf__wrap_err(status, NULL); + } + continue; + } + + /* Woo-hoo. We're back. */ + fetch_ctx->aborted_read = FALSE; + + /* Increment data and len by the difference. */ + skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size); + data += skip; + len -= (apr_size_t)skip; + } + + if (len) + { + apr_size_t written_len; + + written_len = len; + + SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, + &written_len)); + } + + if (status) + { + return svn_ra_serf__wrap_err(status, NULL); + } + } + /* not reached */ +} + +/* Baton for get_file_prop_cb */ +struct file_prop_baton_t +{ + apr_pool_t *result_pool; + svn_node_kind_t kind; + apr_hash_t *props; + const char *sha1_checksum; +}; + +/* Implements svn_ra_serf__prop_func_t for svn_ra_serf__get_file */ +static svn_error_t * +get_file_prop_cb(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + struct file_prop_baton_t *fb = baton; + const char *svn_name; + + if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0) + { + const char *val = value->data; + + if (strcmp(val, "collection") == 0) + fb->kind = svn_node_dir; + else + fb->kind = svn_node_file; + + return SVN_NO_ERROR; + } + else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0 + && strcmp(name, "sha1-checksum") == 0) + { + fb->sha1_checksum = apr_pstrdup(fb->result_pool, value->data); + } + + if (!fb->props) + return SVN_NO_ERROR; + + svn_name = svn_ra_serf__svnname_from_wirename(ns, name, fb->result_pool); + if (svn_name) + { + svn_hash_sets(fb->props, svn_name, + svn_string_dup(value, fb->result_pool)); + } + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__get_file(svn_ra_session_t *ra_session, + const char *path, + svn_revnum_t revision, + svn_stream_t *stream, + svn_revnum_t *fetched_rev, + apr_hash_t **props, + apr_pool_t *pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + const char *fetch_url; + const svn_ra_serf__dav_props_t *which_props; + svn_ra_serf__handler_t *propfind_handler; + struct file_prop_baton_t fb; + + /* Fetch properties. */ + + fetch_url = svn_path_url_add_component2(session->session_url.path, path, pool); + + /* The simple case is if we want HEAD - then a GET on the fetch_url is fine. + * + * Otherwise, we need to get the baseline version for this particular + * revision and then fetch that file. + */ + if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) + { + SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev, + session, + fetch_url, revision, + pool, pool)); + revision = SVN_INVALID_REVNUM; + } + /* REVISION is always SVN_INVALID_REVNUM */ + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); + + if (props) + which_props = all_props; + else if (stream && session->wc_callbacks->get_wc_contents) + which_props = type_and_checksum_props; + else + which_props = check_path_props; + + fb.result_pool = pool; + fb.props = props ? apr_hash_make(pool) : NULL; + fb.kind = svn_node_unknown; + fb.sha1_checksum = NULL; + + SVN_ERR(svn_ra_serf__create_propfind_handler(&propfind_handler, session, + fetch_url, SVN_INVALID_REVNUM, + "0", which_props, + get_file_prop_cb, &fb, + pool)); + + SVN_ERR(svn_ra_serf__context_run_one(propfind_handler, pool)); + + /* Verify that resource type is not collection. */ + if (fb.kind != svn_node_file) + { + return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, + _("Can't get text contents of a directory")); + } + + if (props) + *props = fb.props; + + if (stream) + { + svn_boolean_t found; + SVN_ERR(try_get_wc_contents(&found, session, fb.sha1_checksum, stream, pool)); + + /* No contents found in the WC, let's fetch from server. */ + if (!found) + { + stream_ctx_t *stream_ctx; + svn_ra_serf__handler_t *handler; + + /* Create the fetch context. */ + stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx)); + stream_ctx->result_stream = stream; + stream_ctx->using_compression = session->using_compression; + + handler = svn_ra_serf__create_handler(session, pool); + + handler->method = "GET"; + handler->path = fetch_url; + + handler->custom_accept_encoding = TRUE; + handler->no_dav_headers = TRUE; + + handler->header_delegate = headers_fetch; + handler->header_delegate_baton = stream_ctx; + + handler->response_handler = handle_stream; + handler->response_baton = stream_ctx; + + handler->response_error = cancel_fetch; + handler->response_error_baton = stream_ctx; + + stream_ctx->handler = handler; + + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); + + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + } + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/get_lock.c b/contrib/subversion/subversion/libsvn_ra_serf/get_lock.c new file mode 100644 index 000000000..24d7100fe --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/get_lock.c @@ -0,0 +1,337 @@ +/* + * get_lock.c : obtain single lock information 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 +#include + +#include "svn_dav.h" +#include "svn_pools.h" +#include "svn_ra.h" + +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_path.h" +#include "svn_time.h" +#include "svn_private_config.h" + +#include "ra_serf.h" + + +/* + * This enum represents the current state of our XML parsing for a REPORT. + */ +enum { + INITIAL = 0, + MULTISTATUS, + RESPONSE, + PROPSTAT, + PROP, + LOCK_DISCOVERY, + ACTIVE_LOCK, + LOCK_TYPE, + LOCK_SCOPE, + DEPTH, + TIMEOUT, + LOCK_TOKEN, + OWNER, + HREF +}; + +typedef struct lock_info_t { + apr_pool_t *pool; + + const char *path; + + svn_lock_t *lock; + + svn_boolean_t read_headers; + + svn_ra_serf__handler_t *handler; + + /* The expat handler. We wrap this to do a bit more work. */ + svn_ra_serf__response_handler_t inner_handler; + void *inner_baton; + +} lock_info_t; + +#define D_ "DAV:" +#define S_ SVN_XML_NAMESPACE +static const svn_ra_serf__xml_transition_t locks_ttable[] = { + /* The INITIAL state can transition into D:prop (LOCK) or + to D:multistatus (PROPFIND) */ + { INITIAL, D_, "multistatus", MULTISTATUS, + FALSE, { NULL }, FALSE }, + + { MULTISTATUS, D_, "response", RESPONSE, + FALSE, { NULL }, FALSE }, + + { RESPONSE, D_, "propstat", PROPSTAT, + FALSE, { NULL }, FALSE }, + + { PROPSTAT, D_, "prop", PROP, + FALSE, { NULL }, FALSE }, + + { PROP, D_, "lockdiscovery", LOCK_DISCOVERY, + FALSE, { NULL }, FALSE }, + + { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK, + FALSE, { NULL }, FALSE }, + +#if 0 + /* ### we don't really need to parse locktype/lockscope. we know what + ### the values are going to be. we *could* validate that the only + ### possible children are D:write and D:exclusive. we'd need to + ### modify the state transition to tell us about all children + ### (ie. maybe support "*" for the name) and then validate. but it + ### just isn't important to validate, so disable this for now... */ + + { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE, + FALSE, { NULL }, FALSE }, + + { LOCK_TYPE, D_, "write", WRITE, + FALSE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE, + FALSE, { NULL }, FALSE }, + + { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE, + FALSE, { NULL }, TRUE }, +#endif /* 0 */ + + { ACTIVE_LOCK, D_, "timeout", TIMEOUT, + TRUE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN, + FALSE, { NULL }, FALSE }, + + { LOCK_TOKEN, D_, "href", HREF, + TRUE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "owner", OWNER, + TRUE, { NULL }, TRUE }, + + /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */ + + { 0 } +}; + +static const int locks_expected_status[] = { + 207, + 0 +}; + +/* Conforms to svn_ra_serf__xml_closed_t */ +static svn_error_t * +locks_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) +{ + lock_info_t *lock_ctx = baton; + + if (leaving_state == TIMEOUT) + { + if (strcasecmp(cdata->data, "Infinite") == 0) + lock_ctx->lock->expiration_date = 0; + else if (strncasecmp(cdata->data, "Second-", 7) == 0) + { + unsigned n; + SVN_ERR(svn_cstring_atoui(&n, cdata->data+7)); + + lock_ctx->lock->expiration_date = apr_time_now() + + apr_time_from_sec(n); + } + else + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("Invalid LOCK timeout value '%s'"), + cdata->data); + } + else if (leaving_state == HREF) + { + if (cdata->len) + { + char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len); + + apr_collapse_spaces(buf, buf); + lock_ctx->lock->token = buf; + } + } + else if (leaving_state == OWNER) + { + if (cdata->len) + { + lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool, + cdata->data, cdata->len); + } + } + + return SVN_NO_ERROR; +} + +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +handle_lock(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + lock_info_t *ctx = handler_baton; + + if (!ctx->read_headers) + { + serf_bucket_t *headers; + const char *val; + + headers = serf_bucket_response_get_headers(response); + + val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER); + if (val) + { + ctx->lock->owner = apr_pstrdup(ctx->pool, val); + } + + val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER); + if (val) + { + SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val, + ctx->pool)); + } + + ctx->read_headers = TRUE; + } + + return ctx->inner_handler(request, response, ctx->inner_baton, pool); +} + +/* Implements svn_ra_serf__request_body_delegate_t */ +static svn_error_t * +create_getlock_body(serf_bucket_t **body_bkt, + void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + serf_bucket_t *buckets; + + buckets = serf_bucket_aggregate_create(alloc); + + svn_ra_serf__add_xml_header_buckets(buckets, alloc); + svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind", + "xmlns", "DAV:", + SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "lockdiscovery", SVN_VA_NULL); + svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop"); + svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind"); + + *body_bkt = buckets; + return SVN_NO_ERROR; +} + +static svn_error_t* +setup_getlock_headers(serf_bucket_t *headers, + void *baton, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + serf_bucket_headers_setn(headers, "Depth", "0"); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__get_lock(svn_ra_session_t *ra_session, + svn_lock_t **lock, + const char *path, + apr_pool_t *result_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + svn_ra_serf__handler_t *handler; + svn_ra_serf__xml_context_t *xmlctx; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + lock_info_t *lock_ctx; + const char *req_url; + svn_error_t *err; + + req_url = svn_path_url_add_component2(session->session_url.path, path, + scratch_pool); + + lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx)); + lock_ctx->pool = result_pool; + lock_ctx->path = req_url; + lock_ctx->lock = svn_lock_create(result_pool); + lock_ctx->lock->path = apr_pstrdup(result_pool, path); + + xmlctx = svn_ra_serf__xml_context_create(locks_ttable, + NULL, locks_closed, NULL, + lock_ctx, + scratch_pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, + locks_expected_status, + scratch_pool); + + handler->method = "PROPFIND"; + handler->path = req_url; + handler->body_type = "text/xml"; + + handler->body_delegate = create_getlock_body; + handler->body_delegate_baton = lock_ctx; + + handler->header_delegate = setup_getlock_headers; + handler->header_delegate_baton = lock_ctx; + + handler->no_dav_headers = TRUE; + + lock_ctx->inner_handler = handler->response_handler; + lock_ctx->inner_baton = handler->response_baton; + handler->response_handler = handle_lock; + handler->response_baton = lock_ctx; + + lock_ctx->handler = handler; + + err = svn_ra_serf__context_run_one(handler, scratch_pool); + + if ((err && (handler->sline.code == 500 || handler->sline.code == 501)) + || svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) + return svn_error_trace( + svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, + _("Server does not support locking features"))); + else if (svn_error_find_cause(err, SVN_ERR_FS_NOT_FOUND)) + svn_error_clear(err); /* Behave like the other RA layers */ + else if (handler->sline.code != 207) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + + if (lock_ctx->lock && lock_ctx->lock->token) + *lock = lock_ctx->lock; + else + *lock = NULL; + + svn_pool_destroy(scratch_pool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/getdate.c b/contrib/subversion/subversion/libsvn_ra_serf/getdate.c index cc1014ef0..30d57f310 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/getdate.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/getdate.c @@ -44,7 +44,7 @@ * This enum represents the current state of our XML parsing for a REPORT. */ enum date_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, REPORT, VERSION_NAME }; @@ -82,11 +82,14 @@ date_closed(svn_ra_serf__xml_estate_t *xes, apr_pool_t *scratch_pool) { date_context_t *date_ctx = baton; + apr_int64_t rev; SVN_ERR_ASSERT(leaving_state == VERSION_NAME); SVN_ERR_ASSERT(cdata != NULL); - *date_ctx->revision = SVN_STR_TO_REV(cdata->data); + SVN_ERR(svn_cstring_atoi64(&rev, cdata->data)); + + *date_ctx->revision = (svn_revnum_t)rev; return SVN_NO_ERROR; } @@ -97,7 +100,8 @@ static svn_error_t * create_getdate_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; date_context_t *date_ctx = baton; @@ -107,7 +111,7 @@ create_getdate_body(serf_bucket_t **body_bkt, svn_ra_serf__add_open_tag_buckets(buckets, alloc, "S:dated-rev-report", "xmlns:S", SVN_XML_NAMESPACE, "xmlns:D", "DAV:", - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "D:" SVN_DAV__CREATIONDATE, @@ -131,40 +135,37 @@ svn_ra_serf__get_dated_revision(svn_ra_session_t *ra_session, svn_ra_serf__handler_t *handler; svn_ra_serf__xml_context_t *xmlctx; const char *report_target; - svn_error_t *err; date_ctx = apr_palloc(pool, sizeof(*date_ctx)); date_ctx->time = tm; date_ctx->revision = revision; - SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool)); + SVN_ERR(svn_ra_serf__report_resource(&report_target, session, pool)); xmlctx = svn_ra_serf__xml_context_create(date_ttable, NULL, date_closed, NULL, date_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = report_target; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; handler->body_delegate = create_getdate_body; handler->body_delegate_baton = date_ctx; *date_ctx->revision = SVN_INVALID_REVNUM; - err = svn_ra_serf__context_run_one(handler, pool); + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - SVN_ERR(svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - report_target, - handler->location), - err)); + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); - SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*revision)); + if (!SVN_IS_VALID_REVNUM(*revision)) + return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, + _("The REPORT response did not include " + "the requested properties")); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/getlocations.c b/contrib/subversion/subversion/libsvn_ra_serf/getlocations.c index 1ae4f82dd..063272ec3 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/getlocations.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/getlocations.c @@ -43,7 +43,7 @@ * This enum represents the current state of our XML parsing for a REPORT. */ enum loc_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, REPORT, LOCATION }; @@ -94,7 +94,12 @@ getloc_closed(svn_ra_serf__xml_estate_t *xes, path = svn_hash_gets(attrs, "path"); if (revstr != NULL && path != NULL) { - svn_revnum_t rev = SVN_STR_TO_REV(revstr); + apr_int64_t rev_val; + svn_revnum_t rev; + + SVN_ERR(svn_cstring_atoi64(&rev_val, revstr)); + rev = (svn_revnum_t)rev_val; + apr_hash_set(loc_ctx->paths, apr_pmemdup(loc_ctx->pool, &rev, sizeof(rev)), sizeof(rev), apr_pstrdup(loc_ctx->pool, path)); @@ -109,7 +114,8 @@ static svn_error_t * create_get_locations_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; loc_context_t *loc_ctx = baton; @@ -121,7 +127,7 @@ create_get_locations_body(serf_bucket_t **body_bkt, "S:get-locations", "xmlns:S", SVN_XML_NAMESPACE, "xmlns:D", "DAV:", - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "S:path", loc_ctx->path, @@ -159,7 +165,6 @@ svn_ra_serf__get_locations(svn_ra_session_t *ra_session, svn_ra_serf__handler_t *handler; svn_ra_serf__xml_context_t *xmlctx; const char *req_url; - svn_error_t *err; loc_ctx = apr_pcalloc(pool, sizeof(*loc_ctx)); loc_ctx->pool = pool; @@ -171,31 +176,26 @@ svn_ra_serf__get_locations(svn_ra_session_t *ra_session, *locations = loc_ctx->paths; SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, - session, NULL /* conn */, - NULL /* url */, peg_revision, + session, NULL /* url */, peg_revision, pool, pool)); xmlctx = svn_ra_serf__xml_context_create(getloc_ttable, NULL, getloc_closed, NULL, loc_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_delegate = create_get_locations_body; handler->body_delegate_baton = loc_ctx; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; - err = svn_ra_serf__context_run_one(handler, pool); + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - SVN_ERR(svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - req_url, - handler->location), - err)); + SVN_ERR(svn_ra_serf__error_on_status(handler->sline, + handler->path, + handler->location)); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/getlocationsegments.c b/contrib/subversion/subversion/libsvn_ra_serf/getlocationsegments.c index e5c58a90b..884ab5daa 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/getlocationsegments.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/getlocationsegments.c @@ -52,8 +52,8 @@ typedef struct gls_context_t { } gls_context_t; -enum { - INITIAL = 0, +enum locseg_state_e { + INITIAL = XML_STATE_INITIAL, REPORT, SEGMENT }; @@ -84,6 +84,8 @@ gls_closed(svn_ra_serf__xml_estate_t *xes, const char *path; const char *start_str; const char *end_str; + apr_int64_t start_val; + apr_int64_t end_val; svn_location_segment_t segment; SVN_ERR_ASSERT(leaving_state == SEGMENT); @@ -95,9 +97,12 @@ gls_closed(svn_ra_serf__xml_estate_t *xes, /* The transition table said these must exist. */ SVN_ERR_ASSERT(start_str && end_str); + SVN_ERR(svn_cstring_atoi64(&start_val, start_str)); + SVN_ERR(svn_cstring_atoi64(&end_val, end_str)); + segment.path = path; /* may be NULL */ - segment.range_start = SVN_STR_TO_REV(start_str); - segment.range_end = SVN_STR_TO_REV(end_str); + segment.range_start = (svn_revnum_t)start_val; + segment.range_end = (svn_revnum_t)end_val; SVN_ERR(gls_ctx->receiver(&segment, gls_ctx->receiver_baton, scratch_pool)); return SVN_NO_ERROR; @@ -109,7 +114,8 @@ static svn_error_t * create_gls_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; gls_context_t *gls_ctx = baton; @@ -119,7 +125,7 @@ create_gls_body(serf_bucket_t **body_bkt, svn_ra_serf__add_open_tag_buckets(buckets, alloc, "S:get-location-segments", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "S:path", gls_ctx->path, @@ -173,31 +179,29 @@ svn_ra_serf__get_location_segments(svn_ra_session_t *ra_session, gls_ctx->receiver_baton = receiver_baton; SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, - session, NULL /* conn */, - NULL /* url */, peg_revision, + session, NULL /* url */, peg_revision, pool, pool)); xmlctx = svn_ra_serf__xml_context_create(gls_ttable, NULL, gls_closed, NULL, gls_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_delegate = create_gls_body; handler->body_delegate_baton = gls_ctx; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; err = svn_ra_serf__context_run_one(handler, pool); - err = svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location), - err); + if (!err) + { + err = svn_ra_serf__error_on_status(handler->sline, + handler->path, + handler->location); + } if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL); diff --git a/contrib/subversion/subversion/libsvn_ra_serf/getlocks.c b/contrib/subversion/subversion/libsvn_ra_serf/getlocks.c index df201a750..518d2aaa0 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/getlocks.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/getlocks.c @@ -47,8 +47,8 @@ /* * This enum represents the current state of our XML parsing for a REPORT. */ -enum { - INITIAL = 0, +enum getlocks_state_e { + INITIAL = XML_STATE_INITIAL, REPORT, LOCK, PATH, @@ -213,7 +213,8 @@ static svn_error_t * create_getlocks_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { lock_context_t *lock_ctx = baton; serf_bucket_t *buckets; @@ -222,7 +223,7 @@ create_getlocks_body(serf_bucket_t **body_bkt, svn_ra_serf__add_open_tag_buckets( buckets, alloc, "S:get-locks-report", "xmlns:S", SVN_XML_NAMESPACE, - "depth", svn_depth_to_word(lock_ctx->requested_depth), NULL); + "depth", svn_depth_to_word(lock_ctx->requested_depth), SVN_VA_NULL); svn_ra_serf__add_close_tag_buckets(buckets, alloc, "S:get-locks-report"); *body_bkt = buckets; @@ -244,12 +245,11 @@ svn_ra_serf__get_locks(svn_ra_session_t *ra_session, svn_error_t *err; req_url = svn_path_url_add_component2(session->session_url.path, path, pool); - SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session, - NULL, pool)); + SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session, pool)); lock_ctx = apr_pcalloc(pool, sizeof(*lock_ctx)); lock_ctx->pool = pool; - lock_ctx->path = apr_pstrcat(pool, "/", rel_path, (char *)NULL); + lock_ctx->path = apr_pstrcat(pool, "/", rel_path, SVN_VA_NULL); lock_ctx->requested_depth = depth; lock_ctx->hash = apr_hash_make(pool); @@ -257,33 +257,41 @@ svn_ra_serf__get_locks(svn_ra_session_t *ra_session, NULL, getlocks_closed, NULL, lock_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; handler->body_delegate = create_getlocks_body; handler->body_delegate_baton = lock_ctx; err = svn_ra_serf__context_run_one(handler, pool); - - /* Wrap the server generated error for an unsupported report with the - documented error for this ra function. */ - if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) - err = svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL); - - SVN_ERR(err); + + if (err) + { + if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) + { + /* The server told us that it doesn't support this report type. + We return the documented error for svn_ra_get_locks(), but + with the original error report */ + return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL); + } + else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + /* File doesn't exist in HEAD: Not an error */ + svn_error_clear(err); + } + else + return svn_error_trace(err); + } /* We get a 404 when a path doesn't exist in HEAD, but it might have existed earlier (E.g. 'svn ls http://s/svn/trunk/file@1' */ - if (handler->sline.code != 404) + if (handler->sline.code != 200 + && handler->sline.code != 404) { - SVN_ERR(svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location)); + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); } *locks = lock_ctx->hash; diff --git a/contrib/subversion/subversion/libsvn_ra_serf/inherited_props.c b/contrib/subversion/subversion/libsvn_ra_serf/inherited_props.c index f1172e7b6..6edafb102 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/inherited_props.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/inherited_props.c @@ -28,12 +28,14 @@ #include "svn_hash.h" #include "svn_path.h" #include "svn_ra.h" +#include "svn_sorts.h" #include "svn_string.h" #include "svn_xml.h" #include "svn_props.h" #include "svn_base64.h" #include "private/svn_dav_protocol.h" +#include "private/svn_sorts_private.h" #include "../libsvn_ra/ra_loader.h" #include "svn_private_config.h" #include "ra_serf.h" @@ -41,7 +43,7 @@ /* The current state of our XML parsing. */ typedef enum iprops_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, IPROPS_REPORT, IPROPS_ITEM, IPROPS_PATH, @@ -141,9 +143,7 @@ iprops_closed(svn_ra_serf__xml_estate_t *xes, return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); iprops_ctx->curr_iprop->path_or_url = - svn_path_url_add_component2(iprops_ctx->repos_root_url, - cdata->data, - iprops_ctx->pool); + apr_pstrdup(iprops_ctx->pool, cdata->data); } else if (leaving_state == IPROPS_PROPNAME) { @@ -197,7 +197,8 @@ static svn_error_t * create_iprops_body(serf_bucket_t **bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { iprops_context_t *iprops_ctx = baton; serf_bucket_t *body_bkt; @@ -207,7 +208,7 @@ create_iprops_body(serf_bucket_t **bkt, svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "S:" SVN_DAV__INHERITED_PROPS_REPORT, "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__REVISION, apr_ltoa(pool, iprops_ctx->revision), @@ -220,6 +221,131 @@ create_iprops_body(serf_bucket_t **bkt, return SVN_NO_ERROR; } +/* Per request information for get_iprops_via_more_requests */ +typedef struct iprop_rq_info_t +{ + const char *relpath; + const char *urlpath; + apr_hash_t *props; + svn_ra_serf__handler_t *handler; +} iprop_rq_info_t; + + +/* Assumes session reparented to the repository root. The old session + root is passed as session_url */ +static svn_error_t * +get_iprops_via_more_requests(svn_ra_session_t *ra_session, + apr_array_header_t **iprops, + const char *session_url, + const char *path, + svn_revnum_t revision, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + const char *url; + const char *relpath; + apr_array_header_t *rq_info; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_interval_time_t waittime_left = session->timeout; + const svn_revnum_t rev_marker = SVN_INVALID_REVNUM; + int i; + + rq_info = apr_array_make(scratch_pool, 16, sizeof(iprop_rq_info_t *)); + + if (!svn_path_is_empty(path)) + url = svn_path_url_add_component2(session_url, path, scratch_pool); + else + url = session_url; + + relpath = svn_uri_skip_ancestor(session->repos_root_str, url, scratch_pool); + + /* Create all requests */ + while (relpath[0] != '\0') + { + iprop_rq_info_t *rq = apr_pcalloc(scratch_pool, sizeof(*rq)); + + relpath = svn_relpath_dirname(relpath, scratch_pool); + + rq->relpath = relpath; + rq->props = apr_hash_make(scratch_pool); + + SVN_ERR(svn_ra_serf__get_stable_url(&rq->urlpath, NULL, session, + svn_path_url_add_component2( + session->repos_root.path, + relpath, scratch_pool), + revision, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_ra_serf__create_propfind_handler( + &rq->handler, session, + rq->urlpath, + rev_marker, "0", all_props, + svn_ra_serf__deliver_svn_props, + rq->props, + scratch_pool)); + + /* Allow ignoring authz problems */ + rq->handler->no_fail_on_http_failure_status = TRUE; + + svn_ra_serf__request_create(rq->handler); + + APR_ARRAY_PUSH(rq_info, iprop_rq_info_t *) = rq; + } + + while (TRUE) + { + svn_pool_clear(iterpool); + + SVN_ERR(svn_ra_serf__context_run(session, &waittime_left, iterpool)); + + for (i = 0; i < rq_info->nelts; i++) + { + iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); + + if (!rq->handler->done) + break; + } + + if (i >= rq_info->nelts) + break; /* All requests done */ + } + + *iprops = apr_array_make(result_pool, rq_info->nelts, + sizeof(svn_prop_inherited_item_t *)); + + /* And now create the result set */ + for (i = 0; i < rq_info->nelts; i++) + { + iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); + apr_hash_t *node_props; + svn_prop_inherited_item_t *new_iprop; + + if (rq->handler->sline.code != 207 && rq->handler->sline.code != 403) + { + if (rq->handler->server_error) + SVN_ERR(svn_ra_serf__server_error_create(rq->handler, + scratch_pool)); + + return svn_error_trace(svn_ra_serf__unexpected_status(rq->handler)); + } + + node_props = rq->props; + + svn_ra_serf__keep_only_regular_props(node_props, scratch_pool); + + if (!apr_hash_count(node_props)) + continue; + + new_iprop = apr_palloc(result_pool, sizeof(*new_iprop)); + new_iprop->path_or_url = apr_pstrdup(result_pool, rq->relpath); + new_iprop->prop_hash = svn_prop_hash_dup(node_props, result_pool); + svn_sort__array_insert(*iprops, &new_iprop, 0); + } + + return SVN_NO_ERROR; +} + /* Request a inherited-props-report from the URL attached to RA_SESSION, and fill the IPROPS array hash with the results. */ svn_error_t * @@ -230,20 +356,53 @@ svn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_error_t *err; iprops_context_t *iprops_ctx; svn_ra_serf__session_t *session = ra_session->priv; svn_ra_serf__handler_t *handler; svn_ra_serf__xml_context_t *xmlctx; const char *req_url; + svn_boolean_t iprop_capable; + + SVN_ERR(svn_ra_serf__has_capability(ra_session, &iprop_capable, + SVN_RA_CAPABILITY_INHERITED_PROPS, + scratch_pool)); + + if (!iprop_capable) + { + svn_error_t *err; + const char *reparent_uri = NULL; + const char *session_uri; + const char *repos_root_url; + + SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root_url, + scratch_pool)); + + session_uri = apr_pstrdup(scratch_pool, session->session_url_str); + if (strcmp(repos_root_url, session->session_url_str) != 0) + { + reparent_uri = session_uri; + SVN_ERR(svn_ra_serf__reparent(ra_session, repos_root_url, + scratch_pool)); + } + + err = get_iprops_via_more_requests(ra_session, iprops, session_uri, path, + revision, result_pool, scratch_pool); + + if (reparent_uri) + err = svn_error_compose_create(err, + svn_ra_serf__reparent(ra_session, + reparent_uri , + scratch_pool)); + + return svn_error_trace(err); + } SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, session, - NULL /* conn */, NULL /* url */, revision, - result_pool, scratch_pool)); + scratch_pool, scratch_pool)); SVN_ERR_ASSERT(session->repos_root_str); @@ -258,26 +417,24 @@ svn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session, iprops_ctx->revision = revision; xmlctx = svn_ra_serf__xml_context_create(iprops_table, - iprops_opened, iprops_closed, NULL, + iprops_opened, iprops_closed, + NULL, iprops_ctx, scratch_pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, + scratch_pool); handler->method = "REPORT"; handler->path = req_url; - handler->conn = session->conns[0]; - handler->session = session; + handler->body_delegate = create_iprops_body; handler->body_delegate_baton = iprops_ctx; handler->body_type = "text/xml"; - handler->handler_pool = scratch_pool; - - err = svn_ra_serf__context_run_one(handler, scratch_pool); - SVN_ERR(svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location), - err)); + + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); + + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); *iprops = iprops_ctx->iprops; diff --git a/contrib/subversion/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in b/contrib/subversion/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in new file mode 100644 index 000000000..e02ef12af --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_ra_serf +Description: Subversion HTTP/WebDAV Protocol Repository Access Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr serf-1 +Libs: -L${libdir} -lsvn_ra_serf @SVN_XML_LIBS@ @SVN_ZLIB_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/lock.c b/contrib/subversion/subversion/libsvn_ra_serf/lock.c new file mode 100644 index 000000000..dd045e3a1 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/lock.c @@ -0,0 +1,679 @@ +/* + * lock.c : entry point for locking 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 +#include +#include + +#include "svn_dav.h" +#include "svn_hash.h" +#include "svn_pools.h" +#include "svn_ra.h" + +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_path.h" +#include "svn_sorts.h" +#include "svn_time.h" +#include "svn_private_config.h" +#include "private/svn_sorts_private.h" + +#include "ra_serf.h" + + +/* + * This enum represents the current state of our XML parsing for a REPORT. + */ +enum { + INITIAL = 0, + PROP, + LOCK_DISCOVERY, + ACTIVE_LOCK, + LOCK_TYPE, + LOCK_SCOPE, + DEPTH, + TIMEOUT, + LOCK_TOKEN, + OWNER, + HREF +}; + + +typedef struct lock_ctx_t { + apr_pool_t *pool; + + const char *path; + + const char *token; /* For unlock */ + svn_lock_t *lock; /* For lock */ + + svn_boolean_t force; + svn_revnum_t revision; + + svn_boolean_t read_headers; + + svn_ra_serf__handler_t *handler; + + /* The expat handler. We wrap this to do a bit more work. */ + svn_ra_serf__response_handler_t inner_handler; + void *inner_baton; + +} lock_ctx_t; + + +#define D_ "DAV:" +#define S_ SVN_XML_NAMESPACE +static const svn_ra_serf__xml_transition_t locks_ttable[] = { + /* The INITIAL state can transition into D:prop (LOCK) or + to D:multistatus (PROPFIND) */ + { INITIAL, D_, "prop", PROP, + FALSE, { NULL }, FALSE }, + + { PROP, D_, "lockdiscovery", LOCK_DISCOVERY, + FALSE, { NULL }, FALSE }, + + { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK, + FALSE, { NULL }, FALSE }, + +#if 0 + /* ### we don't really need to parse locktype/lockscope. we know what + ### the values are going to be. we *could* validate that the only + ### possible children are D:write and D:exclusive. we'd need to + ### modify the state transition to tell us about all children + ### (ie. maybe support "*" for the name) and then validate. but it + ### just isn't important to validate, so disable this for now... */ + + { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE, + FALSE, { NULL }, FALSE }, + + { LOCK_TYPE, D_, "write", WRITE, + FALSE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE, + FALSE, { NULL }, FALSE }, + + { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE, + FALSE, { NULL }, TRUE }, +#endif /* 0 */ + + { ACTIVE_LOCK, D_, "timeout", TIMEOUT, + TRUE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN, + FALSE, { NULL }, FALSE }, + + { LOCK_TOKEN, D_, "href", HREF, + TRUE, { NULL }, TRUE }, + + { ACTIVE_LOCK, D_, "owner", OWNER, + TRUE, { NULL }, TRUE }, + + /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */ + + { 0 } +}; + +/* Conforms to svn_ra_serf__xml_closed_t */ +static svn_error_t * +locks_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) +{ + lock_ctx_t *lock_ctx = baton; + + if (leaving_state == TIMEOUT) + { + /* This function just parses the result of our own lock request, + so on a normal server we will only encounter 'Infinite' here. */ + if (strcasecmp(cdata->data, "Infinite") == 0) + lock_ctx->lock->expiration_date = 0; + else if (strncasecmp(cdata->data, "Second-", 7) == 0) + { + unsigned n; + SVN_ERR(svn_cstring_atoui(&n, cdata->data+7)); + + lock_ctx->lock->expiration_date = apr_time_now() + + apr_time_from_sec(n); + } + else + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("Invalid LOCK timeout value '%s'"), + cdata->data); + } + else if (leaving_state == HREF) + { + if (cdata->len) + { + char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len); + + apr_collapse_spaces(buf, buf); + lock_ctx->lock->token = buf; + } + } + else if (leaving_state == OWNER) + { + if (cdata->len) + { + lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool, + cdata->data, cdata->len); + } + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +set_lock_headers(serf_bucket_t *headers, + void *baton, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + lock_ctx_t *lock_ctx = baton; + + if (lock_ctx->force) + { + serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, + SVN_DAV_OPTION_LOCK_STEAL); + } + + if (SVN_IS_VALID_REVNUM(lock_ctx->revision)) + { + serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER, + apr_ltoa(pool, lock_ctx->revision)); + } + + return APR_SUCCESS; +} + +/* Helper function for svn_ra_serf__lock and svn_ra_serf__unlock */ +static svn_error_t * +run_locks(svn_ra_serf__session_t *sess, + apr_array_header_t *lock_ctxs, + svn_boolean_t locking, + svn_ra_lock_callback_t lock_func, + void *lock_baton, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool; + apr_interval_time_t waittime_left = sess->timeout; + + assert(sess->pending_error == SVN_NO_ERROR); + + iterpool = svn_pool_create(scratch_pool); + while (lock_ctxs->nelts) + { + int i; + + svn_pool_clear(iterpool); + + SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool)); + + for (i = 0; i < lock_ctxs->nelts; i++) + { + lock_ctx_t *ctx = APR_ARRAY_IDX(lock_ctxs, i, lock_ctx_t *); + + if (ctx->handler->done) + { + svn_error_t *server_err = NULL; + svn_error_t *cb_err = NULL; + svn_error_t *err; + + if (ctx->handler->server_error) + server_err = svn_ra_serf__server_error_create(ctx->handler, iterpool); + + /* Api users expect specific error code to detect failures, + pass the rest to svn_ra_serf__error_on_status */ + switch (ctx->handler->sline.code) + { + case 200: + case 204: + err = NULL; /* (un)lock succeeded */ + break; + + case 400: + err = svn_error_createf(SVN_ERR_FS_NO_SUCH_LOCK, NULL, + _("No lock on path '%s' (%d %s)"), + ctx->path, + ctx->handler->sline.code, + ctx->handler->sline.reason); + break; + case 403: + /* ### Authz can also lead to 403. */ + err = svn_error_createf(SVN_ERR_FS_LOCK_OWNER_MISMATCH, + NULL, + _("Unlock of '%s' failed (%d %s)"), + ctx->path, + ctx->handler->sline.code, + ctx->handler->sline.reason); + break; + case 405: + err = svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, + NULL, + _("Path '%s' doesn't exist in " + "HEAD revision (%d %s)"), + ctx->path, + ctx->handler->sline.code, + ctx->handler->sline.reason); + break; + case 423: + err = svn_error_createf(SVN_ERR_FS_PATH_ALREADY_LOCKED, + NULL, + _("Path '%s' already locked " + "(%d %s)"), + ctx->path, + ctx->handler->sline.code, + ctx->handler->sline.reason); + break; + + case 404: + case 409: + case 500: + if (server_err) + { + /* Handle out of date, etc by just passing the server + error */ + err = NULL; + break; + } + + /* Fall through */ + default: + err = svn_ra_serf__unexpected_status(ctx->handler); + break; + } + + if (server_err && err && server_err->apr_err == err->apr_err) + err = svn_error_compose_create(server_err, err); + else + err = svn_error_compose_create(err, server_err); + + if (err + && !SVN_ERR_IS_UNLOCK_ERROR(err) + && !SVN_ERR_IS_LOCK_ERROR(err)) + { + /* If the error that we are going to report is just about the + POST unlock hook, we should first report that the operation + succeeded, or the repository and working copy will be + out of sync... */ + + if (lock_func && + err->apr_err == SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED) + { + err = svn_error_compose_create( + err, lock_func(lock_baton, ctx->path, locking, + NULL, NULL, ctx->pool)); + } + + return svn_error_trace(err); /* Don't go through callbacks */ + } + + if (lock_func) + { + svn_lock_t *report_lock = NULL; + + if (locking && ctx->lock->token) + report_lock = ctx->lock; + + cb_err = lock_func(lock_baton, ctx->path, locking, + report_lock, err, ctx->pool); + } + svn_error_clear(err); + + SVN_ERR(cb_err); + + waittime_left = sess->timeout; + svn_sort__array_delete(lock_ctxs, i, 1); + i--; + + svn_pool_destroy(ctx->pool); + continue; + } + } + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +handle_lock(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + lock_ctx_t *ctx = handler_baton; + + if (!ctx->read_headers) + { + serf_bucket_t *headers; + const char *val; + + headers = serf_bucket_response_get_headers(response); + + val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER); + if (val) + { + ctx->lock->owner = apr_pstrdup(ctx->pool, val); + } + + val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER); + if (val) + { + SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val, + ctx->pool)); + } + + ctx->read_headers = TRUE; + } + + return ctx->inner_handler(request, response, ctx->inner_baton, pool); +} + +/* Implements svn_ra_serf__request_body_delegate_t */ +static svn_error_t * +create_lock_body(serf_bucket_t **body_bkt, + void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + lock_ctx_t *ctx = baton; + serf_bucket_t *buckets; + + buckets = serf_bucket_aggregate_create(alloc); + + svn_ra_serf__add_xml_header_buckets(buckets, alloc); + svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockinfo", + "xmlns", "DAV:", + SVN_VA_NULL); + + svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockscope", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, "exclusive", SVN_VA_NULL); + svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockscope"); + + svn_ra_serf__add_open_tag_buckets(buckets, alloc, "locktype", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, "write", SVN_VA_NULL); + svn_ra_serf__add_close_tag_buckets(buckets, alloc, "locktype"); + + if (ctx->lock->comment) + { + svn_ra_serf__add_tag_buckets(buckets, "owner", ctx->lock->comment, + alloc); + } + + svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockinfo"); + + *body_bkt = buckets; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__lock(svn_ra_session_t *ra_session, + apr_hash_t *path_revs, + const char *comment, + svn_boolean_t force, + svn_ra_lock_callback_t lock_func, + void *lock_baton, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + apr_array_header_t *lock_requests; + + lock_requests = apr_array_make(scratch_pool, apr_hash_count(path_revs), + sizeof(lock_ctx_t*)); + + /* ### Perhaps we should open more connections than just one? See update.c */ + + iterpool = svn_pool_create(scratch_pool); + + for (hi = apr_hash_first(scratch_pool, path_revs); + hi; + hi = apr_hash_next(hi)) + { + svn_ra_serf__handler_t *handler; + svn_ra_serf__xml_context_t *xmlctx; + const char *req_url; + lock_ctx_t *lock_ctx; + apr_pool_t *lock_pool; + + svn_pool_clear(iterpool); + + lock_pool = svn_pool_create(scratch_pool); + lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx)); + + lock_ctx->pool = lock_pool; + lock_ctx->path = apr_hash_this_key(hi); + lock_ctx->revision = *((svn_revnum_t*)apr_hash_this_val(hi)); + lock_ctx->lock = svn_lock_create(lock_pool); + lock_ctx->lock->path = lock_ctx->path; + lock_ctx->lock->comment = comment; + + lock_ctx->force = force; + req_url = svn_path_url_add_component2(session->session_url.path, + lock_ctx->path, lock_pool); + + xmlctx = svn_ra_serf__xml_context_create(locks_ttable, + NULL, locks_closed, NULL, + lock_ctx, + lock_pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, + lock_pool); + + handler->method = "LOCK"; + handler->path = req_url; + handler->body_type = "text/xml"; + + /* Same stupid algorithm from get_best_connection() in update.c */ + handler->conn = session->conns[session->cur_conn]; + session->cur_conn++; + + if (session->cur_conn >= session->num_conns) + session->cur_conn = 0; + + handler->header_delegate = set_lock_headers; + handler->header_delegate_baton = lock_ctx; + + handler->body_delegate = create_lock_body; + handler->body_delegate_baton = lock_ctx; + + lock_ctx->inner_handler = handler->response_handler; + lock_ctx->inner_baton = handler->response_baton; + handler->response_handler = handle_lock; + handler->response_baton = lock_ctx; + + handler->no_fail_on_http_failure_status = TRUE; + + lock_ctx->handler = handler; + + APR_ARRAY_PUSH(lock_requests, lock_ctx_t *) = lock_ctx; + + svn_ra_serf__request_create(handler); + } + + SVN_ERR(run_locks(session, lock_requests, TRUE, lock_func, lock_baton, + iterpool)); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +static svn_error_t * +set_unlock_headers(serf_bucket_t *headers, + void *baton, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) +{ + lock_ctx_t *ctx = baton; + + serf_bucket_headers_set(headers, "Lock-Token", ctx->token); + if (ctx->force) + { + serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, + SVN_DAV_OPTION_LOCK_BREAK); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__unlock(svn_ra_session_t *ra_session, + apr_hash_t *path_tokens, + svn_boolean_t force, + svn_ra_lock_callback_t lock_func, + void *lock_baton, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + apr_array_header_t *lock_requests; + + iterpool = svn_pool_create(scratch_pool); + + /* If we are stealing locks we need the lock tokens */ + if (force) + { + /* Theoretically this part can be improved (for performance) by using + svn_ra_get_locks() to obtain all the locks in a single request, but + do we really want to improve the performance of + $ svn unlock --force * + */ + + for (hi = apr_hash_first(scratch_pool, path_tokens); + hi; + hi = apr_hash_next(hi)) + { + const char *path; + const char *token; + svn_lock_t *existing_lock; + svn_error_t *err; + + svn_pool_clear(iterpool); + + path = apr_hash_this_key(hi); + token = apr_hash_this_val(hi); + + if (token && token[0]) + continue; + + if (session->cancel_func) + SVN_ERR(session->cancel_func(session->cancel_baton)); + + err = svn_ra_serf__get_lock(ra_session, &existing_lock, path, + iterpool); + + if (!err && existing_lock) + { + svn_hash_sets(path_tokens, path, + apr_pstrdup(scratch_pool, existing_lock->token)); + continue; + } + + err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, err, + _("'%s' is not locked in the repository"), + path); + + if (lock_func) + { + svn_error_t *err2; + err2 = lock_func(lock_baton, path, FALSE, NULL, err, iterpool); + svn_error_clear(err); + + SVN_ERR(err2); + } + else + { + svn_error_clear(err); + } + + svn_hash_sets(path_tokens, path, NULL); + } + } + + /* ### Perhaps we should open more connections than just one? See update.c */ + + lock_requests = apr_array_make(scratch_pool, apr_hash_count(path_tokens), + sizeof(lock_ctx_t*)); + + for (hi = apr_hash_first(scratch_pool, path_tokens); + hi; + hi = apr_hash_next(hi)) + { + svn_ra_serf__handler_t *handler; + const char *req_url, *token; + lock_ctx_t *lock_ctx; + apr_pool_t *lock_pool; + + svn_pool_clear(iterpool); + + lock_pool = svn_pool_create(scratch_pool); + lock_ctx = apr_pcalloc(lock_pool, sizeof(*lock_ctx)); + + lock_ctx->pool = lock_pool; + + lock_ctx->path = apr_hash_this_key(hi); + token = apr_hash_this_val(hi); + + lock_ctx->force = force; + lock_ctx->token = apr_pstrcat(lock_pool, "<", token, ">", SVN_VA_NULL); + + req_url = svn_path_url_add_component2(session->session_url.path, lock_ctx->path, + lock_pool); + + handler = svn_ra_serf__create_handler(session, lock_pool); + + handler->method = "UNLOCK"; + handler->path = req_url; + + handler->header_delegate = set_unlock_headers; + handler->header_delegate_baton = lock_ctx; + + handler->response_handler = svn_ra_serf__expect_empty_body; + handler->response_baton = handler; + + handler->no_fail_on_http_failure_status = TRUE; + + lock_ctx->handler = handler; + + APR_ARRAY_PUSH(lock_requests, lock_ctx_t *) = lock_ctx; + + svn_ra_serf__request_create(handler); + } + + SVN_ERR(run_locks(session, lock_requests, FALSE, lock_func, lock_baton, + iterpool)); + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/locks.c b/contrib/subversion/subversion/libsvn_ra_serf/locks.c deleted file mode 100644 index 252c30182..000000000 --- a/contrib/subversion/subversion/libsvn_ra_serf/locks.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * locks.c : entry point for locking 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 -#include - -#include "svn_dav.h" -#include "svn_pools.h" -#include "svn_ra.h" - -#include "../libsvn_ra/ra_loader.h" -#include "svn_config.h" -#include "svn_path.h" -#include "svn_time.h" -#include "svn_private_config.h" - -#include "ra_serf.h" - - -/* - * This enum represents the current state of our XML parsing for a REPORT. - */ -enum { - INITIAL = 0, - MULTISTATUS, - RESPONSE, - PROPSTAT, - PROP, - LOCK_DISCOVERY, - ACTIVE_LOCK, - LOCK_TYPE, - LOCK_SCOPE, - DEPTH, - TIMEOUT, - LOCK_TOKEN, - OWNER, - HREF -}; - -typedef struct lock_info_t { - apr_pool_t *pool; - - const char *path; - - svn_lock_t *lock; - - svn_boolean_t force; - svn_revnum_t revision; - - svn_boolean_t read_headers; - - svn_ra_serf__handler_t *handler; - - /* The expat handler. We wrap this to do a bit more work. */ - svn_ra_serf__response_handler_t inner_handler; - void *inner_baton; - -} lock_info_t; - -#define D_ "DAV:" -#define S_ SVN_XML_NAMESPACE -static const svn_ra_serf__xml_transition_t locks_ttable[] = { - /* The INITIAL state can transition into D:prop (LOCK) or - to D:multistatus (PROPFIND) */ - { INITIAL, D_, "prop", PROP, - FALSE, { NULL }, FALSE }, - { INITIAL, D_, "multistatus", MULTISTATUS, - FALSE, { NULL }, FALSE }, - - { MULTISTATUS, D_, "response", RESPONSE, - FALSE, { NULL }, FALSE }, - - { RESPONSE, D_, "propstat", PROPSTAT, - FALSE, { NULL }, FALSE }, - - { PROPSTAT, D_, "prop", PROP, - FALSE, { NULL }, FALSE }, - - { PROP, D_, "lockdiscovery", LOCK_DISCOVERY, - FALSE, { NULL }, FALSE }, - - { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK, - FALSE, { NULL }, FALSE }, - -#if 0 - /* ### we don't really need to parse locktype/lockscope. we know what - ### the values are going to be. we *could* validate that the only - ### possible children are D:write and D:exclusive. we'd need to - ### modify the state transition to tell us about all children - ### (ie. maybe support "*" for the name) and then validate. but it - ### just isn't important to validate, so disable this for now... */ - - { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE, - FALSE, { NULL }, FALSE }, - - { LOCK_TYPE, D_, "write", WRITE, - FALSE, { NULL }, TRUE }, - - { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE, - FALSE, { NULL }, FALSE }, - - { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE, - FALSE, { NULL }, TRUE }, -#endif /* 0 */ - - { ACTIVE_LOCK, D_, "timeout", TIMEOUT, - TRUE, { NULL }, TRUE }, - - { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN, - FALSE, { NULL }, FALSE }, - - { LOCK_TOKEN, D_, "href", HREF, - TRUE, { NULL }, TRUE }, - - { ACTIVE_LOCK, D_, "owner", OWNER, - TRUE, { NULL }, TRUE }, - - /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */ - - { 0 } -}; - - -/* Conforms to svn_ra_serf__xml_closed_t */ -static svn_error_t * -locks_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) -{ - lock_info_t *lock_ctx = baton; - - if (leaving_state == TIMEOUT) - { - if (strcasecmp(cdata->data, "Infinite") == 0) - lock_ctx->lock->expiration_date = 0; - else if (strncasecmp(cdata->data, "Second-", 7) == 0) - { - unsigned n; - SVN_ERR(svn_cstring_atoui(&n, cdata->data+7)); - - lock_ctx->lock->expiration_date = apr_time_now() + - apr_time_from_sec(n); - } - else - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Invalid LOCK timeout value '%s'"), - cdata->data); - } - else if (leaving_state == HREF) - { - if (cdata->len) - { - char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len); - - apr_collapse_spaces(buf, buf); - lock_ctx->lock->token = buf; - } - } - else if (leaving_state == OWNER) - { - if (cdata->len) - { - lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool, - cdata->data, cdata->len); - } - } - - return SVN_NO_ERROR; -} - - -static svn_error_t * -set_lock_headers(serf_bucket_t *headers, - void *baton, - apr_pool_t *pool) -{ - lock_info_t *lock_ctx = baton; - - if (lock_ctx->force) - { - serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, - SVN_DAV_OPTION_LOCK_STEAL); - } - - if (SVN_IS_VALID_REVNUM(lock_ctx->revision)) - { - serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER, - apr_ltoa(pool, lock_ctx->revision)); - } - - return APR_SUCCESS; -} - - -/* Register an error within the session. If something is already there, - then it will take precedence. */ -static svn_error_t * -determine_error(svn_ra_serf__handler_t *handler, - svn_error_t *err) -{ - { - apr_status_t errcode; - - if (handler->sline.code == 423) - errcode = SVN_ERR_FS_PATH_ALREADY_LOCKED; - else if (handler->sline.code == 403) - errcode = SVN_ERR_RA_DAV_FORBIDDEN; - else - return err; - - /* Client-side or server-side error already. Return it. */ - if (err != NULL) - return err; - - /* The server did not send us a detailed human-readable error. - Provide a generic error. */ - err = svn_error_createf(errcode, NULL, - _("Lock request failed: %d %s"), - handler->sline.code, - handler->sline.reason); - } - - return err; -} - - -/* Implements svn_ra_serf__response_handler_t */ -static svn_error_t * -handle_lock(serf_request_t *request, - serf_bucket_t *response, - void *handler_baton, - apr_pool_t *pool) -{ - lock_info_t *ctx = handler_baton; - - /* 403 (Forbidden) when a lock doesn't exist. - 423 (Locked) when a lock already exists. */ - if (ctx->handler->sline.code == 403 - || ctx->handler->sline.code == 423) - { - /* Go look in the body for a server-provided error. This will - reset flags for the core handler to Do The Right Thing. We - won't be back to this handler again. */ - return svn_error_trace(svn_ra_serf__expect_empty_body( - request, response, ctx->handler, pool)); - } - - if (!ctx->read_headers) - { - serf_bucket_t *headers; - const char *val; - - headers = serf_bucket_response_get_headers(response); - - val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER); - if (val) - { - ctx->lock->owner = apr_pstrdup(ctx->pool, val); - } - - val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER); - if (val) - { - SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val, - ctx->pool)); - } - - ctx->read_headers = TRUE; - } - - return ctx->inner_handler(request, response, ctx->inner_baton, pool); -} - -/* Implements svn_ra_serf__request_body_delegate_t */ -static svn_error_t * -create_getlock_body(serf_bucket_t **body_bkt, - void *baton, - serf_bucket_alloc_t *alloc, - apr_pool_t *pool) -{ - serf_bucket_t *buckets; - - buckets = serf_bucket_aggregate_create(alloc); - - svn_ra_serf__add_xml_header_buckets(buckets, alloc); - svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind", - "xmlns", "DAV:", - NULL); - svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", NULL); - svn_ra_serf__add_tag_buckets(buckets, "lockdiscovery", NULL, alloc); - svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop"); - svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind"); - - *body_bkt = buckets; - return SVN_NO_ERROR; -} - -static svn_error_t* -setup_getlock_headers(serf_bucket_t *headers, - void *baton, - apr_pool_t *pool) -{ - serf_bucket_headers_setn(headers, "Depth", "0"); - - return SVN_NO_ERROR; -} - -/* Implements svn_ra_serf__request_body_delegate_t */ -static svn_error_t * -create_lock_body(serf_bucket_t **body_bkt, - void *baton, - serf_bucket_alloc_t *alloc, - apr_pool_t *pool) -{ - lock_info_t *ctx = baton; - serf_bucket_t *buckets; - - buckets = serf_bucket_aggregate_create(alloc); - - svn_ra_serf__add_xml_header_buckets(buckets, alloc); - svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockinfo", - "xmlns", "DAV:", - NULL); - - svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockscope", NULL); - svn_ra_serf__add_tag_buckets(buckets, "exclusive", NULL, alloc); - svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockscope"); - - svn_ra_serf__add_open_tag_buckets(buckets, alloc, "locktype", NULL); - svn_ra_serf__add_tag_buckets(buckets, "write", NULL, alloc); - svn_ra_serf__add_close_tag_buckets(buckets, alloc, "locktype"); - - if (ctx->lock->comment) - { - svn_ra_serf__add_tag_buckets(buckets, "owner", ctx->lock->comment, - alloc); - } - - svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockinfo"); - - *body_bkt = buckets; - return SVN_NO_ERROR; -} - -svn_error_t * -svn_ra_serf__get_lock(svn_ra_session_t *ra_session, - svn_lock_t **lock, - const char *path, - apr_pool_t *result_pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_context_t *xmlctx; - apr_pool_t *scratch_pool = svn_pool_create(result_pool); - lock_info_t *lock_ctx; - const char *req_url; - svn_error_t *err; - - req_url = svn_path_url_add_component2(session->session_url.path, path, - scratch_pool); - - lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx)); - lock_ctx->pool = result_pool; - lock_ctx->path = req_url; - lock_ctx->lock = svn_lock_create(result_pool); - lock_ctx->lock->path = apr_pstrdup(result_pool, path); - - xmlctx = svn_ra_serf__xml_context_create(locks_ttable, - NULL, locks_closed, NULL, - lock_ctx, - scratch_pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool); - - handler->method = "PROPFIND"; - handler->path = req_url; - handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; - - handler->body_delegate = create_getlock_body; - handler->body_delegate_baton = lock_ctx; - - handler->header_delegate = setup_getlock_headers; - handler->header_delegate_baton = lock_ctx; - - lock_ctx->inner_handler = handler->response_handler; - lock_ctx->inner_baton = handler->response_baton; - handler->response_handler = handle_lock; - handler->response_baton = lock_ctx; - - lock_ctx->handler = handler; - - err = svn_ra_serf__context_run_one(handler, scratch_pool); - err = determine_error(handler, err); - - if (handler->sline.code == 404) - { - return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, err, - _("Malformed URL for repository")); - } - if (err) - { - /* TODO Shh. We're telling a white lie for now. */ - return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, - _("Server does not support locking features")); - } - - if (lock_ctx->lock && lock_ctx->lock->token) - *lock = lock_ctx->lock; - else - *lock = NULL; - - svn_pool_destroy(scratch_pool); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_ra_serf__lock(svn_ra_session_t *ra_session, - apr_hash_t *path_revs, - const char *comment, - svn_boolean_t force, - svn_ra_lock_callback_t lock_func, - void *lock_baton, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_index_t *hi; - apr_pool_t *iterpool; - - iterpool = svn_pool_create(scratch_pool); - - /* ### TODO for issue 2263: Send all the locks over the wire at once. This - ### loop is just a temporary shim. - ### an alternative, which is backwards-compat with all servers is to - ### pipeline these requests. ie. stop using run_wait/run_one. */ - - for (hi = apr_hash_first(scratch_pool, path_revs); - hi; - hi = apr_hash_next(hi)) - { - svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_context_t *xmlctx; - const char *req_url; - lock_info_t *lock_ctx; - svn_error_t *err; - svn_error_t *new_err = NULL; - - svn_pool_clear(iterpool); - - lock_ctx = apr_pcalloc(iterpool, sizeof(*lock_ctx)); - - lock_ctx->pool = iterpool; - lock_ctx->path = svn__apr_hash_index_key(hi); - lock_ctx->revision = *((svn_revnum_t*)svn__apr_hash_index_val(hi)); - lock_ctx->lock = svn_lock_create(iterpool); - lock_ctx->lock->path = lock_ctx->path; - lock_ctx->lock->comment = comment; - - lock_ctx->force = force; - req_url = svn_path_url_add_component2(session->session_url.path, - lock_ctx->path, iterpool); - - xmlctx = svn_ra_serf__xml_context_create(locks_ttable, - NULL, locks_closed, NULL, - lock_ctx, - iterpool); - handler = svn_ra_serf__create_expat_handler(xmlctx, iterpool); - - handler->method = "LOCK"; - handler->path = req_url; - handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; - - handler->header_delegate = set_lock_headers; - handler->header_delegate_baton = lock_ctx; - - handler->body_delegate = create_lock_body; - handler->body_delegate_baton = lock_ctx; - - lock_ctx->inner_handler = handler->response_handler; - lock_ctx->inner_baton = handler->response_baton; - handler->response_handler = handle_lock; - handler->response_baton = lock_ctx; - - lock_ctx->handler = handler; - - err = svn_ra_serf__context_run_one(handler, iterpool); - err = determine_error(handler, err); - - if (lock_func) - new_err = lock_func(lock_baton, lock_ctx->path, TRUE, lock_ctx->lock, - err, iterpool); - svn_error_clear(err); - - SVN_ERR(new_err); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - -struct unlock_context_t { - const char *token; - svn_boolean_t force; -}; - -static svn_error_t * -set_unlock_headers(serf_bucket_t *headers, - void *baton, - apr_pool_t *pool) -{ - struct unlock_context_t *ctx = baton; - - serf_bucket_headers_set(headers, "Lock-Token", ctx->token); - if (ctx->force) - { - serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, - SVN_DAV_OPTION_LOCK_BREAK); - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_ra_serf__unlock(svn_ra_session_t *ra_session, - apr_hash_t *path_tokens, - svn_boolean_t force, - svn_ra_lock_callback_t lock_func, - void *lock_baton, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_index_t *hi; - apr_pool_t *iterpool; - - iterpool = svn_pool_create(scratch_pool); - - /* ### TODO for issue 2263: Send all the locks over the wire at once. This - ### loop is just a temporary shim. - ### an alternative, which is backwards-compat with all servers is to - ### pipeline these requests. ie. stop using run_wait/run_one. */ - - for (hi = apr_hash_first(scratch_pool, path_tokens); - hi; - hi = apr_hash_next(hi)) - { - svn_ra_serf__handler_t *handler; - const char *req_url, *path, *token; - svn_lock_t *existing_lock = NULL; - struct unlock_context_t unlock_ctx; - svn_error_t *err = NULL; - svn_error_t *new_err = NULL; - - - svn_pool_clear(iterpool); - - path = svn__apr_hash_index_key(hi); - token = svn__apr_hash_index_val(hi); - - if (force && (!token || token[0] == '\0')) - { - SVN_ERR(svn_ra_serf__get_lock(ra_session, &existing_lock, path, - iterpool)); - token = existing_lock ? existing_lock->token : NULL; - if (!token) - { - err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, NULL, - _("'%s' is not locked in the repository"), - path); - - if (lock_func) - { - svn_error_t *err2; - err2 = lock_func(lock_baton, path, FALSE, NULL, err, - iterpool); - svn_error_clear(err); - err = NULL; - if (err2) - return svn_error_trace(err2); - } - else - { - svn_error_clear(err); - err = NULL; - } - continue; - } - } - - unlock_ctx.force = force; - unlock_ctx.token = apr_pstrcat(iterpool, "<", token, ">", (char *)NULL); - - req_url = svn_path_url_add_component2(session->session_url.path, path, - iterpool); - - handler = apr_pcalloc(iterpool, sizeof(*handler)); - - handler->handler_pool = iterpool; - handler->method = "UNLOCK"; - handler->path = req_url; - handler->conn = session->conns[0]; - handler->session = session; - - handler->header_delegate = set_unlock_headers; - handler->header_delegate_baton = &unlock_ctx; - - handler->response_handler = svn_ra_serf__expect_empty_body; - handler->response_baton = handler; - - SVN_ERR(svn_ra_serf__context_run_one(handler, iterpool)); - - switch (handler->sline.code) - { - case 204: - break; /* OK */ - case 403: - /* Api users expect this specific error code to detect failures */ - err = svn_error_createf(SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL, - _("Unlock request failed: %d %s"), - handler->sline.code, - handler->sline.reason); - break; - default: - err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("Unlock request failed: %d %s"), - handler->sline.code, - handler->sline.reason); - } - - if (lock_func) - new_err = lock_func(lock_baton, path, FALSE, existing_lock, err, - iterpool); - - svn_error_clear(err); - SVN_ERR(new_err); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/log.c b/contrib/subversion/subversion/libsvn_ra_serf/log.c index 02f2f2995..773ae5c9c 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/log.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/log.c @@ -22,7 +22,8 @@ */ - + + #include #include @@ -44,12 +45,13 @@ #include "ra_serf.h" #include "../libsvn_ra/ra_loader.h" - + + /* * This enum represents the current state of our XML parsing for a REPORT. */ -enum { - INITIAL = 0, +enum log_state_e { + INITIAL = XML_STATE_INITIAL, REPORT, ITEM, VERSION, @@ -145,7 +147,8 @@ static const svn_ra_serf__xml_transition_t log_ttable[] = { { 0 } }; - + + /* Store CDATA into REVPROPS, associated with PROPNAME. If ENCODING is not NULL, then it must base "base64" and CDATA will be decoded first. @@ -207,12 +210,14 @@ collect_path(apr_hash_t *paths, copyfrom_rev = svn_hash_gets(attrs, "copyfrom-rev"); if (copyfrom_path && copyfrom_rev) { - svn_revnum_t rev = SVN_STR_TO_REV(copyfrom_rev); + apr_int64_t rev; + + SVN_ERR(svn_cstring_atoi64(&rev, copyfrom_rev)); - if (SVN_IS_VALID_REVNUM(rev)) + if (SVN_IS_VALID_REVNUM((svn_revnum_t)rev)) { lcp->copyfrom_path = apr_pstrdup(result_pool, copyfrom_path); - lcp->copyfrom_rev = rev; + lcp->copyfrom_rev = (svn_revnum_t)rev; } } @@ -296,7 +301,12 @@ log_closed(svn_ra_serf__xml_estate_t *xes, rev_str = svn_hash_gets(attrs, "revision"); if (rev_str) - log_entry->revision = SVN_STR_TO_REV(rev_str); + { + apr_int64_t rev; + + SVN_ERR(svn_cstring_atoi64(&rev, rev_str)); + log_entry->revision = (svn_revnum_t)rev; + } else log_entry->revision = SVN_INVALID_REVNUM; @@ -397,12 +407,13 @@ log_closed(svn_ra_serf__xml_estate_t *xes, return SVN_NO_ERROR; } - +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_log_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *buckets; log_context_t *log_ctx = baton; @@ -412,7 +423,7 @@ create_log_body(serf_bucket_t **body_bkt, svn_ra_serf__add_open_tag_buckets(buckets, alloc, "S:log-report", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(buckets, "S:start-revision", @@ -432,23 +443,22 @@ create_log_body(serf_bucket_t **body_bkt, if (log_ctx->changed_paths) { - svn_ra_serf__add_tag_buckets(buckets, - "S:discover-changed-paths", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:discover-changed-paths", + SVN_VA_NULL); } if (log_ctx->strict_node_history) { - svn_ra_serf__add_tag_buckets(buckets, - "S:strict-node-history", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:strict-node-history", SVN_VA_NULL); } if (log_ctx->include_merged_revisions) { - svn_ra_serf__add_tag_buckets(buckets, - "S:include-merged-revisions", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:include-merged-revisions", + SVN_VA_NULL); } if (log_ctx->revprops) @@ -463,16 +473,14 @@ create_log_body(serf_bucket_t **body_bkt, } if (log_ctx->revprops->nelts == 0) { - svn_ra_serf__add_tag_buckets(buckets, - "S:no-revprops", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:no-revprops", SVN_VA_NULL); } } else { - svn_ra_serf__add_tag_buckets(buckets, - "S:all-revprops", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:all-revprops", SVN_VA_NULL); } if (log_ctx->paths) @@ -487,9 +495,8 @@ create_log_body(serf_bucket_t **body_bkt, } } - svn_ra_serf__add_tag_buckets(buckets, - "S:encode-binary-props", NULL, - alloc); + svn_ra_serf__add_empty_tag_buckets(buckets, alloc, + "S:encode-binary-props", SVN_VA_NULL); svn_ra_serf__add_close_tag_buckets(buckets, alloc, "S:log-report"); @@ -518,7 +525,6 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session, svn_ra_serf__xml_context_t *xmlctx; svn_boolean_t want_custom_revprops; svn_revnum_t peg_rev; - svn_error_t *err; const char *req_url; log_ctx = apr_pcalloc(pool, sizeof(*log_ctx)); @@ -574,7 +580,7 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session, peg_rev = (start == SVN_INVALID_REVNUM || start > end) ? start : end; SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, - session, NULL /* conn */, + session, NULL /* url */, peg_rev, pool, pool)); @@ -582,23 +588,18 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session, log_opened, log_closed, NULL, log_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = req_url; handler->body_delegate = create_log_body; handler->body_delegate_baton = log_ctx; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; - err = svn_ra_serf__context_run_one(handler, pool); + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - SVN_ERR(svn_error_compose_create( + return svn_error_trace( svn_ra_serf__error_on_status(handler->sline, req_url, - handler->location), - err)); - - return SVN_NO_ERROR; + handler->location)); } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/merge.c b/contrib/subversion/subversion/libsvn_ra_serf/merge.c index 670e4219f..0a2fd5465 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/merge.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/merge.c @@ -48,7 +48,7 @@ * This enum represents the current state of our XML parsing for a MERGE. */ typedef enum merge_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, MERGE_RESPONSE, UPDATED_SET, RESPONSE, @@ -65,7 +65,7 @@ typedef enum merge_state_e { AUTHOR, POST_COMMIT_ERR, - PROP_VAL + STATUS } merge_state_e; @@ -171,7 +171,12 @@ merge_closed(svn_ra_serf__xml_estate_t *xes, rev_str = svn_hash_gets(attrs, "revision"); if (rev_str) - merge_ctx->commit_info->revision = SVN_STR_TO_REV(rev_str); + { + apr_int64_t rev; + + SVN_ERR(svn_cstring_atoi64(&rev, rev_str)); + merge_ctx->commit_info->revision = (svn_revnum_t)rev; + } else merge_ctx->commit_info->revision = SVN_INVALID_REVNUM; @@ -266,7 +271,8 @@ merge_closed(svn_ra_serf__xml_estate_t *xes, static svn_error_t * setup_merge_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { merge_context_t *ctx = baton; @@ -294,7 +300,7 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock-token-list", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); for (hi = apr_hash_first(pool, lock_tokens); hi; @@ -313,9 +319,9 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, if (parent && !svn_relpath_skip_ancestor(parent, key)) continue; - svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", NULL); + svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", SVN_VA_NULL); - svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", NULL); + svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", SVN_VA_NULL); svn_ra_serf__add_cdata_len_buckets(body, alloc, path.data, path.len); svn_ra_serf__add_close_tag_buckets(body, alloc, "lock-path"); @@ -327,11 +333,13 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock-token-list"); } +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t* create_merge_body(serf_bucket_t **bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { merge_context_t *ctx = baton; serf_bucket_t *body_bkt; @@ -341,9 +349,9 @@ create_merge_body(serf_bucket_t **bkt, svn_ra_serf__add_xml_header_buckets(body_bkt, alloc); svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:merge", "xmlns:D", "DAV:", - NULL); - svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", NULL); - svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL); + SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", SVN_VA_NULL); + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", SVN_VA_NULL); svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc, ctx->merge_resource_url, @@ -352,19 +360,26 @@ create_merge_body(serf_bucket_t **bkt, svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href"); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:source"); - svn_ra_serf__add_tag_buckets(body_bkt, "D:no-auto-merge", NULL, alloc); - svn_ra_serf__add_tag_buckets(body_bkt, "D:no-checkout", NULL, alloc); - - svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL); - svn_ra_serf__add_tag_buckets(body_bkt, "D:checked-in", NULL, alloc); - svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__VERSION_NAME, NULL, alloc); - svn_ra_serf__add_tag_buckets(body_bkt, "D:resourcetype", NULL, alloc); - svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__CREATIONDATE, NULL, alloc); - svn_ra_serf__add_tag_buckets(body_bkt, "D:creator-displayname", NULL, alloc); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:no-auto-merge", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:no-checkout", SVN_VA_NULL); + + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:checked-in", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:" SVN_DAV__VERSION_NAME, SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:resourcetype", SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:" SVN_DAV__CREATIONDATE, SVN_VA_NULL); + svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc, + "D:creator-displayname", SVN_VA_NULL); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop"); - svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt, alloc, - pool); + svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt, + alloc, pool); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:merge"); @@ -376,9 +391,7 @@ create_merge_body(serf_bucket_t **bkt, svn_error_t * svn_ra_serf__run_merge(const svn_commit_info_t **commit_info, - int *response_code, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, const char *merge_resource_url, apr_hash_t *lock_tokens, svn_boolean_t keep_locks, @@ -407,14 +420,14 @@ svn_ra_serf__run_merge(const svn_commit_info_t **commit_info, NULL, merge_closed, NULL, merge_ctx, scratch_pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, + scratch_pool); handler->method = "MERGE"; handler->path = merge_ctx->merge_url; handler->body_delegate = create_merge_body; handler->body_delegate_baton = merge_ctx; - handler->conn = conn; - handler->session = session; + handler->body_type = "text/xml"; handler->header_delegate = setup_merge_headers; handler->header_delegate_baton = merge_ctx; @@ -423,8 +436,21 @@ svn_ra_serf__run_merge(const svn_commit_info_t **commit_info, SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + *commit_info = merge_ctx->commit_info; - *response_code = handler->sline.code; + + /* Sanity check (Reported to be triggered by CodePlex's svnbridge) */ + if (! SVN_IS_VALID_REVNUM(merge_ctx->commit_info->revision)) + { + return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, + _("The MERGE response did not include " + "a new revision")); + } + + merge_ctx->commit_info->repos_root = apr_pstrdup(result_pool, + session->repos_root_str); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_serf/mergeinfo.c b/contrib/subversion/subversion/libsvn_ra_serf/mergeinfo.c index bd4fcbab5..589ff0768 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/mergeinfo.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/mergeinfo.c @@ -41,7 +41,7 @@ /* The current state of our XML parsing. */ typedef enum mergeinfo_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, MERGEINFO_REPORT, MERGEINFO_ITEM, MERGEINFO_PATH, @@ -130,12 +130,13 @@ mergeinfo_closed(svn_ra_serf__xml_estate_t *xes, return SVN_NO_ERROR; } - +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_mergeinfo_body(serf_bucket_t **bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { mergeinfo_context_t *mergeinfo_ctx = baton; serf_bucket_t *body_bkt; @@ -145,7 +146,7 @@ create_mergeinfo_body(serf_bucket_t **bkt, svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "S:" SVN_DAV__MERGEINFO_REPORT, "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__REVISION, @@ -191,7 +192,6 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session, svn_boolean_t include_descendants, apr_pool_t *pool) { - svn_error_t *err; mergeinfo_context_t *mergeinfo_ctx; svn_ra_serf__session_t *session = ra_session->priv; svn_ra_serf__handler_t *handler; @@ -201,7 +201,7 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session, *catalog = NULL; SVN_ERR(svn_ra_serf__get_stable_url(&path, NULL /* latest_revnum */, - session, NULL /* conn */, + session, NULL /* url */, revision, pool, pool)); @@ -217,24 +217,21 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session, NULL, mergeinfo_closed, NULL, mergeinfo_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool); handler->method = "REPORT"; handler->path = path; - handler->conn = session->conns[0]; - handler->session = session; + handler->body_delegate = create_mergeinfo_body; handler->body_delegate_baton = mergeinfo_ctx; handler->body_type = "text/xml"; - err = svn_ra_serf__context_run_one(handler, pool); + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); - SVN_ERR(svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, handler->path, - handler->location), - err)); + SVN_ERR(svn_ra_serf__error_on_status(handler->sline, handler->path, + handler->location)); - if (handler->done && apr_hash_count(mergeinfo_ctx->result_catalog)) + if (apr_hash_count(mergeinfo_ctx->result_catalog)) *catalog = mergeinfo_ctx->result_catalog; return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_ra_serf/multistatus.c b/contrib/subversion/subversion/libsvn_ra_serf/multistatus.c new file mode 100644 index 000000000..9c269c39b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/multistatus.c @@ -0,0 +1,750 @@ +/* + * multistatus.c : parse multistatus (error) responses. + * + * ==================================================================== + * 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 + +#include + +#include +#include + +#include "svn_private_config.h" +#include "svn_hash.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_string.h" +#include "svn_xml.h" +#include "svn_props.h" +#include "svn_dirent_uri.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_fspath.h" + +#include "ra_serf.h" + +/* The current state of our XML parsing. */ +typedef enum iprops_state_e { + INITIAL = XML_STATE_INITIAL, + MS_MULTISTATUS, + + MS_RESPONSE, + MS_RESPONSE_HREF, + + MS_PROPSTAT, + MS_PROPSTAT_PROP, + MS_PROPSTAT_PROP_NAME, + MS_PROPSTAT_STATUS, + MS_PROPSTAT_RESPONSEDESCRIPTION, + MS_PROPSTAT_ERROR, + MS_PROPSTAT_ERROR_HUMANREADABLE, + + MS_RESPONSE_STATUS, + MS_RESPONSE_RESPONSEDESCRIPTION, + MS_RESPONSE_ERROR, + MS_RESPONSE_ERROR_HUMANREADABLE, + + MS_MULTISTATUS_RESPONSEDESCRIPTION, + + D_ERROR, + S_ERROR, + M_ERROR_HUMANREADABLE +} iprops_state_e; + +/* + + + http://something + + HTTP/1.1 500 failed + + + Some Subversion error + + + + Human readable description + + http://redirected + + ... + + + Or for property operations: + + + + http://somewhere-else + + + HTTP/1.1 499 failed + + + Some Subversion error + + + + Human readable description + + + HTTP/1.1 499 failed + + + Some Subversion error + + + + Human readable description + + http://redirected + + Global description + + + + Or on request failures + + + + Some Subversion error + + + */ + +#define D_ "DAV:" +#define S_ SVN_XML_NAMESPACE +#define M_ "http://apache.org/dav/xmlns" +static const svn_ra_serf__xml_transition_t multistatus_ttable[] = { + { INITIAL, D_, "multistatus", MS_MULTISTATUS, + FALSE, { NULL }, FALSE }, + + { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION, + TRUE, { NULL }, TRUE }, + + /* */ + { MS_MULTISTATUS, D_, "response", MS_RESPONSE, + FALSE, { NULL }, TRUE }, + + { MS_RESPONSE, D_, "href", MS_RESPONSE_HREF, + TRUE, { NULL }, TRUE }, + + /* */ + { MS_RESPONSE, D_, "propstat", MS_PROPSTAT, + FALSE, { NULL }, TRUE }, + + { MS_PROPSTAT, D_, "prop", MS_PROPSTAT_PROP, + FALSE, { NULL }, FALSE }, + + { MS_PROPSTAT_PROP, "", "*", MS_PROPSTAT_PROP_NAME, + FALSE, { NULL }, FALSE }, + + { MS_PROPSTAT, D_, "status", MS_PROPSTAT_STATUS, + TRUE, { NULL }, TRUE }, + + { MS_PROPSTAT, D_, "responsedescription", MS_PROPSTAT_RESPONSEDESCRIPTION, + TRUE, { NULL }, TRUE }, + + { MS_PROPSTAT, D_, "error", MS_PROPSTAT_ERROR, + FALSE, { NULL }, FALSE }, + + { MS_PROPSTAT_ERROR, M_, "human-readable", MS_PROPSTAT_ERROR_HUMANREADABLE, + TRUE, { "?errcode", NULL }, TRUE }, + /* */ + + + { MS_RESPONSE, D_, "status", MS_RESPONSE_STATUS, + TRUE, { NULL }, TRUE }, + + { MS_RESPONSE, D_, "responsedescription", MS_RESPONSE_RESPONSEDESCRIPTION, + TRUE, { NULL }, TRUE }, + + { MS_RESPONSE, D_, "error", MS_RESPONSE_ERROR, + FALSE, { NULL }, TRUE }, + + { MS_RESPONSE_ERROR, M_, "human-readable", MS_RESPONSE_ERROR_HUMANREADABLE, + TRUE, { "?errcode", NULL }, TRUE }, + + /* */ + + { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION, + TRUE, { NULL }, TRUE }, + + + { INITIAL, D_, "error", D_ERROR, + FALSE, { NULL }, TRUE }, + + { D_ERROR, S_, "error", S_ERROR, + FALSE, { NULL }, FALSE }, + + { D_ERROR, M_, "human-readable", M_ERROR_HUMANREADABLE, + TRUE, { "?errcode", NULL }, TRUE }, + + { 0 } +}; + +/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric + status code into *STATUS_CODE_OUT. Ignores leading whitespace. */ +static svn_error_t * +parse_status_line(int *status_code_out, + const char **reason, + const char *status_line, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + const char *token; + char *tok_status; + svn_stringbuf_t *temp_buf = svn_stringbuf_create(status_line, scratch_pool); + + svn_stringbuf_strip_whitespace(temp_buf); + token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status); + if (token) + token = apr_strtok(NULL, " \t\r\n", &tok_status); + if (!token) + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("Malformed DAV:status '%s'"), + status_line); + err = svn_cstring_atoi(status_code_out, token); + if (err) + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err, + _("Malformed DAV:status '%s'"), + status_line); + + token = apr_strtok(NULL, " \t\r\n", &tok_status); + + *reason = apr_pstrdup(result_pool, token); + + return SVN_NO_ERROR; +} + + +typedef struct error_item_t +{ + const char *path; + const char *propname; + + int http_status; + const char *http_reason; + apr_status_t apr_err; + + const char *message; +} error_item_t; + +static svn_error_t * +multistatus_opened(svn_ra_serf__xml_estate_t *xes, + void *baton, + int entered_state, + const svn_ra_serf__dav_props_t *tag, + apr_pool_t *scratch_pool) +{ + /*struct svn_ra_serf__server_error_t *server_error = baton;*/ + const char *propname; + + switch (entered_state) + { + case MS_PROPSTAT_PROP_NAME: + if (strcmp(tag->xmlns, SVN_DAV_PROP_NS_SVN) == 0) + propname = apr_pstrcat(scratch_pool, SVN_PROP_PREFIX, tag->name, + SVN_VA_NULL); + else + propname = tag->name; + svn_ra_serf__xml_note(xes, MS_PROPSTAT, "propname", propname); + break; + case S_ERROR: + /* This toggles an has error boolean in libsvn_ra_neon in 1.7 */ + break; + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +multistatus_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) +{ + struct svn_ra_serf__server_error_t *server_error = baton; + const char *errcode; + const char *status; + + switch (leaving_state) + { + case MS_RESPONSE_HREF: + { + apr_status_t result; + apr_uri_t uri; + + result = apr_uri_parse(scratch_pool, cdata->data, &uri); + if (result) + return svn_ra_serf__wrap_err(result, NULL); + svn_ra_serf__xml_note(xes, MS_RESPONSE, "path", + svn_urlpath__canonicalize(uri.path, scratch_pool)); + } + break; + case MS_RESPONSE_STATUS: + svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data); + break; + case MS_RESPONSE_ERROR_HUMANREADABLE: + svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data); + errcode = svn_hash_gets(attrs, "errcode"); + if (errcode) + svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode); + break; + case MS_RESPONSE: + if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL) + { + error_item_t *item; + + item = apr_pcalloc(server_error->pool, sizeof(*item)); + + item->path = apr_pstrdup(server_error->pool, + svn_hash_gets(attrs, "path")); + + SVN_ERR(parse_status_line(&item->http_status, + &item->http_reason, + status, + server_error->pool, + scratch_pool)); + + /* Do we have a mod_dav specific message? */ + item->message = svn_hash_gets(attrs, "human-readable"); + + if (item->message) + { + if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL) + { + apr_int64_t val; + + SVN_ERR(svn_cstring_atoi64(&val, errcode)); + item->apr_err = (apr_status_t)val; + } + + item->message = apr_pstrdup(server_error->pool, item->message); + } + else + item->message = apr_pstrdup(server_error->pool, + svn_hash_gets(attrs, "description")); + + APR_ARRAY_PUSH(server_error->items, error_item_t *) = item; + } + break; + + + case MS_PROPSTAT_STATUS: + svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data); + break; + case MS_PROPSTAT_ERROR_HUMANREADABLE: + svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data); + errcode = svn_hash_gets(attrs, "errcode"); + if (errcode) + svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode); + break; + case MS_PROPSTAT_RESPONSEDESCRIPTION: + svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description", + cdata->data); + break; + + case MS_PROPSTAT: + if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL) + { + apr_hash_t *response_attrs; + error_item_t *item; + + response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE); + item = apr_pcalloc(server_error->pool, sizeof(*item)); + + item->path = apr_pstrdup(server_error->pool, + svn_hash_gets(response_attrs, "path")); + item->propname = apr_pstrdup(server_error->pool, + svn_hash_gets(attrs, "propname")); + + SVN_ERR(parse_status_line(&item->http_status, + &item->http_reason, + status, + server_error->pool, + scratch_pool)); + + /* Do we have a mod_dav specific message? */ + item->message = svn_hash_gets(attrs, "human-readable"); + + if (item->message) + { + if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL) + { + apr_int64_t val; + + SVN_ERR(svn_cstring_atoi64(&val, errcode)); + item->apr_err = (apr_status_t)val; + } + + item->message = apr_pstrdup(server_error->pool, item->message); + } + else + item->message = apr_pstrdup(server_error->pool, + svn_hash_gets(attrs, "description")); + + + APR_ARRAY_PUSH(server_error->items, error_item_t *) = item; + } + break; + + case M_ERROR_HUMANREADABLE: + svn_ra_serf__xml_note(xes, D_ERROR, "human-readable", cdata->data); + errcode = svn_hash_gets(attrs, "errcode"); + if (errcode) + svn_ra_serf__xml_note(xes, D_ERROR, "errcode", errcode); + break; + + case D_ERROR: + { + error_item_t *item; + + item = apr_pcalloc(server_error->pool, sizeof(*item)); + + item->http_status = server_error->handler->sline.code; + + /* Do we have a mod_dav specific message? */ + item->message = svn_hash__get_cstring(attrs, "human-readable", + NULL); + + if (item->message) + { + if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL) + { + apr_int64_t val; + + SVN_ERR(svn_cstring_atoi64(&val, errcode)); + item->apr_err = (apr_status_t)val; + } + + item->message = apr_pstrdup(server_error->pool, item->message); + } + + + APR_ARRAY_PUSH(server_error->items, error_item_t *) = item; + } + } + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__server_error_t *server_error = handler->server_error; + svn_error_t *err = NULL; + int i; + + for (i = 0; i < server_error->items->nelts; i++) + { + const error_item_t *item; + apr_status_t status; + const char *message; + svn_error_t *new_err; + + item = APR_ARRAY_IDX(server_error->items, i, error_item_t *); + + if (!item->apr_err && item->http_status == 200) + { + continue; /* Success code */ + } + else if (!item->apr_err && item->http_status == 424 && item->propname) + { + continue; /* Failed because other PROPPATCH operations failed */ + } + + if (item->apr_err) + status = item->apr_err; + else + switch (item->http_status) + { + case 0: + continue; /* Not an error */ + case 301: + case 302: + case 303: + case 307: + case 308: + status = SVN_ERR_RA_DAV_RELOCATED; + break; + case 403: + status = SVN_ERR_RA_DAV_FORBIDDEN; + break; + case 404: + status = SVN_ERR_FS_NOT_FOUND; + break; + case 409: + status = SVN_ERR_FS_CONFLICT; + break; + case 412: + status = SVN_ERR_RA_DAV_PRECONDITION_FAILED; + break; + case 423: + status = SVN_ERR_FS_NO_LOCK_TOKEN; + break; + case 500: + status = SVN_ERR_RA_DAV_REQUEST_FAILED; + break; + case 501: + status = SVN_ERR_UNSUPPORTED_FEATURE; + break; + default: + if (err) + status = err->apr_err; /* Just use previous */ + else + status = SVN_ERR_RA_DAV_REQUEST_FAILED; + break; + } + + if (item->message && *item->message) + { + svn_stringbuf_t *sb = svn_stringbuf_create(item->message, + scratch_pool); + + svn_stringbuf_strip_whitespace(sb); + message = sb->data; + } + else if (item->propname) + { + message = apr_psprintf(scratch_pool, + _("Property operation on '%s' failed"), + item->propname); + } + else + { + /* Yuck: Older servers sometimes assume that we get convertable + apr error codes, while mod_dav_svn just produces a blank + text error, because err->message is NULL. */ + serf_status_line sline; + svn_error_t *tmp_err; + + memset(&sline, 0, sizeof(sline)); + sline.code = item->http_status; + sline.reason = item->http_reason; + + tmp_err = svn_ra_serf__error_on_status(sline, item->path, NULL); + + message = (tmp_err && tmp_err->message) + ? apr_pstrdup(scratch_pool, tmp_err->message) + : _(""); + svn_error_clear(tmp_err); + } + + SVN_ERR_ASSERT(status > 0); + new_err = svn_error_create(status, NULL, message); + + if (item->propname) + new_err = svn_error_createf(new_err->apr_err, new_err, + _("While handling the '%s' property on '%s':"), + item->propname, item->path); + else if (item->path) + new_err = svn_error_createf(new_err->apr_err, new_err, + _("While handling the '%s' path:"), + item->path); + + err = svn_error_compose_create( + err, + new_err); + } + + /* Theoretically a 207 status can have a 'global' description without a + global STATUS that summarizes the final result of property/href + operations. + + We should wrap that around the existing errors if there is one. + + But currently I don't see how mod_dav ever sets that value */ + + if (!err) + { + /* We should fail.... but why... Who installed us? */ + err = svn_error_trace(svn_ra_serf__unexpected_status(handler)); + } + + return err; +} + + +svn_error_t * +svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err, + svn_ra_serf__handler_t *handler, + svn_boolean_t expect_207_only, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__server_error_t *ms_baton; + svn_ra_serf__handler_t *tmp_handler; + + int *expected_status = apr_pcalloc(result_pool, + 2 * sizeof(expected_status[0])); + + expected_status[0] = handler->sline.code; + + ms_baton = apr_pcalloc(result_pool, sizeof(*ms_baton)); + ms_baton->pool = result_pool; + + ms_baton->items = apr_array_make(result_pool, 4, sizeof(error_item_t *)); + ms_baton->handler = handler; + + ms_baton->xmlctx = svn_ra_serf__xml_context_create(multistatus_ttable, + multistatus_opened, + multistatus_closed, + NULL, + ms_baton, + ms_baton->pool); + + tmp_handler = svn_ra_serf__create_expat_handler(handler->session, + ms_baton->xmlctx, + expected_status, + result_pool); + + /* Ugly way to obtain expat_handler() */ + tmp_handler->sline = handler->sline; + ms_baton->response_handler = tmp_handler->response_handler; + ms_baton->response_baton = tmp_handler->response_baton; + + *server_err = ms_baton; + return SVN_NO_ERROR; +} + + + +/* Implements svn_ra_serf__response_handler_t */ +svn_error_t * +svn_ra_serf__handle_multistatus_only(serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__handler_t *handler = baton; + + /* This function is just like expect_empty_body() except for the + XML parsing callbacks. We are looking for very limited pieces of + the multistatus response. */ + + /* We should see this just once, in order to initialize SERVER_ERROR. + At that point, the core error processing will take over. If we choose + not to parse an error, then we'll never return here (because we + change the response handler). */ + SVN_ERR_ASSERT(handler->server_error == NULL); + + { + serf_bucket_t *hdrs; + const char *val; + + hdrs = serf_bucket_response_get_headers(response); + val = serf_bucket_headers_get(hdrs, "Content-Type"); + if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) + { + svn_ra_serf__server_error_t *server_err; + + SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, + handler, + TRUE, + handler->handler_pool, + handler->handler_pool)); + + handler->server_error = server_err; + } + else + { + /* The body was not text/xml, so we don't know what to do with it. + Toss anything that arrives. */ + handler->discard_body = TRUE; + } + } + + /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it + to call the response handler again. That will start up the XML parsing, + or it will be dropped on the floor (per the decision above). */ + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error, + svn_ra_serf__handler_t *handler, + serf_request_t *request, + serf_bucket_t *response, + apr_status_t *serf_status, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + + err = server_error->response_handler(request, response, + server_error->response_baton, + scratch_pool); + /* If we do not receive an error or it is a non-transient error, return + immediately. + + APR_EOF will be returned when parsing is complete. + + APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through + parsing and the network has no more data right now. If we receive that, + clear the error and return - allowing serf to wait for more data. + */ + if (!err || SERF_BUCKET_READ_ERROR(err->apr_err)) + return svn_error_trace(err); + + if (!APR_STATUS_IS_EOF(err->apr_err)) + { + *serf_status = err->apr_err; + svn_error_clear(err); + return SVN_NO_ERROR; + } + + /* Clear the EOF. We don't need it as subversion error. */ + svn_error_clear(err); + *serf_status = APR_EOF; + + /* On PROPPATCH we always get status 207, which may or may not imply an + error status, but let's keep it generic and just do the check for + any multistatus */ + if (handler->sline.code == 207 /* MULTISTATUS */) + { + svn_boolean_t have_error = FALSE; + int i; + + for (i = 0; i < server_error->items->nelts; i++) + { + const error_item_t *item; + item = APR_ARRAY_IDX(server_error->items, i, error_item_t *); + + if (!item->apr_err && item->http_status == 200) + { + continue; /* Success code */ + } + + have_error = TRUE; + break; + } + + if (! have_error) + handler->server_error = NULL; /* We didn't have a server error */ + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/options.c b/contrib/subversion/subversion/libsvn_ra_serf/options.c index 5389b0452..a52977bbf 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/options.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/options.c @@ -30,6 +30,7 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_pools.h" +#include "svn_path.h" #include "svn_ra.h" #include "svn_dav.h" #include "svn_xml.h" @@ -51,7 +52,7 @@ * This enum represents the current state of our XML parsing for an OPTIONS. */ enum options_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, OPTIONS, ACTIVITY_COLLECTION, HREF @@ -65,7 +66,6 @@ typedef struct options_context_t { 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; @@ -112,19 +112,20 @@ options_closed(svn_ra_serf__xml_estate_t *xes, return SVN_NO_ERROR; } - +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_options_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_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_VA_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"); @@ -372,7 +373,7 @@ options_response_handler(serf_request_t *request, serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, opt_ctx); - /* Assume mergeinfo capability unsupported, if didn't recieve information + /* Assume mergeinfo capability unsupported, if didn't receive 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, @@ -389,7 +390,6 @@ options_response_handler(serf_request_t *request, 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; @@ -399,7 +399,6 @@ create_options_req(options_context_t **opt_ctx, 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; @@ -407,14 +406,12 @@ create_options_req(options_context_t **opt_ctx, NULL, options_closed, NULL, new_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, 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; @@ -431,22 +428,25 @@ create_options_req(options_context_t **opt_ctx, svn_error_t * svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, 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(create_options_req(&opt_ctx, session, 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)); + + if (opt_ctx->handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler)); + + if (! SVN_IS_VALID_REVNUM(opt_ctx->youngest_rev)) + return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, + _("The OPTIONS response did not include " + "the youngest revision")); *youngest = opt_ctx->youngest_rev; - SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest)); return SVN_NO_ERROR; } @@ -454,21 +454,39 @@ svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, svn_error_t * svn_ra_serf__v1_get_activity_collection(const char **activity_url, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, 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)); + if (session->activity_collection_url) + { + *activity_url = apr_pstrdup(result_pool, + session->activity_collection_url); + return SVN_NO_ERROR; + } + + SVN_ERR(create_options_req(&opt_ctx, session, 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)); + if (opt_ctx->handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler)); + + /* Cache the result. */ + if (opt_ctx->activity_collection) + { + session->activity_collection_url = + apr_pstrdup(session->pool, opt_ctx->activity_collection); + } + 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")); + } *activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection); @@ -483,15 +501,20 @@ svn_ra_serf__v1_get_activity_collection(const char **activity_url, svn_error_t * svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, const char **corrected_url, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { options_context_t *opt_ctx; - svn_error_t *err; + + if (corrected_url) + *corrected_url = NULL; /* This routine automatically fills in serf_sess->capabilities */ - SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool)); + SVN_ERR(create_options_req(&opt_ctx, serf_sess, scratch_pool)); - err = svn_ra_serf__context_run_one(opt_ctx->handler, pool); + opt_ctx->handler->no_fail_on_http_redirect_status = TRUE; + + SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); /* If our caller cares about server redirections, and our response carries such a thing, report as much. We'll disregard ERR -- @@ -499,16 +522,47 @@ svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, 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; + if (!opt_ctx->handler->location || !*opt_ctx->handler->location) + { + return svn_error_create( + SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL, + _("Location header not set on redirect response")); + } + else if (svn_path_is_url(opt_ctx->handler->location)) + { + *corrected_url = svn_uri_canonicalize(opt_ctx->handler->location, + result_pool); + } + else + { + /* RFC1945 and RFC2616 state that the Location header's value + (from whence this CORRECTED_URL comes), if present, must be an + absolute URI. But some Apache versions (those older than 2.2.11, + it seems) transmit only the path portion of the URI. + See issue #3775 for details. */ + + apr_uri_t corrected_URI = serf_sess->session_url; + + corrected_URI.path = (char *)corrected_url; + *corrected_url = svn_uri_canonicalize( + apr_uri_unparse(scratch_pool, &corrected_URI, 0), + result_pool); + } + return SVN_NO_ERROR; } + else if (opt_ctx->handler->sline.code >= 300 + && opt_ctx->handler->sline.code < 399) + { + return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL, + (opt_ctx->handler->sline.code == 301 + ? _("Repository moved permanently to '%s'") + : _("Repository moved temporarily to '%s'")), + opt_ctx->handler->location); + } - 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)); + if (opt_ctx->handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler)); /* Opportunistically cache any reported activity URL. (We don't want to have to ask for this again later, potentially against an @@ -522,12 +576,13 @@ svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, return SVN_NO_ERROR; } - +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_simple_options_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { serf_bucket_t *body; serf_bucket_t *s; @@ -549,18 +604,16 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, { svn_ra_serf__handler_t *handler; - handler = apr_pcalloc(scratch_pool, sizeof(*handler)); - handler->handler_pool = scratch_pool; + handler = svn_ra_serf__create_handler(serf_sess, 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; + handler->no_fail_on_http_failure_status = TRUE; /* No special headers. */ @@ -574,9 +627,8 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, return SVN_NO_ERROR; } - SVN_ERR(svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location)); + if (handler->sline.code != 200) + SVN_ERR(svn_ra_serf__unexpected_status(handler)); return SVN_NO_ERROR; } @@ -602,7 +654,7 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session, /* 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)); + SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool, pool)); /* Try again, now that we've fetched the capabilities. */ cap_result = svn_hash_gets(serf_sess->capabilities, capability); @@ -628,7 +680,9 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session, APR_ARRAY_PUSH(paths, const char *) = ""; err = svn_ra_serf__get_mergeinfo(ra_session, &ignored, paths, 0, - FALSE, FALSE, pool); + svn_mergeinfo_explicit, + FALSE /* include_descendants */, + pool); if (err) { @@ -646,7 +700,7 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session, cap_result = capability_yes; } else - return err; + return svn_error_trace(err); } else cap_result = capability_yes; diff --git a/contrib/subversion/subversion/libsvn_ra_serf/property.c b/contrib/subversion/subversion/libsvn_ra_serf/property.c index 586d38ff4..b7e03183c 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/property.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/property.c @@ -42,7 +42,7 @@ /* Our current parsing state we're in for the PROPFIND response. */ typedef enum prop_state_e { - INITIAL = 0, + INITIAL = XML_STATE_INITIAL, MULTISTATUS, RESPONSE, HREF, @@ -59,20 +59,12 @@ typedef enum prop_state_e { * This structure represents a pending PROPFIND response. */ typedef struct propfind_context_t { - /* pool to issue allocations from */ - apr_pool_t *pool; - svn_ra_serf__handler_t *handler; - /* associated serf session */ - svn_ra_serf__session_t *sess; - svn_ra_serf__connection_t *conn; - /* the requested path */ const char *path; - /* the requested version (number and string form) */ - svn_revnum_t rev; + /* the requested version (in string form) */ const char *label; /* the request depth */ @@ -81,12 +73,8 @@ typedef struct propfind_context_t { /* the list of requested properties */ const svn_ra_serf__dav_props_t *find_props; - /* hash table that will be updated with the properties - * - * This can be shared between multiple propfind_context_t - * structures - */ - apr_hash_t *ret_props; + svn_ra_serf__prop_func_t prop_func; + void *prop_func_baton; /* hash table containing all the properties associated with the * "current" tag. These will get copied into RET_PROPS @@ -94,12 +82,6 @@ typedef struct propfind_context_t { * "good"; otherwise, they'll get discarded. */ apr_hash_t *ps_props; - - /* If not-NULL, add us to this list when we're done. */ - svn_ra_serf__list_t **done_list; - - svn_ra_serf__list_t done_item; - } propfind_context_t; @@ -136,10 +118,14 @@ static const svn_ra_serf__xml_transition_t propfind_ttable[] = { { 0 } }; +static const int propfind_expected_status[] = { + 207, + 0 +}; /* Return the HTTP status code contained in STATUS_LINE, or 0 if there's a problem parsing it. */ -static int parse_status_code(const char *status_line) +static apr_int64_t parse_status_code(const char *status_line) { /* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */ if (status_line[0] == 'H' && @@ -159,24 +145,6 @@ static int parse_status_code(const char *status_line) return 0; } - -/* Conforms to svn_ra_serf__path_rev_walker_t */ -static svn_error_t * -copy_into_ret_props(void *baton, - const char *path, apr_ssize_t path_len, - const char *ns, apr_ssize_t ns_len, - const char *name, apr_ssize_t name_len, - const svn_string_t *val, - apr_pool_t *pool) -{ - propfind_context_t *ctx = baton; - - svn_ra_serf__set_ver_prop(ctx->ret_props, path, ctx->rev, ns, name, - val, ctx->pool); - return SVN_NO_ERROR; -} - - /* Conforms to svn_ra_serf__xml_opened_t */ static svn_error_t * propfind_opened(svn_ra_serf__xml_estate_t *xes, @@ -189,17 +157,40 @@ propfind_opened(svn_ra_serf__xml_estate_t *xes, if (entered_state == PROPVAL) { - svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace); + svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->xmlns); svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name); } else if (entered_state == PROPSTAT) { - ctx->ps_props = apr_hash_make(ctx->pool); + ctx->ps_props = apr_hash_make(svn_ra_serf__xml_state_pool(xes)); } return SVN_NO_ERROR; } +/* Set PROPS for NS:NAME VAL. Helper for propfind_closed */ +static void +set_ns_prop(apr_hash_t *ns_props, + const char *ns, const char *name, + const svn_string_t *val, apr_pool_t *result_pool) +{ + apr_hash_t *props = svn_hash_gets(ns_props, ns); + + if (!props) + { + props = apr_hash_make(result_pool); + ns = apr_pstrdup(result_pool, ns); + svn_hash_sets(ns_props, ns, props); + } + + if (val) + { + name = apr_pstrdup(result_pool, name); + val = svn_string_dup(val, result_pool); + } + + svn_hash_sets(props, name, val); +} /* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * @@ -218,17 +209,10 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, onto the "done list". External callers will then know this request has been completed (tho stray response bytes may still arrive). */ - if (ctx->done_list) - { - ctx->done_item.data = ctx->handler; - ctx->done_item.next = *ctx->done_list; - *ctx->done_list = &ctx->done_item; - } } else if (leaving_state == HREF) { const char *path; - const svn_string_t *val_str; if (strcmp(ctx->depth, "1") == 0) path = svn_urlpath__canonicalize(cdata->data, scratch_pool); @@ -237,11 +221,10 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, svn_ra_serf__xml_note(xes, RESPONSE, "path", path); - /* Copy the value into the right pool, then save the HREF. */ - val_str = svn_string_dup(cdata, ctx->pool); - svn_ra_serf__set_ver_prop(ctx->ret_props, - path, ctx->rev, D_, "href", val_str, - ctx->pool); + SVN_ERR(ctx->prop_func(ctx->prop_func_baton, + path, + D_, "href", + cdata, scratch_pool)); } else if (leaving_state == COLLECTION) { @@ -257,21 +240,23 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, that we wish to ignore. (Typically, if it's not a 200, the status will be 404 to indicate that a property we specifically requested from the server doesn't exist.) */ - int status = parse_status_code(cdata->data); + apr_int64_t status = parse_status_code(cdata->data); if (status != 200) svn_ra_serf__xml_note(xes, PROPSTAT, "ignore-prop", "*"); } else if (leaving_state == PROPVAL) { - const char *encoding = svn_hash_gets(attrs, "V:encoding"); + const char *encoding; const svn_string_t *val_str; - apr_hash_t *gathered; - const char *path; const char *ns; const char *name; const char *altvalue; - if (encoding) + if ((altvalue = svn_hash_gets(attrs, "altvalue")) != NULL) + { + val_str = svn_string_create(altvalue, scratch_pool); + } + else if ((encoding = svn_hash_gets(attrs, "V:encoding")) != NULL) { if (strcmp(encoding, "base64") != 0) return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, @@ -280,17 +265,15 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, encoding); /* Decode into the right pool. */ - val_str = svn_base64_decode_string(cdata, ctx->pool); + val_str = svn_base64_decode_string(cdata, scratch_pool); } else { /* Copy into the right pool. */ - val_str = svn_string_dup(cdata, ctx->pool); + val_str = cdata; } - /* The current path sits on the RESPONSE state. Gather up all the - state from this PROPVAL to the (grandparent) RESPONSE state, - and grab the path from there. + /* The current path sits on the RESPONSE state. Now, it would be nice if we could, at this point, know that the status code for this property indicated a problem -- then @@ -300,24 +283,12 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, here, setting the property and value as expected. Once we know for sure the status code associate with the property, we'll decide its fate. */ - gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE); - - /* These will be dup'd into CTX->POOL, as necessary. */ - path = svn_hash_gets(gathered, "path"); - if (path == NULL) - path = ctx->path; ns = svn_hash_gets(attrs, "ns"); - name = apr_pstrdup(ctx->pool, - svn_hash_gets(attrs, "name")); - - altvalue = svn_hash_gets(attrs, "altvalue"); - if (altvalue != NULL) - val_str = svn_string_create(altvalue, ctx->pool); + name = svn_hash_gets(attrs, "name"); - svn_ra_serf__set_ver_prop(ctx->ps_props, - path, ctx->rev, ns, name, val_str, - ctx->pool); + set_ns_prop(ctx->ps_props, ns, name, val_str, + apr_hash_pool_get(ctx->ps_props)); } else { @@ -325,146 +296,60 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes, SVN_ERR_ASSERT(leaving_state == PROPSTAT); - gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT); + gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE); /* If we've squirreled away a note that says we want to ignore these properties, we'll do so. Otherwise, we need to copy them from the temporary hash into the ctx->ret_props hash. */ if (! svn_hash_gets(gathered, "ignore-prop")) { - SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev, - copy_into_ret_props, ctx, - scratch_pool)); - } + apr_hash_index_t *hi_ns; + const char *path; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); - ctx->ps_props = NULL; - } - - return SVN_NO_ERROR; -} + path = svn_hash_gets(gathered, "path"); + if (!path) + path = ctx->path; -const svn_string_t * -svn_ra_serf__get_ver_prop_string(apr_hash_t *props, - const char *path, - svn_revnum_t rev, - const char *ns, - const char *name) -{ - apr_hash_t *ver_props, *path_props, *ns_props; - void *val = NULL; - - ver_props = apr_hash_get(props, &rev, sizeof(rev)); - if (ver_props) - { - path_props = svn_hash_gets(ver_props, path); - - if (path_props) - { - ns_props = svn_hash_gets(path_props, ns); - if (ns_props) + for (hi_ns = apr_hash_first(scratch_pool, ctx->ps_props); + hi_ns; + hi_ns = apr_hash_next(hi_ns)) { - val = svn_hash_gets(ns_props, name); + const char *ns = apr_hash_this_key(hi_ns); + apr_hash_t *props = apr_hash_this_val(hi_ns); + apr_hash_index_t *hi_prop; + + svn_pool_clear(iterpool); + + for (hi_prop = apr_hash_first(iterpool, props); + hi_prop; + hi_prop = apr_hash_next(hi_prop)) + { + const char *name = apr_hash_this_key(hi_prop); + const svn_string_t *value = apr_hash_this_val(hi_prop); + + SVN_ERR(ctx->prop_func(ctx->prop_func_baton, path, + ns, name, value, iterpool)); + } } - } - } - - return val; -} - -const char * -svn_ra_serf__get_ver_prop(apr_hash_t *props, - const char *path, - svn_revnum_t rev, - const char *ns, - const char *name) -{ - const svn_string_t *val; - val = svn_ra_serf__get_ver_prop_string(props, path, rev, ns, name); - - if (val) - { - return val->data; - } - - return NULL; -} - -const svn_string_t * -svn_ra_serf__get_prop_string(apr_hash_t *props, - const char *path, - const char *ns, - const char *name) -{ - return svn_ra_serf__get_ver_prop_string(props, path, SVN_INVALID_REVNUM, - ns, name); -} - -const char * -svn_ra_serf__get_prop(apr_hash_t *props, - const char *path, - const char *ns, - const char *name) -{ - return svn_ra_serf__get_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name); -} - -void -svn_ra_serf__set_ver_prop(apr_hash_t *props, - const char *path, svn_revnum_t rev, - const char *ns, const char *name, - const svn_string_t *val, apr_pool_t *pool) -{ - apr_hash_t *ver_props, *path_props, *ns_props; - - ver_props = apr_hash_get(props, &rev, sizeof(rev)); - if (!ver_props) - { - ver_props = apr_hash_make(pool); - apr_hash_set(props, apr_pmemdup(pool, &rev, sizeof(rev)), sizeof(rev), - ver_props); - } - - path_props = svn_hash_gets(ver_props, path); - - if (!path_props) - { - path_props = apr_hash_make(pool); - path = apr_pstrdup(pool, path); - svn_hash_sets(ver_props, path, path_props); - - /* todo: we know that we'll fail the next check, but fall through - * for now for simplicity's sake. - */ - } + svn_pool_destroy(iterpool); + } - ns_props = svn_hash_gets(path_props, ns); - if (!ns_props) - { - ns_props = apr_hash_make(pool); - ns = apr_pstrdup(pool, ns); - svn_hash_sets(path_props, ns, ns_props); + ctx->ps_props = NULL; /* Allocated in PROPSTAT state pool */ } - svn_hash_sets(ns_props, name, val); + return SVN_NO_ERROR; } -void -svn_ra_serf__set_prop(apr_hash_t *props, - const char *path, - const char *ns, const char *name, - const svn_string_t *val, apr_pool_t *pool) -{ - svn_ra_serf__set_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name, - val, pool); -} static svn_error_t * setup_propfind_headers(serf_bucket_t *headers, - void *setup_baton, - apr_pool_t *pool) + void *setup_baton, + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { propfind_context_t *ctx = setup_baton; @@ -480,11 +365,13 @@ setup_propfind_headers(serf_bucket_t *headers, #define PROPFIND_HEADER "" #define PROPFIND_TRAILER "" +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_propfind_body(serf_bucket_t **bkt, void *setup_baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { propfind_context_t *ctx = setup_baton; @@ -495,7 +382,7 @@ create_propfind_body(serf_bucket_t **bkt, body_bkt = serf_bucket_aggregate_create(alloc); prop = ctx->find_props; - while (prop && prop->namespace) + while (prop && prop->xmlns) { /* special case the allprop case. */ if (strcmp(prop->name, "allprop") == 0) @@ -515,7 +402,7 @@ create_propfind_body(serf_bucket_t **bkt, alloc); serf_bucket_aggregate_append(body_bkt, tmp); - tmp = SERF_BUCKET_SIMPLE_STRING(prop->namespace, alloc); + tmp = SERF_BUCKET_SIMPLE_STRING(prop->xmlns, alloc); serf_bucket_aggregate_append(body_bkt, tmp); tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"/>", sizeof("\"/>")-1, @@ -559,16 +446,15 @@ create_propfind_body(serf_bucket_t **bkt, svn_error_t * -svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, - apr_hash_t *ret_props, - svn_ra_serf__session_t *sess, - svn_ra_serf__connection_t *conn, - const char *path, - svn_revnum_t rev, - const char *depth, - const svn_ra_serf__dav_props_t *find_props, - svn_ra_serf__list_t **done_list, - apr_pool_t *pool) +svn_ra_serf__create_propfind_handler(svn_ra_serf__handler_t **propfind_handler, + svn_ra_serf__session_t *sess, + const char *path, + svn_revnum_t rev, + const char *depth, + const svn_ra_serf__dav_props_t *find_props, + svn_ra_serf__prop_func_t prop_func, + void *prop_func_baton, + apr_pool_t *pool) { propfind_context_t *new_prop_ctx; svn_ra_serf__handler_t *handler; @@ -576,15 +462,11 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx)); - new_prop_ctx->pool = apr_hash_pool_get(ret_props); new_prop_ctx->path = path; new_prop_ctx->find_props = find_props; - new_prop_ctx->ret_props = ret_props; + new_prop_ctx->prop_func = prop_func; + new_prop_ctx->prop_func_baton = prop_func_baton; new_prop_ctx->depth = depth; - new_prop_ctx->sess = sess; - new_prop_ctx->conn = conn; - new_prop_ctx->rev = rev; - new_prop_ctx->done_list = done_list; if (SVN_IS_VALID_REVNUM(rev)) { @@ -601,7 +483,9 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, NULL, new_prop_ctx, pool); - handler = svn_ra_serf__create_expat_handler(xmlctx, pool); + handler = svn_ra_serf__create_expat_handler(sess, xmlctx, + propfind_expected_status, + pool); handler->method = "PROPFIND"; handler->path = path; @@ -611,8 +495,7 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, handler->header_delegate = setup_propfind_headers; handler->header_delegate_baton = new_prop_ctx; - handler->session = new_prop_ctx->sess; - handler->conn = new_prop_ctx->conn; + handler->no_dav_headers = TRUE; new_prop_ctx->handler = handler; @@ -621,208 +504,85 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, return SVN_NO_ERROR; } - -/* - * This helper function will block until the PROP_CTX indicates that is done - * or another error is returned. - */ svn_error_t * -svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler, - apr_pool_t *scratch_pool) +svn_ra_serf__deliver_svn_props(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) { - svn_error_t *err; - svn_error_t *err2; + apr_hash_t *props = baton; + apr_pool_t *result_pool = apr_hash_pool_get(props); + const char *prop_name; - err = svn_ra_serf__context_run_one(handler, scratch_pool); + prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool); + if (prop_name == NULL) + return SVN_NO_ERROR; - err2 = svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location); + svn_hash_sets(props, prop_name, svn_string_dup(value, result_pool)); - return svn_error_compose_create(err2, err); + return SVN_NO_ERROR; } /* - * This is a blocking version of deliver_props. + * Implementation of svn_ra_serf__prop_func_t that delivers all DAV properties + * in (const char * -> apr_hash_t *) on Namespace pointing to a second hash + * (const char * -> svn_string_t *) to the values. */ -svn_error_t * -svn_ra_serf__retrieve_props(apr_hash_t **results, - svn_ra_serf__session_t *sess, - svn_ra_serf__connection_t *conn, - const char *url, - svn_revnum_t rev, - const char *depth, - const svn_ra_serf__dav_props_t *props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +static svn_error_t * +deliver_node_props(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) { - svn_ra_serf__handler_t *handler; + apr_hash_t *nss = baton; + apr_hash_t *props; + apr_pool_t *result_pool = apr_hash_pool_get(nss); - *results = apr_hash_make(result_pool); + props = svn_hash_gets(nss, ns); - SVN_ERR(svn_ra_serf__deliver_props(&handler, *results, sess, conn, url, - rev, depth, props, NULL, result_pool)); - SVN_ERR(svn_ra_serf__wait_for_props(handler, scratch_pool)); + if (!props) + { + props = apr_hash_make(result_pool); + + ns = apr_pstrdup(result_pool, ns); + svn_hash_sets(nss, ns, props); + } + + name = apr_pstrdup(result_pool, name); + svn_hash_sets(props, name, svn_string_dup(value, result_pool)); return SVN_NO_ERROR; } - svn_error_t * svn_ra_serf__fetch_node_props(apr_hash_t **results, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *url, svn_revnum_t revision, const svn_ra_serf__dav_props_t *which_props, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *multiprops; - apr_hash_t *ver_props; - - /* Note: a couple extra hash tables and whatnot get into RESULT_POOL. - Not a big deal at this point. Theoretically, we could fetch all - props into SCRATCH_POOL, then copy just the REVISION/URL props - into RESULT_POOL. Too much work for too little gain... */ - SVN_ERR(svn_ra_serf__retrieve_props(&multiprops, conn->session, conn, - url, revision, "0", which_props, - result_pool, scratch_pool)); - - ver_props = apr_hash_get(multiprops, &revision, sizeof(revision)); - if (ver_props != NULL) - { - *results = svn_hash_gets(ver_props, url); - if (*results != NULL) - return SVN_NO_ERROR; - } - - return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, - _("The PROPFIND response did not include " - "the requested properties")); -} - - -svn_error_t * -svn_ra_serf__walk_node_props(apr_hash_t *props, - svn_ra_serf__walker_visitor_t walker, - void *baton, - apr_pool_t *scratch_pool) -{ - apr_pool_t *iterpool; - apr_hash_index_t *ns_hi; - - iterpool = svn_pool_create(scratch_pool); - for (ns_hi = apr_hash_first(scratch_pool, props); ns_hi; - ns_hi = apr_hash_next(ns_hi)) - { - void *ns_val; - const void *ns_name; - apr_hash_index_t *name_hi; - - /* NOTE: We do not clear ITERPOOL in this loop. Generally, there are - very few namespaces, so this loop will not have many iterations. - Instead, ITERPOOL is used for the inner loop. */ - - apr_hash_this(ns_hi, &ns_name, NULL, &ns_val); - - for (name_hi = apr_hash_first(scratch_pool, ns_val); name_hi; - name_hi = apr_hash_next(name_hi)) - { - void *prop_val; - const void *prop_name; - - /* See note above, regarding clearing of this pool. */ - svn_pool_clear(iterpool); - - apr_hash_this(name_hi, &prop_name, NULL, &prop_val); - - SVN_ERR(walker(baton, ns_name, prop_name, prop_val, iterpool)); - } - } - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_ra_serf__walk_all_props(apr_hash_t *props, - const char *name, - svn_revnum_t rev, - svn_ra_serf__walker_visitor_t walker, - void *baton, - apr_pool_t *scratch_pool) -{ - apr_hash_t *ver_props; - apr_hash_t *path_props; - - ver_props = apr_hash_get(props, &rev, sizeof(rev)); - if (!ver_props) - return SVN_NO_ERROR; - - path_props = svn_hash_gets(ver_props, name); - if (!path_props) - return SVN_NO_ERROR; - - return svn_error_trace(svn_ra_serf__walk_node_props(path_props, - walker, baton, - scratch_pool)); -} - - -svn_error_t * -svn_ra_serf__walk_all_paths(apr_hash_t *props, - svn_revnum_t rev, - svn_ra_serf__path_rev_walker_t walker, - void *baton, - apr_pool_t *pool) -{ - apr_hash_index_t *path_hi; - apr_hash_t *ver_props; + apr_hash_t *props; + svn_ra_serf__handler_t *handler; - ver_props = apr_hash_get(props, &rev, sizeof(rev)); + props = apr_hash_make(result_pool); - if (!ver_props) - { - return SVN_NO_ERROR; - } + SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, + url, revision, "0", which_props, + deliver_node_props, + props, scratch_pool)); - for (path_hi = apr_hash_first(pool, ver_props); path_hi; - path_hi = apr_hash_next(path_hi)) - { - void *path_props; - const void *path_name; - apr_ssize_t path_len; - apr_hash_index_t *ns_hi; - - apr_hash_this(path_hi, &path_name, &path_len, &path_props); - for (ns_hi = apr_hash_first(pool, path_props); ns_hi; - ns_hi = apr_hash_next(ns_hi)) - { - void *ns_val; - const void *ns_name; - apr_ssize_t ns_len; - apr_hash_index_t *name_hi; - apr_hash_this(ns_hi, &ns_name, &ns_len, &ns_val); - for (name_hi = apr_hash_first(pool, ns_val); name_hi; - name_hi = apr_hash_next(name_hi)) - { - void *prop_val; - const void *prop_name; - apr_ssize_t prop_len; - - apr_hash_this(name_hi, &prop_name, &prop_len, &prop_val); - /* use a subpool? */ - SVN_ERR(walker(baton, path_name, path_len, ns_name, ns_len, - prop_name, prop_len, prop_val, pool)); - } - } - } + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); + *results = props; return SVN_NO_ERROR; } - const char * svn_ra_serf__svnname_from_wirename(const char *ns, const char *name, @@ -832,10 +592,10 @@ svn_ra_serf__svnname_from_wirename(const char *ns, return apr_pstrdup(result_pool, name); if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) - return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); + return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, SVN_VA_NULL); if (strcmp(ns, SVN_PROP_PREFIX) == 0) - return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); + return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, SVN_VA_NULL); if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) return SVN_PROP_ENTRY_COMMITTED_REV; @@ -863,99 +623,9 @@ svn_ra_serf__svnname_from_wirename(const char *ns, } /* An unknown namespace, must be a custom property. */ - return apr_pstrcat(result_pool, ns, name, (char *)NULL); + return apr_pstrcat(result_pool, ns, name, SVN_VA_NULL); } - -/* Conforms to svn_ra_serf__walker_visitor_t */ -static svn_error_t * -set_flat_props(void *baton, - const char *ns, - const char *name, - const svn_string_t *value, - apr_pool_t *pool) -{ - apr_hash_t *props = baton; - apr_pool_t *result_pool = apr_hash_pool_get(props); - const char *prop_name; - - /* ### is VAL in the proper pool? */ - - prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool); - if (prop_name != NULL) - svn_hash_sets(props, prop_name, value); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_ra_serf__flatten_props(apr_hash_t **flat_props, - apr_hash_t *props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - *flat_props = apr_hash_make(result_pool); - - return svn_error_trace(svn_ra_serf__walk_node_props( - props, - set_flat_props, - *flat_props /* baton */, - scratch_pool)); -} - - -static svn_error_t * -select_revprops(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - apr_hash_t *revprops = baton; - apr_pool_t *result_pool = apr_hash_pool_get(revprops); - const char *prop_name; - - /* ### copy NAME into the RESULT_POOL? */ - /* ### copy VAL into the RESULT_POOL? */ - - if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) - prop_name = name; - else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) - prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); - else if (strcmp(ns, SVN_PROP_PREFIX) == 0) - prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); - else if (strcmp(ns, "") == 0) - prop_name = name; - else - { - /* do nothing for now? */ - return SVN_NO_ERROR; - } - - svn_hash_sets(revprops, prop_name, val); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_ra_serf__select_revprops(apr_hash_t **revprops, - const char *name, - svn_revnum_t rev, - apr_hash_t *all_revprops, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - *revprops = apr_hash_make(result_pool); - - return svn_error_trace(svn_ra_serf__walk_all_props( - all_revprops, name, rev, - select_revprops, *revprops, - scratch_pool)); -} - - /* * Contact the server (using CONN) to calculate baseline * information for BASELINE_URL at REVISION (which may be @@ -969,7 +639,7 @@ svn_ra_serf__select_revprops(apr_hash_t **revprops, static svn_error_t * retrieve_baseline_info(svn_revnum_t *actual_revision, const char **basecoll_url_p, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *baseline_url, svn_revnum_t revision, apr_pool_t *result_pool, @@ -979,7 +649,7 @@ retrieve_baseline_info(svn_revnum_t *actual_revision, apr_hash_t *dav_props; const char *basecoll_url; - SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, + SVN_ERR(svn_ra_serf__fetch_node_props(&props, session, baseline_url, revision, baseline_props, scratch_pool, scratch_pool)); @@ -1000,12 +670,18 @@ retrieve_baseline_info(svn_revnum_t *actual_revision, const char *version_name; version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME); - if (!version_name) + if (version_name) + { + apr_int64_t rev; + + SVN_ERR(svn_cstring_atoi64(&rev, version_name)); + *actual_revision = (svn_revnum_t)rev; + } + + if (!version_name || !SVN_IS_VALID_REVNUM(*actual_revision)) return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, _("The PROPFIND response did not include " "the requested version-name value")); - - *actual_revision = SVN_STR_TO_REV(version_name); } return SVN_NO_ERROR; @@ -1023,7 +699,7 @@ retrieve_baseline_info(svn_revnum_t *actual_revision, static svn_error_t * v1_get_youngest_revnum(svn_revnum_t *youngest, const char **basecoll_url, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *vcc_url, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -1033,7 +709,7 @@ v1_get_youngest_revnum(svn_revnum_t *youngest, /* Fetching DAV:checked-in from the VCC (with no Label: to specify a revision) will return the latest Baseline resource's URL. */ - SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, conn, vcc_url, + SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, session, vcc_url, SVN_INVALID_REVNUM, "checked-in", scratch_pool, scratch_pool)); @@ -1052,15 +728,15 @@ v1_get_youngest_revnum(svn_revnum_t *youngest, /* First check baseline information cache. */ SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url, youngest, - conn->session->blncache, + session->blncache, baseline_url, scratch_pool)); if (!bc_url) { - SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn, + SVN_ERR(retrieve_baseline_info(youngest, &bc_url, session, baseline_url, SVN_INVALID_REVNUM, scratch_pool, scratch_pool)); - SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache, + SVN_ERR(svn_ra_serf__blncache_set(session->blncache, baseline_url, *youngest, bc_url, scratch_pool)); } @@ -1081,12 +757,12 @@ svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest, if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) return svn_error_trace(svn_ra_serf__v2_get_youngest_revnum( - youngest, session->conns[0], scratch_pool)); + youngest, session, scratch_pool)); - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, scratch_pool)); return svn_error_trace(v1_get_youngest_revnum(youngest, NULL, - session->conns[0], vcc_url, + session, vcc_url, scratch_pool, scratch_pool)); } @@ -1103,9 +779,9 @@ static svn_error_t * get_baseline_info(const char **bc_url, svn_revnum_t *revnum_used, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, svn_revnum_t revision, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { /* If we detected HTTP v2 support on the server, we can construct the baseline collection URL ourselves, and fetch the latest @@ -1119,14 +795,10 @@ get_baseline_info(const char **bc_url, else { SVN_ERR(svn_ra_serf__v2_get_youngest_revnum( - revnum_used, conn, pool)); - if (! SVN_IS_VALID_REVNUM(*revnum_used)) - return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, - _("The OPTIONS response did not include " - "the youngest revision")); + revnum_used, session, scratch_pool)); } - *bc_url = apr_psprintf(pool, "%s/%ld", + *bc_url = apr_psprintf(result_pool, "%s/%ld", session->rev_root_stub, *revnum_used); } @@ -1135,20 +807,22 @@ get_baseline_info(const char **bc_url, { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, scratch_pool)); if (SVN_IS_VALID_REVNUM(revision)) { /* First check baseline information cache. */ SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url, session->blncache, - revision, pool)); + revision, result_pool)); if (!*bc_url) { - SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn, - vcc_url, revision, pool, pool)); + SVN_ERR(retrieve_baseline_info(NULL, bc_url, session, + vcc_url, revision, + result_pool, scratch_pool)); SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL, - revision, *bc_url, pool)); + revision, *bc_url, + scratch_pool)); } *revnum_used = revision; @@ -1156,8 +830,8 @@ get_baseline_info(const char **bc_url, else { SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url, - conn, vcc_url, - pool, pool)); + session, vcc_url, + result_pool, scratch_pool)); } } @@ -1169,7 +843,6 @@ svn_error_t * svn_ra_serf__get_stable_url(const char **stable_url, svn_revnum_t *latest_revnum, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, const char *url, svn_revnum_t revision, apr_pool_t *result_pool, @@ -1183,15 +856,10 @@ svn_ra_serf__get_stable_url(const char **stable_url, if (! url) url = session->session_url.path; - /* If the caller didn't provide a specific connection for us to use, - we'll use the default connection. */ - if (! conn) - conn = session->conns[0]; - SVN_ERR(get_baseline_info(&basecoll_url, &revnum_used, - session, conn, revision, scratch_pool)); + session, revision, scratch_pool, scratch_pool)); SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url, - session, conn, scratch_pool)); + session, scratch_pool)); *stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath, result_pool); @@ -1202,39 +870,9 @@ svn_ra_serf__get_stable_url(const char **stable_url, } -svn_error_t * -svn_ra_serf__get_resource_type(svn_node_kind_t *kind, - apr_hash_t *props) -{ - apr_hash_t *dav_props; - const char *res_type; - - dav_props = apr_hash_get(props, "DAV:", 4); - res_type = svn_prop_get_value(dav_props, "resourcetype"); - if (!res_type) - { - /* How did this happen? */ - return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, - _("The PROPFIND response did not include the " - "requested resourcetype value")); - } - - if (strcmp(res_type, "collection") == 0) - { - *kind = svn_node_dir; - } - else - { - *kind = svn_node_file; - } - - return SVN_NO_ERROR; -} - - svn_error_t * svn_ra_serf__fetch_dav_prop(const char **value, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *url, svn_revnum_t revision, const char *propname, @@ -1244,7 +882,7 @@ svn_ra_serf__fetch_dav_prop(const char **value, apr_hash_t *props; apr_hash_t *dav_props; - SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision, + SVN_ERR(svn_ra_serf__fetch_node_props(&props, session, url, revision, checked_in_props, scratch_pool, scratch_pool)); dav_props = apr_hash_get(props, "DAV:", 4); @@ -1261,3 +899,19 @@ svn_ra_serf__fetch_dav_prop(const char **value, return SVN_NO_ERROR; } + +/* Removes all non regular properties from PROPS */ +void +svn_ra_serf__keep_only_regular_props(apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi)) + { + const char *propname = apr_hash_this_key(hi); + + if (svn_property_kind2(propname) != svn_prop_regular_kind) + svn_hash_sets(props, propname, NULL); + } +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/ra_serf.h b/contrib/subversion/subversion/libsvn_ra_serf/ra_serf.h index 335a9e397..fcef73777 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/ra_serf.h +++ b/contrib/subversion/subversion/libsvn_ra_serf/ra_serf.h @@ -54,9 +54,6 @@ extern "C" { #error Please update your version of serf to at least 1.2.1. #endif -/** Use this to silence compiler warnings about unused parameters. */ -#define UNUSED_CTX(x) ((void)(x)) - /** Wait duration (in microseconds) used in calls to serf_context_run() */ #define SVN_RA_SERF__CONTEXT_RUN_DURATION 500000 @@ -99,10 +96,13 @@ typedef struct svn_ra_serf__connection_t { * The master serf RA session. * * This is stored in the ra session ->priv field. + * + * ### Check ra_serf_dup_session when adding fields. */ struct svn_ra_serf__session_t { /* Pool for allocations during this session */ apr_pool_t *pool; + apr_hash_t *config; /* For duplicating */ /* The current context */ serf_context_t *context; @@ -154,6 +154,7 @@ struct svn_ra_serf__session_t { /* Callback functions to get info from WC */ const svn_ra_callbacks2_t *wc_callbacks; void *wc_callback_baton; + svn_auth_baton_t *auth_baton; /* Callback function to send progress info to the client */ svn_ra_progress_notify_func_t progress_func; @@ -263,31 +264,11 @@ struct svn_ra_serf__session_t { */ typedef struct svn_ra_serf__dav_props_t { /* Element namespace */ - const char *namespace; + const char *xmlns; /* Element name */ const char *name; } svn_ra_serf__dav_props_t; -/* - * Structure which represents an XML namespace. - */ -typedef struct ns_t { - /* The assigned name. */ - const char *namespace; - /* The full URL for this namespace. */ - const char *url; - /* The next namespace in our list. */ - struct ns_t *next; -} svn_ra_serf__ns_t; - -/* - * An incredibly simple list. - */ -typedef struct ra_serf_list_t { - void *data; - struct ra_serf_list_t *next; -} svn_ra_serf__list_t; - /** DAV property sets **/ static const svn_ra_serf__dav_props_t base_props[] = @@ -378,6 +359,16 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done, svn_ra_serf__session_t *sess, apr_pool_t *scratch_pool); +/* Run the context once. Manage waittime_left to handle timing out when + nothing happens over the session->timout. + */ +svn_error_t * +svn_ra_serf__context_run(svn_ra_serf__session_t *sess, + apr_interval_time_t *waittime_left, + apr_pool_t *scratch_pool); + + + /* Callback for response handlers */ typedef svn_error_t * (*svn_ra_serf__response_handler_t)(serf_request_t *request, @@ -385,20 +376,26 @@ typedef svn_error_t * void *handler_baton, apr_pool_t *scratch_pool); +/* Callback when the request is done */ +typedef svn_error_t * +(*svn_ra_serf__response_done_delegate_t)(serf_request_t *request, + void *done_baton, + apr_pool_t *scratch_pool); + /* Callback for when a request body is needed. */ -/* ### should pass a scratch_pool */ typedef svn_error_t * (*svn_ra_serf__request_body_delegate_t)(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *request_pool); + apr_pool_t *request_pool, + apr_pool_t *scratch_pool); /* Callback for when request headers are needed. */ -/* ### should pass a scratch_pool */ typedef svn_error_t * (*svn_ra_serf__request_header_delegate_t)(serf_bucket_t *headers, void *baton, - apr_pool_t *request_pool); + apr_pool_t *request_pool, + apr_pool_t *scratch_pool); /* Callback for when a response has an error. */ typedef svn_error_t * @@ -413,6 +410,8 @@ typedef struct svn_ra_serf__server_error_t svn_ra_serf__server_error_t; /* * Structure that can be passed to our default handler to guide the * execution of the request through its lifecycle. + * + * Use svn_ra_serf__create_handler() to create instances of this struct. */ typedef struct svn_ra_serf__handler_t { /* The HTTP method string of the request */ @@ -429,8 +428,20 @@ typedef struct svn_ra_serf__handler_t { enabled. */ svn_boolean_t custom_accept_encoding; + /* If TRUE then default DAV: capabilities request headers is not configured + for request. */ + svn_boolean_t no_dav_headers; + + /* If TRUE doesn't fail requests on HTTP error statuses like 405, 408, 500 + (see util.c response_done()) */ + svn_boolean_t no_fail_on_http_failure_status; + + /* If TRUE doesn't fail requests on HTTP redirect statuses like 301, 307 */ + svn_boolean_t no_fail_on_http_redirect_status; + /* Has the request/response been completed? */ svn_boolean_t done; + svn_boolean_t scheduled; /* Is the request scheduled in a context */ /* If we captured an error from the server, then this will be non-NULL. It will be allocated from HANDLER_POOL. */ @@ -447,6 +458,19 @@ typedef struct svn_ra_serf__handler_t { serf_status_line sline; /* The parsed Status-Line */ const char *location; /* The Location: header, if any */ + /* This function and baton pair allows handling the completion of request. + * + * The default handler is responsible for the HTTP failure processing. + * + * If no_fail_on_http_failure_status is not TRUE, then the callback will + * return recorded server errors or if there is none and the http status + * specifies an error returns an error for that. + * + * The default baton is the handler itself. + */ + svn_ra_serf__response_done_delegate_t done_delegate; + void *done_delegate_baton; + /* The handler and baton pair to be executed when a non-recoverable error * is detected. If it is NULL in the presence of an error, an abort() may * be triggered. @@ -490,7 +514,6 @@ typedef struct svn_ra_serf__handler_t { /* Pool for allocating SLINE.REASON and LOCATION. If this pool is NULL, then the requestor does not care about SLINE and LOCATION. */ apr_pool_t *handler_pool; - } svn_ra_serf__handler_t; @@ -511,149 +534,6 @@ svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, */ void svn_ra_serf__request_create(svn_ra_serf__handler_t *handler); -/* XML helper callbacks. */ - -typedef struct svn_ra_serf__xml_state_t { - /* A numeric value that represents the current state in parsing. - * - * Value 0 is reserved for use as the default state. - */ - int current_state; - - /* Private pointer set by the parsing code. */ - void *private; - - /* Allocations should be made in this pool to match the lifetime of the - * state. - */ - apr_pool_t *pool; - - /* The currently-declared namespace for this state. */ - svn_ra_serf__ns_t *ns_list; - - /* Our previous states. */ - struct svn_ra_serf__xml_state_t *prev; -} svn_ra_serf__xml_state_t; - -/* Forward declaration of the XML parser structure. */ -typedef struct svn_ra_serf__xml_parser_t svn_ra_serf__xml_parser_t; - -/* Callback invoked with @a baton by our XML @a parser when an element with - * the @a name containing @a attrs is opened. - */ -typedef svn_error_t * -(*svn_ra_serf__xml_start_element_t)(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - const char **attrs, - apr_pool_t *scratch_pool); - -/* Callback invoked with @a baton by our XML @a parser when an element with - * the @a name is closed. - */ -typedef svn_error_t * -(*svn_ra_serf__xml_end_element_t)(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - apr_pool_t *scratch_pool); - -/* Callback invoked with @a baton by our XML @a parser when a CDATA portion - * of @a data with size @a len is encountered. - * - * This may be invoked multiple times for the same tag. - */ -typedef svn_error_t * -(*svn_ra_serf__xml_cdata_chunk_handler_t)(svn_ra_serf__xml_parser_t *parser, - const char *data, - apr_size_t len, - apr_pool_t *scratch_pool); - -/* - * Helper structure associated with handle_xml_parser handler that will - * specify how an XML response will be processed. - */ -struct svn_ra_serf__xml_parser_t { - /* Temporary allocations should be made in this pool. */ - apr_pool_t *pool; - - /* What kind of response are we parsing? If set, this should typically - define the report name. */ - const char *response_type; - - /* Caller-specific data passed to the start, end, cdata callbacks. */ - void *user_data; - - /* Callback invoked when a tag is opened. */ - svn_ra_serf__xml_start_element_t start; - - /* Callback invoked when a tag is closed. */ - svn_ra_serf__xml_end_element_t end; - - /* Callback invoked when a cdata chunk is received. */ - svn_ra_serf__xml_cdata_chunk_handler_t cdata; - - /* Our associated expat-based XML parser. */ - XML_Parser xmlp; - - /* Our current state. */ - svn_ra_serf__xml_state_t *state; - - /* Our previously used states (will be reused). */ - svn_ra_serf__xml_state_t *free_state; - - /* If non-NULL, this value will be set to TRUE when the response is - * completed. - */ - svn_boolean_t *done; - - /* If non-NULL, when this parser completes, it will add done_item to - * the list. - */ - svn_ra_serf__list_t **done_list; - - /* A pointer to the item that will be inserted into the list upon - * completeion. - */ - svn_ra_serf__list_t *done_item; - - /* If this flag is TRUE, errors during parsing will be ignored. - * - * This is mainly used when we are processing an error XML response to - * avoid infinite loops. - */ - svn_boolean_t ignore_errors; - - /* If an error occurred, this value will be non-NULL. */ - svn_error_t *error; - - /* Deciding whether to pause, or not, is performed within the parsing - callbacks. If a callback decides to set this flag, then the loop - driving the parse (generally, a series of calls to serf_context_run()) - is going to need to coordinate the un-pausing of the parser by - processing pending content. Thus, deciding to pause the parser is a - coordinate effort rather than merely setting this flag. - - When an XML parsing callback sets this flag, note that additional - elements may be parsed (as the current buffer is consumed). At some - point, the flag will be recognized and arriving network content will - be stashed away in the PENDING structure (see below). - - At some point, the controlling loop should clear this value. The - underlying network processing will note the change and begin passing - content into the XML callbacks. - - Note that the controlling loop should also process pending content - since the arriving network content will typically finish first. */ - svn_boolean_t paused; - - /* While the XML parser is paused, content arriving from the server - must be saved locally. We cannot stop reading, or the server may - decide to drop the connection. The content will be stored in memory - up to a certain limit, and will then be spilled over to disk. - - See libsvn_ra_serf/util.c */ - struct svn_ra_serf__pending_t *pending; -}; - - /* v2 of the XML parsing functions */ /* The XML parsing context. */ @@ -719,6 +599,10 @@ typedef svn_error_t * apr_pool_t *scratch_pool); +/* Magic state value for the initial state in a svn_ra_serf__xml_transition_t + table */ +#define XML_STATE_INITIAL 0 + /* State transition table. When the XML Context is constructed, it is in state 0. User states are @@ -727,6 +611,8 @@ typedef svn_error_t * In a list of transitions, use { 0 } to indicate the end. Specifically, the code looks for NS == NULL. + The initial state for each transition table is XML_STATE_INITIAL. + ### more docco */ typedef struct svn_ra_serf__xml_transition_t { @@ -757,6 +643,11 @@ typedef struct svn_ra_serf__xml_transition_t { } svn_ra_serf__xml_transition_t; +/* Constructor for svn_ra_serf__handler_t. Initializes a new handler + with default settings for SESSION. */ +svn_ra_serf__handler_t * +svn_ra_serf__create_handler(svn_ra_serf__session_t *session, + apr_pool_t *result_pool); /* Construct an XML parsing context, based on the TTABLE transition table. As content is parsed, the CLOSED_CB callback will be invoked according @@ -783,18 +674,26 @@ svn_ra_serf__xml_context_create( void *baton, apr_pool_t *result_pool); -/* Destroy all subpools for this structure. */ -void -svn_ra_serf__xml_context_destroy( - svn_ra_serf__xml_context_t *xmlctx); +/* Verifies if the parsing completed successfully and destroys + all subpools. */ +svn_error_t * +svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx); /* Construct a handler with the response function/baton set up to parse a response body using the given XML context. The handler and its internal structures are allocated in RESULT_POOL. + As part of the handling the http status value is compared to 200, or + if EXPECTED_STATUS is not NULL to all the values in EXPECTED_STATUS. + EXPECTED_STATUS is expected to be a list of integers ending with a 0 + that lives at least as long as RESULT_POOL. If the status doesn't + match the request has failed and will be parsed as en error response. + This also initializes HANDLER_POOL to the given RESULT_POOL. */ svn_ra_serf__handler_t * -svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx, +svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session, + svn_ra_serf__xml_context_t *xmlctx, + const int *expected_status, apr_pool_t *result_pool); @@ -833,57 +732,25 @@ svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes, apr_pool_t * svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes); - -/* Any XML parser may be used. When an opening tag is seen, call this - function to feed the information into XMLCTX. */ -svn_error_t * -svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name, - const char *const *attrs); - - -/* When a close tag is seen, call this function to feed the information - into XMLCTX. */ -svn_error_t * -svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name); - - -/* When cdata is parsed by the wrapping XML parser, call this function to - feed the cdata into the XMLCTX. */ -svn_error_t * -svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, - const char *data, - apr_size_t len); - - /* * Parses a server-side error message into a local Subversion error. */ struct svn_ra_serf__server_error_t { - /* Our local representation of the error. */ - svn_error_t *error; - - /* Are we done with the response? */ - svn_boolean_t done; - - /* Have we seen an error tag? */ - svn_boolean_t in_error; + apr_pool_t *pool; - /* Have we seen a HTTP "412 Precondition Failed" error? */ - svn_boolean_t contains_precondition_error; + /* XML parser and namespace used to parse the remote response */ + svn_ra_serf__xml_context_t *xmlctx; - /* Should we be collecting the XML cdata? */ - svn_boolean_t collect_cdata; + svn_ra_serf__response_handler_t response_handler; + void *response_baton; - /* Collected cdata. NULL if cdata not needed. */ - svn_stringbuf_t *cdata; + /* The partial errors to construct the final error from */ + apr_array_header_t *items; - /* XML parser and namespace used to parse the remote response */ - svn_ra_serf__xml_parser_t parser; + /* The hooked handler */ + svn_ra_serf__handler_t *handler; }; - /* * Handler that discards the entire @a response body associated with a * @a request. Implements svn_ra_serf__response_handler_t. @@ -940,20 +807,33 @@ svn_ra_serf__expect_empty_body(serf_request_t *request, /* - * This function will feed the RESPONSE body into XMLP. When parsing is - * completed (i.e. an EOF is received), *DONE is set to TRUE. - * Implements svn_ra_serf__response_handler_t. - * - * If an error occurs during processing RESP_ERR is invoked with the - * RESP_ERR_BATON. - * - * Temporary allocations are made in POOL. + * This function sets up error parsing for an existing request */ svn_error_t * -svn_ra_serf__handle_xml_parser(serf_request_t *request, - serf_bucket_t *response, - void *handler_baton, - apr_pool_t *pool); +svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err, + svn_ra_serf__handler_t *handler, + svn_boolean_t expect_207_only, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* + * Forwards response data to the server error parser + */ +svn_error_t * +svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error, + svn_ra_serf__handler_t *handler, + serf_request_t *request, + serf_bucket_t *response, + apr_status_t *serf_status, + apr_pool_t *scratch_pool); + +/* + * Creates the svn_error_t * instance from the error recorded in + * HANDLER->server_error + */ +svn_error_t * +svn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler, + apr_pool_t *scratch_pool); /* serf_response_handler_t implementation that completely discards * the response. @@ -967,28 +847,6 @@ svn_ra_serf__response_discard_handler(serf_request_t *request, apr_pool_t *pool); -/** XML helper functions. **/ - -/* - * Advance the internal XML @a parser to the @a state. - */ -void -svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser, - int state); - -/* - * Return to the previous internal XML @a parser state. - */ -void -svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser); - - -svn_error_t * -svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, - svn_boolean_t *network_eof, - apr_pool_t *scratch_pool); - - /* * Add the appropriate serf buckets to @a agg_bucket represented by * the XML * @a tag and @a value. @@ -1028,7 +886,7 @@ void svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket, serf_bucket_alloc_t *bkt_alloc, const char *tag, - ...); + ...) SVN_NEEDS_SENTINEL_NULL; /* * Add the appropriate serf buckets to AGG_BUCKET representing xml tag close @@ -1041,6 +899,16 @@ svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket, serf_bucket_alloc_t *bkt_alloc, const char *tag); +/* Add the appropriate serf buckets to AGG_BUCKET representing the XML + * open tag with name TAG, and then immediately closes the tag using the /> + * notation + */ +void +svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket, + serf_bucket_alloc_t *bkt_alloc, + const char *tag, + ...) SVN_NEEDS_SENTINEL_NULL; + /* * Add the appropriate serf buckets to AGG_BUCKET with xml-escaped * version of DATA. @@ -1051,82 +919,55 @@ void svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket, serf_bucket_alloc_t *bkt_alloc, const char *data, apr_size_t len); -/* - * Look up the @a attrs array for namespace definitions and add each one - * to the @a ns_list of namespaces. - * - * New namespaces will be allocated in RESULT_POOL. - */ -void -svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list, - const char *const *attrs, - apr_pool_t *result_pool); -/* - * Look up @a name in the @a ns_list list for previously declared namespace - * definitions. - * - * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple - * representing the expanded name. - */ + +/** PROPFIND-related functions **/ + +/* Removes all non regular properties from PROPS */ void -svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, - const svn_ra_serf__ns_t *ns_list, - const char *name); +svn_ra_serf__keep_only_regular_props(apr_hash_t *props, + apr_pool_t *scratch_pool); -/** PROPFIND-related functions **/ +/* Callback used via svn_ra_serf__deliver_props2 */ +typedef svn_error_t * +(*svn_ra_serf__prop_func_t)(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool); /* - * This function will deliver a PROP_CTX PROPFIND request in the SESS - * serf context for the properties listed in LOOKUP_PROPS at URL for - * DEPTH ("0","1","infinity"). - * - * This function will not block waiting for the response. Callers are - * expected to call svn_ra_serf__wait_for_props(). + * Implementation of svn_ra_serf__prop_func_t that just delivers svn compatible + * properties in the apr_hash_t * that is used as baton. */ svn_error_t * -svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, - apr_hash_t *prop_vals, - svn_ra_serf__session_t *sess, - svn_ra_serf__connection_t *conn, - const char *url, - svn_revnum_t rev, - const char *depth, - const svn_ra_serf__dav_props_t *lookup_props, - svn_ra_serf__list_t **done_list, - apr_pool_t *pool); +svn_ra_serf__deliver_svn_props(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool); /* - * This helper function will block until PROPFIND_HANDLER indicates that is - * done or another error is returned. + * This function will create a handler for a PROPFIND request, which will deliver + * properties to PROP_FUNC() with PROP_BATON for the properties listed in LOOKUP_PROPS + * at URL for DEPTH ("0","1","infinity"). */ svn_error_t * -svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler, - apr_pool_t *scratch_pool); - -/* This is a blocking version of deliver_props. - - The properties are fetched and placed into RESULTS, allocated in - RESULT_POOL. - - ### more docco about the other params. - - Temporary allocations are made in SCRATCH_POOL. -*/ -svn_error_t * -svn_ra_serf__retrieve_props(apr_hash_t **results, - svn_ra_serf__session_t *sess, - svn_ra_serf__connection_t *conn, - const char *url, - svn_revnum_t rev, - const char *depth, - const svn_ra_serf__dav_props_t *props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - -/* Using CONN, fetch the properties specified by WHICH_PROPS using CONN +svn_ra_serf__create_propfind_handler(svn_ra_serf__handler_t **handler, + svn_ra_serf__session_t *session, + const char *path, + svn_revnum_t rev, + const char *depth, + const svn_ra_serf__dav_props_t *find_props, + svn_ra_serf__prop_func_t prop_func, + void *prop_func_baton, + apr_pool_t *result_pool); + + +/* Using SESSION, fetch the properties specified by WHICH_PROPS using CONN for URL at REVISION. The resulting properties are placed into a 2-level hash in RESULTS, mapping NAMESPACE -> hash, which is allocated in RESULT_POOL. @@ -1139,7 +980,7 @@ svn_ra_serf__retrieve_props(apr_hash_t **results, Temporary allocations are made in SCRATCH_POOL. */ svn_error_t * svn_ra_serf__fetch_node_props(apr_hash_t **results, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *url, svn_revnum_t revision, const svn_ra_serf__dav_props_t *which_props, @@ -1147,7 +988,7 @@ svn_ra_serf__fetch_node_props(apr_hash_t **results, apr_pool_t *scratch_pool); -/* Using CONN, fetch a DAV: property from the resource identified by URL +/* Using SESSION, fetch a DAV: property from the resource identified by URL within REVISION. The PROPNAME may be one of: "checked-in" @@ -1161,66 +1002,13 @@ svn_ra_serf__fetch_node_props(apr_hash_t **results, Temporary allocations are made in SCRATCH_POOL. */ svn_error_t * svn_ra_serf__fetch_dav_prop(const char **value, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, const char *url, svn_revnum_t revision, const char *propname, apr_pool_t *result_pool, apr_pool_t *scratch_pool); - -/* Set PROPS for PATH at REV revision with a NS:NAME VAL. - * - * The POOL governs allocation. - */ -void -svn_ra_serf__set_ver_prop(apr_hash_t *props, - const char *path, svn_revnum_t rev, - const char *ns, const char *name, - const svn_string_t *val, apr_pool_t *pool); -#define svn_ra_serf__set_rev_prop svn_ra_serf__set_ver_prop - -/** Property walker functions **/ - -typedef svn_error_t * -(*svn_ra_serf__walker_visitor_t)(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *pool); - -svn_error_t * -svn_ra_serf__walk_all_props(apr_hash_t *props, - const char *name, - svn_revnum_t rev, - svn_ra_serf__walker_visitor_t walker, - void *baton, - apr_pool_t *pool); - - -/* Like walk_all_props(), but a 2-level hash. */ -svn_error_t * -svn_ra_serf__walk_node_props(apr_hash_t *props, - svn_ra_serf__walker_visitor_t walker, - void *baton, - apr_pool_t *scratch_pool); - - -typedef svn_error_t * -(*svn_ra_serf__path_rev_walker_t)(void *baton, - const char *path, apr_ssize_t path_len, - const char *ns, apr_ssize_t ns_len, - const char *name, apr_ssize_t name_len, - const svn_string_t *val, - apr_pool_t *pool); -svn_error_t * -svn_ra_serf__walk_all_paths(apr_hash_t *props, - svn_revnum_t rev, - svn_ra_serf__path_rev_walker_t walker, - void *baton, - apr_pool_t *pool); - - /* Map a property name, as passed over the wire, into its corresponding Subversion-internal name. The returned name will be a static value, or allocated within RESULT_POOL. @@ -1232,75 +1020,6 @@ svn_ra_serf__svnname_from_wirename(const char *ns, const char *name, apr_pool_t *result_pool); - -/* Select the basic revision properties from the set of "all" properties. - Return these in *REVPROPS, allocated from RESULT_POOL. */ -svn_error_t * -svn_ra_serf__select_revprops(apr_hash_t **revprops, - const char *name, - svn_revnum_t rev, - apr_hash_t *all_revprops, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - -/* PROPS is nested hash tables mapping NS -> NAME -> VALUE. - This function takes the NS:NAME:VALUE hashes and flattens them into a set of - names to VALUE. The names are composed of NS:NAME, with specific - rewrite from wire names (DAV) to SVN names. This mapping is managed - by the svn_ra_serf__set_baton_props() function. - - FLAT_PROPS is allocated in RESULT_POOL. - ### right now, we do a shallow copy from PROPS to FLAT_PROPS. therefore, - ### the names and values in PROPS must be in the proper pool. - - Temporary allocations are made in SCRATCH_POOL. */ -svn_error_t * -svn_ra_serf__flatten_props(apr_hash_t **flat_props, - apr_hash_t *props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - -/* Return the property value for PATH at REV revision with a NS:NAME. - * PROPS is a four-level nested hash: (svn_revnum_t => char *path => - * char *ns => char *name => svn_string_t *). */ -const svn_string_t * -svn_ra_serf__get_ver_prop_string(apr_hash_t *props, - const char *path, svn_revnum_t rev, - const char *ns, const char *name); - -/* Same as svn_ra_serf__get_ver_prop_string(), but returns a C string. */ -const char * -svn_ra_serf__get_ver_prop(apr_hash_t *props, - const char *path, svn_revnum_t rev, - const char *ns, const char *name); - -/* Same as svn_ra_serf__get_ver_prop_string(), but for the unknown revision. */ -const svn_string_t * -svn_ra_serf__get_prop_string(apr_hash_t *props, - const char *path, - const char *ns, - const char *name); - -/* Same as svn_ra_serf__get_ver_prop(), but for the unknown revision. */ -const char * -svn_ra_serf__get_prop(apr_hash_t *props, - const char *path, - const char *ns, - const char *name); - -/* Same as svn_ra_serf__set_rev_prop(), but for the unknown revision. */ -void -svn_ra_serf__set_prop(apr_hash_t *props, const char *path, - const char *ns, const char *name, - const svn_string_t *val, apr_pool_t *pool); - -svn_error_t * -svn_ra_serf__get_resource_type(svn_node_kind_t *kind, - apr_hash_t *props); - - /** MERGE-related functions **/ void @@ -1317,9 +1036,7 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, locks set on the paths included in this commit. */ svn_error_t * svn_ra_serf__run_merge(const svn_commit_info_t **commit_info, - int *response_code, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, const char *merge_resource_url, apr_hash_t *lock_tokens, svn_boolean_t keep_locks, @@ -1347,7 +1064,7 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, All temporary allocations will be made in SCRATCH_POOL. */ svn_error_t * svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, apr_pool_t *scratch_pool); @@ -1362,35 +1079,29 @@ svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, All temporary allocations will be made in SCRATCH_POOL. */ svn_error_t * svn_ra_serf__v1_get_activity_collection(const char **activity_url, - svn_ra_serf__connection_t *conn, + svn_ra_serf__session_t *session, apr_pool_t *result_pool, apr_pool_t *scratch_pool); /* Set @a VCC_URL to the default VCC for our repository based on @a * ORIG_PATH for the session @a SESSION, ensuring that the VCC URL and - * repository root URLs are cached in @a SESSION. Use @a CONN for any - * required network communications if it is non-NULL; otherwise use the - * default connection. + * repository root URLs are cached in @a SESSION. * - * All temporary allocations will be made in @a POOL. */ + * All temporary allocations will be made in @a SCRATCH_POOL. */ svn_error_t * svn_ra_serf__discover_vcc(const char **vcc_url, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Set @a REPORT_TARGET to the URI of the resource at which generic - * (path-agnostic) REPORTs should be aimed for @a SESSION. Use @a - * CONN for any required network communications if it is non-NULL; - * otherwise use the default connection. + * (path-agnostic) REPORTs should be aimed for @a SESSION. * * All temporary allocations will be made in @a POOL. */ svn_error_t * svn_ra_serf__report_resource(const char **report_target, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, apr_pool_t *pool); /* Set @a REL_PATH to a path (not URI-encoded) relative to the root of @@ -1402,7 +1113,6 @@ svn_error_t * svn_ra_serf__get_relative_path(const char **rel_path, const char *orig_path, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, apr_pool_t *pool); @@ -1429,11 +1139,9 @@ svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest, The DAV RA provider(s) solve this by generating a URL that is specific to a revision by using a URL into a "baseline collection". - For a specified SESSION, with an optional CONN (if NULL, then the - session's default connection will be used; specifically SESSION->conns[0]), - generate a revision-stable URL for URL at REVISION. If REVISION is - SVN_INVALID_REVNUM, then the stable URL will refer to the youngest - revision at the time this function was called. + For a specified SESSION, generate a revision-stable URL for URL at + REVISION. If REVISION is SVN_INVALID_REVNUM, then the stable URL will + refer to the youngest revision at the time this function was called. If URL is NULL, then the session root will be used. @@ -1452,7 +1160,6 @@ svn_error_t * svn_ra_serf__get_stable_url(const char **stable_url, svn_revnum_t *latest_revnum, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, const char *url, svn_revnum_t revision, apr_pool_t *result_pool, @@ -1461,6 +1168,20 @@ svn_ra_serf__get_stable_url(const char **stable_url, /** RA functions **/ +/* Implements svn_ra__vtable_t.reparent(). */ +svn_error_t * +svn_ra_serf__reparent(svn_ra_session_t *ra_session, + const char *url, + apr_pool_t *pool); + +/* Implements svn_ra__vtable_t.rev_prop(). */ +svn_error_t * +svn_ra_serf__rev_prop(svn_ra_session_t *session, + svn_revnum_t rev, + const char *name, + svn_string_t **value, + apr_pool_t *pool); + /* Implements svn_ra__vtable_t.get_log(). */ svn_error_t * svn_ra_serf__get_log(svn_ra_session_t *session, @@ -1476,6 +1197,22 @@ svn_ra_serf__get_log(svn_ra_session_t *session, void *receiver_baton, apr_pool_t *pool); +/* Implements svn_ra__vtable_t.check_path(). */ +svn_error_t * +svn_ra_serf__check_path(svn_ra_session_t *ra_session, + const char *rel_path, + svn_revnum_t revision, + svn_node_kind_t *kind, + apr_pool_t *pool); + +/* Implements svn_ra__vtable_t.stat(). */ +svn_error_t * +svn_ra_serf__stat(svn_ra_session_t *ra_session, + const char *rel_path, + svn_revnum_t revision, + svn_dirent_t **dirent, + apr_pool_t *pool); + /* Implements svn_ra__vtable_t.get_locations(). */ svn_error_t * svn_ra_serf__get_locations(svn_ra_session_t *session, @@ -1572,7 +1309,12 @@ svn_ra_serf__get_dated_revision(svn_ra_session_t *session, apr_time_t tm, apr_pool_t *pool); -/* Implements svn_ra__vtable_t.get_commit_editor(). */ +/* Implements svn_ra__vtable_t.get_commit_editor(). + * + * Note: Like other commit editors, the returned editor requires that the + * @c copyfrom_path parameter passed to its @c add_file and @c add_directory + * methods is a URL, not a relative path. + */ svn_error_t * svn_ra_serf__get_commit_editor(svn_ra_session_t *session, const svn_delta_editor_t **editor, @@ -1594,6 +1336,17 @@ svn_ra_serf__get_file(svn_ra_session_t *session, apr_hash_t **props, apr_pool_t *pool); +/* Implements svn_ra__vtable_t.get_dir(). */ +svn_error_t * +svn_ra_serf__get_dir(svn_ra_session_t *ra_session, + apr_hash_t **dirents, + svn_revnum_t *fetched_rev, + apr_hash_t **ret_props, + const char *rel_path, + svn_revnum_t revision, + apr_uint32_t dirent_fields, + apr_pool_t *result_pool); + /* Implements svn_ra__vtable_t.change_rev_prop(). */ svn_error_t * svn_ra_serf__change_rev_prop(svn_ra_session_t *session, @@ -1688,7 +1441,8 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session, svn_error_t * svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, const char **corrected_url, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Implements svn_ra__vtable_t.has_capability(). */ svn_error_t * @@ -1745,12 +1499,23 @@ svn_ra_serf__credentials_callback(char **username, char **password, * Convert an HTTP STATUS_CODE resulting from a WebDAV request against * PATH to the relevant error code. Use the response-supplied LOCATION * where it necessary. + * + * Returns SVN_NO_ERROR if sline doesn't specify an error condition */ svn_error_t * svn_ra_serf__error_on_status(serf_status_line sline, const char *path, const char *location); +/** + * Convert an unexpected HTTP STATUS_CODE from a request to the relevant error + * code. Unlike svn_ra_serf__error_on_status() this function creates an error + * for any result + */ +svn_error_t * +svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler); + + /* ###? */ svn_error_t * svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf, @@ -1773,6 +1538,25 @@ svn_ra_serf__wrap_err(apr_status_t status, const char *fmt, ...); +/* Create a bucket that just returns DATA (with length LEN) and then returns + the APR_EAGAIN status */ +serf_bucket_t * +svn_ra_serf__create_bucket_with_eagain(const char *data, + apr_size_t len, + serf_bucket_alloc_t *allocator); + +/* Parse a given URL_STR, fill in all supplied fields of URI + * structure. + * + * This function is a compatibility wrapper around apr_uri_parse(). + * Different apr-util versions set apr_uri_t.path to either NULL or "" + * for root paths, and serf expects to see "/". This function always + * sets URI.path to "/" for these paths. */ +svn_error_t * +svn_ra_serf__uri_parse(apr_uri_t *uri, + const char *url_str, + apr_pool_t *result_pool); + #if defined(SVN_DEBUG) /* Wrapper macros to collect file and line information */ diff --git a/contrib/subversion/subversion/libsvn_ra_serf/replay.c b/contrib/subversion/subversion/libsvn_ra_serf/replay.c index 66e2f584d..8d2da69fa 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/replay.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/replay.c @@ -29,6 +29,7 @@ #include "svn_pools.h" #include "svn_ra.h" #include "svn_dav.h" +#include "svn_hash.h" #include "svn_xml.h" #include "../libsvn_ra/ra_loader.h" #include "svn_config.h" @@ -46,55 +47,93 @@ * This enum represents the current state of our XML parsing. */ typedef enum replay_state_e { - NONE = 0, - REPORT, - OPEN_DIR, - ADD_DIR, - OPEN_FILE, - ADD_FILE, - DELETE_ENTRY, - APPLY_TEXTDELTA, - CHANGE_PROP + INITIAL = XML_STATE_INITIAL, + + REPLAY_REPORT, + REPLAY_TARGET_REVISION, + REPLAY_OPEN_ROOT, + REPLAY_OPEN_DIRECTORY, + REPLAY_OPEN_FILE, + REPLAY_ADD_DIRECTORY, + REPLAY_ADD_FILE, + REPLAY_DELETE_ENTRY, + REPLAY_CLOSE_FILE, + REPLAY_CLOSE_DIRECTORY, + REPLAY_CHANGE_DIRECTORY_PROP, + REPLAY_CHANGE_FILE_PROP, + REPLAY_APPLY_TEXTDELTA } replay_state_e; -typedef struct replay_info_t replay_info_t; +#define S_ SVN_XML_NAMESPACE +static const svn_ra_serf__xml_transition_t replay_ttable[] = { + { INITIAL, S_, "editor-report", REPLAY_REPORT, + FALSE, { NULL }, TRUE }, -struct replay_info_t { - apr_pool_t *pool; + /* Replay just throws every operation as xml element directly + in the replay report, so we can't really use the nice exit + handling of the transition parser to handle clean callbacks */ - void *baton; - svn_stream_t *stream; + { REPLAY_REPORT, S_, "target-revision", REPLAY_TARGET_REVISION, + FALSE, { "rev", NULL }, TRUE }, - replay_info_t *parent; -}; + { REPLAY_REPORT, S_, "open-root", REPLAY_OPEN_ROOT, + FALSE, { "rev", NULL }, TRUE }, -typedef svn_error_t * -(*change_prop_t)(void *baton, - const char *name, - const svn_string_t *value, - apr_pool_t *pool); + { REPLAY_REPORT, S_, "open-directory", REPLAY_OPEN_DIRECTORY, + FALSE, { "name", "rev", NULL }, TRUE }, -typedef struct prop_info_t { - apr_pool_t *pool; + { REPLAY_REPORT, S_, "open-file", REPLAY_OPEN_FILE, + FALSE, { "name", "rev", NULL }, TRUE }, - change_prop_t change; + { REPLAY_REPORT, S_, "add-directory", REPLAY_ADD_DIRECTORY, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE }, - const char *name; - svn_boolean_t del_prop; + { REPLAY_REPORT, S_, "add-file", REPLAY_ADD_FILE, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE }, - svn_stringbuf_t *prop_value; + { REPLAY_REPORT, S_, "delete-entry", REPLAY_DELETE_ENTRY, + FALSE, { "name", "rev", NULL }, TRUE }, - replay_info_t *parent; -} prop_info_t; + { REPLAY_REPORT, S_, "close-file", REPLAY_CLOSE_FILE, + FALSE, { "?checksum", NULL }, TRUE }, -typedef struct replay_context_t { - apr_pool_t *src_rev_pool; - apr_pool_t *dst_rev_pool; + { REPLAY_REPORT, S_, "close-directory", REPLAY_CLOSE_DIRECTORY, + FALSE, { NULL }, TRUE }, - /* Are we done fetching this file? */ - svn_boolean_t done; - svn_ra_serf__list_t **done_list; - svn_ra_serf__list_t done_item; + { REPLAY_REPORT, S_, "change-dir-prop", REPLAY_CHANGE_DIRECTORY_PROP, + TRUE, { "name", "?del", NULL }, TRUE }, + + { REPLAY_REPORT, S_, "change-file-prop", REPLAY_CHANGE_FILE_PROP, + TRUE, { "name", "?del", NULL }, TRUE }, + + { REPLAY_REPORT, S_, "apply-textdelta", REPLAY_APPLY_TEXTDELTA, + FALSE, { "?checksum", NULL }, TRUE }, + + { 0 } +}; + +/* Per directory/file state */ +typedef struct replay_node_t { + apr_pool_t *pool; /* pool allocating this node's data */ + svn_boolean_t file; /* file or dir */ + + void *baton; /* node baton */ + svn_stream_t *stream; /* stream while handling txdata */ + + struct replay_node_t *parent; /* parent node or NULL */ +} replay_node_t; + +/* Per revision replay report state */ +typedef struct revision_report_t { + apr_pool_t *pool; /* per revision pool */ + + struct replay_node_t *current_node; + struct replay_node_t *root_node; + + /* Are we done fetching this file? + Handles book-keeping in multi-report case */ + svn_boolean_t *done; + int *replay_reports; /* NULL or number of outstanding reports */ /* callback to get an editor */ svn_ra_replay_revstart_callback_t revstart_func; @@ -121,479 +160,324 @@ typedef struct replay_context_t { svn_revnum_t revprop_rev; /* Revision properties for this revision. */ - apr_hash_t *revs_props; - apr_hash_t *props; - - /* Keep a reference to the XML parser ctx to report any errors. */ - svn_ra_serf__xml_parser_t *parser_ctx; + apr_hash_t *rev_props; /* Handlers for the PROPFIND and REPORT for the current revision. */ svn_ra_serf__handler_t *propfind_handler; - svn_ra_serf__handler_t *report_handler; - -} replay_context_t; - - -static void * -push_state(svn_ra_serf__xml_parser_t *parser, - replay_context_t *replay_ctx, - replay_state_e state) -{ - svn_ra_serf__xml_push_state(parser, state); - - if (state == OPEN_DIR || state == ADD_DIR || - state == OPEN_FILE || state == ADD_FILE) - { - replay_info_t *info; - apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool); - - info = apr_palloc(pool, sizeof(*info)); - - info->pool = pool; - info->parent = parser->state->private; - info->baton = NULL; - info->stream = NULL; + svn_ra_serf__handler_t *report_handler; /* For done handler */ - parser->state->private = info; - } - else if (state == CHANGE_PROP) - { - prop_info_t *info; - apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool); - - info = apr_pcalloc(pool, sizeof(*info)); - - info->pool = pool; - info->parent = parser->state->private; - info->prop_value = svn_stringbuf_create_empty(pool); - - parser->state->private = info; - } - - return parser->state->private; -} +} revision_report_t; +/* Conforms to svn_ra_serf__xml_opened_t */ static svn_error_t * -start_replay(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - const char **attrs, - apr_pool_t *scratch_pool) +replay_opened(svn_ra_serf__xml_estate_t *xes, + void *baton, + int entered_state, + const svn_ra_serf__dav_props_t *tag, + apr_pool_t *scratch_pool) { - replay_context_t *ctx = parser->user_data; - replay_state_e state; + struct revision_report_t *ctx = baton; - state = parser->state->current_state; - - if (state == NONE && - strcmp(name.name, "editor-report") == 0) + if (entered_state == REPLAY_REPORT) { - push_state(parser, ctx, REPORT); - /* Before we can continue, we need the revision properties. */ SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done); - /* Create a pool for the commit editor. */ - ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool); - - SVN_ERR(svn_ra_serf__select_revprops(&ctx->props, - ctx->revprop_target, - ctx->revprop_rev, - ctx->revs_props, - ctx->dst_rev_pool, - scratch_pool)); + svn_ra_serf__keep_only_regular_props(ctx->rev_props, scratch_pool); if (ctx->revstart_func) { SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton, &ctx->editor, &ctx->editor_baton, - ctx->props, - ctx->dst_rev_pool)); + ctx->rev_props, + ctx->pool)); } } - else if (state == REPORT && - strcmp(name.name, "target-revision") == 0) + else if (entered_state == REPLAY_APPLY_TEXTDELTA) { - const char *rev; - - rev = svn_xml_get_attr_value("rev", attrs); - if (!rev) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in target-revision element")); - } - - SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton, - SVN_STR_TO_REV(rev), - scratch_pool)); + struct replay_node_t *node = ctx->current_node; + apr_hash_t *attrs; + const char *checksum; + svn_txdelta_window_handler_t handler; + void *handler_baton; + + if (! node || ! node->file || node->stream) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); + + /* ### Is there a better way to access a specific attr here? */ + attrs = svn_ra_serf__xml_gather_since(xes, REPLAY_APPLY_TEXTDELTA); + checksum = svn_hash_gets(attrs, "checksum"); + + SVN_ERR(ctx->editor->apply_textdelta(node->baton, checksum, node->pool, + &handler, &handler_baton)); + + if (handler != svn_delta_noop_window_handler) + { + node->stream = svn_base64_decode( + svn_txdelta_parse_svndiff(handler, + handler_baton, + TRUE, + node->pool), + node->pool); + } } - else if (state == REPORT && - strcmp(name.name, "open-root") == 0) - { - const char *rev; - replay_info_t *info; - rev = svn_xml_get_attr_value("rev", attrs); - - if (!rev) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-root element")); - } + return SVN_NO_ERROR; +} - info = push_state(parser, ctx, OPEN_DIR); +/* Conforms to svn_ra_serf__xml_closed_t */ +static svn_error_t * +replay_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) +{ + struct revision_report_t *ctx = baton; - SVN_ERR(ctx->editor->open_root(ctx->editor_baton, - SVN_STR_TO_REV(rev), - ctx->dst_rev_pool, - &info->baton)); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "delete-entry") == 0) + if (leaving_state == REPLAY_REPORT) { - const char *file_name, *rev; - replay_info_t *info; + if (ctx->current_node) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - file_name = svn_xml_get_attr_value("name", attrs); - if (!file_name) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in delete-entry element")); - } - rev = svn_xml_get_attr_value("rev", attrs); - if (!rev) + if (ctx->revfinish_func) { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in delete-entry element")); + SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton, + ctx->editor, ctx->editor_baton, + ctx->rev_props, scratch_pool)); } + } + else if (leaving_state == REPLAY_TARGET_REVISION) + { + const char *revstr = svn_hash_gets(attrs, "rev"); + apr_int64_t rev; - info = push_state(parser, ctx, DELETE_ENTRY); - - SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev), - info->baton, scratch_pool)); - - svn_ra_serf__xml_pop_state(parser); + SVN_ERR(svn_cstring_atoi64(&rev, revstr)); + SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton, + (svn_revnum_t)rev, + scratch_pool)); } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "open-directory") == 0) + else if (leaving_state == REPLAY_OPEN_ROOT) { - const char *rev, *dir_name; - replay_info_t *info; + const char *revstr = svn_hash_gets(attrs, "rev"); + apr_int64_t rev; + apr_pool_t *root_pool = svn_pool_create(ctx->pool); - dir_name = svn_xml_get_attr_value("name", attrs); - if (!dir_name) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in open-directory element")); - } - rev = svn_xml_get_attr_value("rev", attrs); - if (!rev) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-directory element")); - } + if (ctx->current_node || ctx->root_node) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - info = push_state(parser, ctx, OPEN_DIR); + ctx->root_node = apr_pcalloc(root_pool, sizeof(*ctx->root_node)); + ctx->root_node->pool = root_pool; - SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton, - SVN_STR_TO_REV(rev), - ctx->dst_rev_pool, &info->baton)); + ctx->current_node = ctx->root_node; + + SVN_ERR(svn_cstring_atoi64(&rev, revstr)); + SVN_ERR(ctx->editor->open_root(ctx->editor_baton, (svn_revnum_t)rev, + root_pool, + &ctx->current_node->baton)); } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "add-directory") == 0) + else if (leaving_state == REPLAY_OPEN_DIRECTORY + || leaving_state == REPLAY_OPEN_FILE + || leaving_state == REPLAY_ADD_DIRECTORY + || leaving_state == REPLAY_ADD_FILE) { - const char *dir_name, *copyfrom, *copyrev; - svn_revnum_t rev; - replay_info_t *info; - - dir_name = svn_xml_get_attr_value("name", attrs); - if (!dir_name) + struct replay_node_t *node; + apr_pool_t *node_pool; + const char *name = svn_hash_gets(attrs, "name"); + const char *rev_str; + apr_int64_t rev; + + if (!ctx->current_node || ctx->current_node->file) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); + + node_pool = svn_pool_create(ctx->current_node->pool); + node = apr_pcalloc(node_pool, sizeof(*node)); + node->pool = node_pool; + node->parent = ctx->current_node; + + if (leaving_state == REPLAY_OPEN_DIRECTORY + || leaving_state == REPLAY_OPEN_FILE) { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in add-directory element")); + rev_str = svn_hash_gets(attrs, "rev"); } - copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs); - copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs); + else + rev_str = svn_hash_gets(attrs, "copyfrom-rev"); - if (copyrev) - rev = SVN_STR_TO_REV(copyrev); + if (rev_str) + SVN_ERR(svn_cstring_atoi64(&rev, rev_str)); else rev = SVN_INVALID_REVNUM; - info = push_state(parser, ctx, ADD_DIR); - - SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton, - copyfrom, rev, - ctx->dst_rev_pool, &info->baton)); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "close-directory") == 0) - { - replay_info_t *info = parser->state->private; - - SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool)); - - svn_ra_serf__xml_pop_state(parser); - - svn_pool_destroy(info->pool); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "open-file") == 0) - { - const char *file_name, *rev; - replay_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); - if (!file_name) + switch (leaving_state) { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in open-file element")); + case REPLAY_OPEN_DIRECTORY: + node->file = FALSE; + SVN_ERR(ctx->editor->open_directory(name, + ctx->current_node->baton, + (svn_revnum_t)rev, + node->pool, + &node->baton)); + break; + case REPLAY_OPEN_FILE: + node->file = TRUE; + SVN_ERR(ctx->editor->open_file(name, + ctx->current_node->baton, + (svn_revnum_t)rev, + node->pool, + &node->baton)); + break; + case REPLAY_ADD_DIRECTORY: + node->file = FALSE; + SVN_ERR(ctx->editor->add_directory( + name, + ctx->current_node->baton, + SVN_IS_VALID_REVNUM(rev) + ? svn_hash_gets(attrs, "copyfrom-path") + : NULL, + (svn_revnum_t)rev, + node->pool, + &node->baton)); + break; + case REPLAY_ADD_FILE: + node->file = TRUE; + SVN_ERR(ctx->editor->add_file( + name, + ctx->current_node->baton, + SVN_IS_VALID_REVNUM(rev) + ? svn_hash_gets(attrs, "copyfrom-path") + : NULL, + (svn_revnum_t)rev, + node->pool, + &node->baton)); + break; + /* default: unreachable */ } - rev = svn_xml_get_attr_value("rev", attrs); - if (!rev) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-file element")); - } - - info = push_state(parser, ctx, OPEN_FILE); - - SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton, - SVN_STR_TO_REV(rev), - info->pool, &info->baton)); + ctx->current_node = node; } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "add-file") == 0) + else if (leaving_state == REPLAY_CLOSE_FILE) { - const char *file_name, *copyfrom, *copyrev; - svn_revnum_t rev; - replay_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); - if (!file_name) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in add-file element")); - } - copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs); - copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs); - - info = push_state(parser, ctx, ADD_FILE); + struct replay_node_t *node = ctx->current_node; - if (copyrev) - rev = SVN_STR_TO_REV(copyrev); - else - rev = SVN_INVALID_REVNUM; + if (! node || ! node->file) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton, - copyfrom, rev, - info->pool, &info->baton)); + SVN_ERR(ctx->editor->close_file(node->baton, + svn_hash_gets(attrs, "checksum"), + node->pool)); + ctx->current_node = node->parent; + svn_pool_destroy(node->pool); } - else if ((state == OPEN_FILE || state == ADD_FILE) && - strcmp(name.name, "apply-textdelta") == 0) + else if (leaving_state == REPLAY_CLOSE_DIRECTORY) { - const char *checksum; - replay_info_t *info; - svn_txdelta_window_handler_t textdelta; - void *textdelta_baton; - svn_stream_t *delta_stream; - - info = push_state(parser, ctx, APPLY_TEXTDELTA); - - checksum = svn_xml_get_attr_value("checksum", attrs); - if (checksum) - { - checksum = apr_pstrdup(info->pool, checksum); - } + struct replay_node_t *node = ctx->current_node; - SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum, - info->pool, - &textdelta, - &textdelta_baton)); + if (! node || node->file) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton, - TRUE, info->pool); - info->stream = svn_base64_decode(delta_stream, info->pool); + SVN_ERR(ctx->editor->close_directory(node->baton, node->pool)); + ctx->current_node = node->parent; + svn_pool_destroy(node->pool); } - else if ((state == OPEN_FILE || state == ADD_FILE) && - strcmp(name.name, "close-file") == 0) + else if (leaving_state == REPLAY_DELETE_ENTRY) { - replay_info_t *info = parser->state->private; - const char *checksum; - - checksum = svn_xml_get_attr_value("checksum", attrs); - - SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool)); - - svn_ra_serf__xml_pop_state(parser); - - svn_pool_destroy(info->pool); + struct replay_node_t *parent_node = ctx->current_node; + const char *name = svn_hash_gets(attrs, "name"); + const char *revstr = svn_hash_gets(attrs, "rev"); + apr_int64_t rev; + + if (! parent_node || parent_node->file) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); + + SVN_ERR(svn_cstring_atoi64(&rev, revstr)); + SVN_ERR(ctx->editor->delete_entry(name, + (svn_revnum_t)rev, + parent_node->baton, + scratch_pool)); } - else if (((state == OPEN_FILE || state == ADD_FILE) && - strcmp(name.name, "change-file-prop") == 0) || - ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "change-dir-prop") == 0)) + else if (leaving_state == REPLAY_CHANGE_FILE_PROP + || leaving_state == REPLAY_CHANGE_DIRECTORY_PROP) { - const char *prop_name; - prop_info_t *info; + struct replay_node_t *node = ctx->current_node; + const char *name; + const svn_string_t *value; - prop_name = svn_xml_get_attr_value("name", attrs); - if (!prop_name) - { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in %s element"), - name.name); - } - - info = push_state(parser, ctx, CHANGE_PROP); + if (! node || node->file != (leaving_state == REPLAY_CHANGE_FILE_PROP)) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); + name = svn_hash_gets(attrs, "name"); - if (svn_xml_get_attr_value("del", attrs)) - info->del_prop = TRUE; + if (svn_hash_gets(attrs, "del")) + value = NULL; else - info->del_prop = FALSE; + value = svn_base64_decode_string(cdata, scratch_pool); - info->name = apr_pstrdup(info->pool, prop_name); - if (state == OPEN_FILE || state == ADD_FILE) + if (node->file) { - info->change = ctx->editor->change_file_prop; + SVN_ERR(ctx->editor->change_file_prop(node->baton, name, value, + scratch_pool)); } else { - info->change = ctx->editor->change_dir_prop; - } - - } - - return SVN_NO_ERROR; -} - -static svn_error_t * -end_replay(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - apr_pool_t *scratch_pool) -{ - replay_context_t *ctx = parser->user_data; - replay_state_e state; - - state = parser->state->current_state; - - if (state == REPORT && - strcmp(name.name, "editor-report") == 0) - { - svn_ra_serf__xml_pop_state(parser); - if (ctx->revfinish_func) - { - SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton, - ctx->editor, ctx->editor_baton, - ctx->props, - ctx->dst_rev_pool)); + SVN_ERR(ctx->editor->change_dir_prop(node->baton, name, value, + scratch_pool)); } - svn_pool_destroy(ctx->dst_rev_pool); - } - else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0) - { - /* Don't do anything. */ - } - else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0) - { - /* Don't do anything. */ - } - else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0) - { - /* Don't do anything. */ - } - else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0) - { - /* Don't do anything. */ - } - else if ((state == OPEN_FILE || state == ADD_FILE) && - strcmp(name.name, "close-file") == 0) - { - /* Don't do anything. */ - } - else if ((state == APPLY_TEXTDELTA) && - strcmp(name.name, "apply-textdelta") == 0) - { - replay_info_t *info = parser->state->private; - SVN_ERR(svn_stream_close(info->stream)); - svn_ra_serf__xml_pop_state(parser); } - else if (state == CHANGE_PROP && - (strcmp(name.name, "change-file-prop") == 0 || - strcmp(name.name, "change-dir-prop") == 0)) + else if (leaving_state == REPLAY_APPLY_TEXTDELTA) { - prop_info_t *info = parser->state->private; - const svn_string_t *prop_val; + struct replay_node_t *node = ctx->current_node; - if (info->del_prop) - { - prop_val = NULL; - } - else - { - const svn_string_t *morph; - - morph = svn_stringbuf__morph_into_string(info->prop_value); -#ifdef SVN_DEBUG - info->prop_value = NULL; /* morph killed the stringbuf. */ -#endif + if (! node || ! node->file || ! node->stream) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - prop_val = svn_base64_decode_string(morph, info->pool); - } + SVN_ERR(svn_stream_close(node->stream)); - SVN_ERR(info->change(info->parent->baton, info->name, prop_val, - info->parent->pool)); - svn_ra_serf__xml_pop_state(parser); - - svn_pool_destroy(info->pool); + node->stream = NULL; } - return SVN_NO_ERROR; } +/* Conforms to svn_ra_serf__xml_cdata_t */ static svn_error_t * -cdata_replay(svn_ra_serf__xml_parser_t *parser, +replay_cdata(svn_ra_serf__xml_estate_t *xes, + void *baton, + int current_state, const char *data, apr_size_t len, apr_pool_t *scratch_pool) { - replay_context_t *replay_ctx = parser->user_data; - replay_state_e state; - - UNUSED_CTX(replay_ctx); + struct revision_report_t *ctx = baton; - state = parser->state->current_state; - - if (state == APPLY_TEXTDELTA) + if (current_state == REPLAY_APPLY_TEXTDELTA) { - replay_info_t *info = parser->state->private; - apr_size_t written; - - written = len; + struct replay_node_t *node = ctx->current_node; - SVN_ERR(svn_stream_write(info->stream, data, &written)); + if (! node || ! node->file) + return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); - if (written != len) - return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, - _("Error writing stream: unexpected EOF")); - } - else if (state == CHANGE_PROP) - { - prop_info_t *info = parser->state->private; + if (node->stream) + { + apr_size_t written = len; - svn_stringbuf_appendbytes(info->prop_value, data, len); + SVN_ERR(svn_stream_write(node->stream, data, &written)); + if (written != len) + return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, + _("Error writing stream: unexpected EOF")); + } } return SVN_NO_ERROR; } +/* Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_replay_body(serf_bucket_t **bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { - replay_context_t *ctx = baton; + struct revision_report_t *ctx = baton; serf_bucket_t *body_bkt; body_bkt = serf_bucket_aggregate_create(alloc); @@ -601,7 +485,7 @@ create_replay_body(serf_bucket_t **bkt, svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "S:replay-report", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); /* If we have a non-NULL include path, we add it to the body and omit the revision; otherwise, the reverse. */ @@ -616,17 +500,17 @@ create_replay_body(serf_bucket_t **bkt, { svn_ra_serf__add_tag_buckets(body_bkt, "S:revision", - apr_ltoa(ctx->src_rev_pool, ctx->revision), + apr_ltoa(pool, ctx->revision), alloc); } svn_ra_serf__add_tag_buckets(body_bkt, "S:low-water-mark", - apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark), + apr_ltoa(pool, ctx->low_water_mark), alloc); svn_ra_serf__add_tag_buckets(body_bkt, "S:send-deltas", - apr_ltoa(ctx->src_rev_pool, ctx->send_deltas), + apr_ltoa(pool, ctx->send_deltas), alloc); svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report"); @@ -642,65 +526,49 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session, svn_boolean_t send_deltas, const svn_delta_editor_t *editor, void *edit_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { - replay_context_t *replay_ctx; + struct revision_report_t ctx = { NULL }; svn_ra_serf__session_t *session = ra_session->priv; svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_parser_t *parser_ctx; - svn_error_t *err; + svn_ra_serf__xml_context_t *xmlctx; const char *report_target; - SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool)); + SVN_ERR(svn_ra_serf__report_resource(&report_target, session, + scratch_pool)); + + ctx.pool = svn_pool_create(scratch_pool); + ctx.editor = editor; + ctx.editor_baton = edit_baton; + ctx.done = FALSE; + ctx.revision = revision; + ctx.low_water_mark = low_water_mark; + ctx.send_deltas = send_deltas; + ctx.rev_props = apr_hash_make(scratch_pool); - replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx)); - replay_ctx->src_rev_pool = pool; - replay_ctx->editor = editor; - replay_ctx->editor_baton = edit_baton; - replay_ctx->done = FALSE; - replay_ctx->revision = revision; - replay_ctx->low_water_mark = low_water_mark; - replay_ctx->send_deltas = send_deltas; - replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool); + xmlctx = svn_ra_serf__xml_context_create(replay_ttable, + replay_opened, replay_closed, + replay_cdata, + &ctx, + scratch_pool); - handler = apr_pcalloc(pool, sizeof(*handler)); + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, + scratch_pool); - handler->handler_pool = pool; handler->method = "REPORT"; handler->path = session->session_url.path; handler->body_delegate = create_replay_body; - handler->body_delegate_baton = replay_ctx; + handler->body_delegate_baton = &ctx; handler->body_type = "text/xml"; - handler->conn = session->conns[0]; - handler->session = session; - parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); + /* Not setting up done handler as we don't use a global context */ - parser_ctx->pool = pool; - parser_ctx->user_data = replay_ctx; - parser_ctx->start = start_replay; - parser_ctx->end = end_replay; - parser_ctx->cdata = cdata_replay; - parser_ctx->done = &replay_ctx->done; + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); - handler->response_handler = svn_ra_serf__handle_xml_parser; - handler->response_baton = parser_ctx; - - /* This is only needed to handle errors during XML parsing. */ - replay_ctx->parser_ctx = parser_ctx; - replay_ctx->report_handler = handler; /* unused */ - - svn_ra_serf__request_create(handler); - - err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool); - - SVN_ERR(svn_error_compose_create( + return svn_error_trace( svn_ra_serf__error_on_status(handler->sline, handler->path, - handler->location), - err)); - - return SVN_NO_ERROR; + handler->location)); } /* The maximum number of outstanding requests at any time. When this @@ -734,6 +602,33 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session, */ #define MAX_OUTSTANDING_REQUESTS 50 +/* Implements svn_ra_serf__response_done_delegate_t for svn_ra_serf__replay_range */ +static svn_error_t * +replay_done(serf_request_t *request, + void *baton, + apr_pool_t *scratch_pool) +{ + struct revision_report_t *ctx = baton; + svn_ra_serf__handler_t *handler = ctx->report_handler; + + if (handler->server_error) + return svn_ra_serf__server_error_create(handler, scratch_pool); + else if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); + + *ctx->done = TRUE; /* Breaks out svn_ra_serf__context_run_wait */ + + /* Are re replaying multiple revisions? */ + if (ctx->replay_reports) + { + (*ctx->replay_reports)--; + } + + svn_pool_destroy(ctx->pool); /* Destroys handler and request! */ + + return SVN_NO_ERROR; +} + svn_error_t * svn_ra_serf__replay_range(svn_ra_session_t *ra_session, svn_revnum_t start_revision, @@ -743,15 +638,17 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, svn_ra_replay_revstart_callback_t revstart_func, svn_ra_replay_revfinish_callback_t revfinish_func, void *replay_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { svn_ra_serf__session_t *session = ra_session->priv; svn_revnum_t rev = start_revision; const char *report_target; int active_reports = 0; const char *include_path; + svn_boolean_t done; - SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool)); + SVN_ERR(svn_ra_serf__report_resource(&report_target, session, + scratch_pool)); /* Prior to 1.8, mod_dav_svn expect to get replay REPORT requests aimed at the session URL. But that's incorrect -- these reports @@ -774,8 +671,7 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, { SVN_ERR(svn_ra_serf__get_relative_path(&include_path, session->session_url.path, - session, session->conns[0], - pool)); + session, scratch_pool)); } else { @@ -784,10 +680,6 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, while (active_reports || rev <= end_revision) { - svn_ra_serf__list_t *done_list; - svn_ra_serf__list_t *done_reports = NULL; - replay_context_t *replay_ctx; - if (session->cancel_func) SVN_ERR(session->cancel_func(session->cancel_baton)); @@ -795,54 +687,56 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, requests to MAX_OUTSTANDING_REQUESTS. */ if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS) { + struct revision_report_t *rev_ctx; svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_parser_t *parser_ctx; - apr_pool_t *ctx_pool = svn_pool_create(pool); + apr_pool_t *rev_pool = svn_pool_create(scratch_pool); + svn_ra_serf__xml_context_t *xmlctx; const char *replay_target; - replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx)); - replay_ctx->src_rev_pool = ctx_pool; - replay_ctx->revstart_func = revstart_func; - replay_ctx->revfinish_func = revfinish_func; - replay_ctx->replay_baton = replay_baton; - replay_ctx->done = FALSE; - replay_ctx->include_path = include_path; - replay_ctx->revision = rev; - replay_ctx->low_water_mark = low_water_mark; - replay_ctx->send_deltas = send_deltas; - replay_ctx->done_item.data = replay_ctx; + rev_ctx = apr_pcalloc(rev_pool, sizeof(*rev_ctx)); + rev_ctx->pool = rev_pool; + rev_ctx->revstart_func = revstart_func; + rev_ctx->revfinish_func = revfinish_func; + rev_ctx->replay_baton = replay_baton; + rev_ctx->done = &done; + rev_ctx->replay_reports = &active_reports; + rev_ctx->include_path = include_path; + rev_ctx->revision = rev; + rev_ctx->low_water_mark = low_water_mark; + rev_ctx->send_deltas = send_deltas; /* Request all properties of a certain revision. */ - replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool); + rev_ctx->rev_props = apr_hash_make(rev_ctx->pool); if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) { - replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld", - session->rev_stub, rev); - replay_ctx->revprop_rev = SVN_INVALID_REVNUM; + rev_ctx->revprop_target = apr_psprintf(rev_pool, "%s/%ld", + session->rev_stub, rev); + rev_ctx->revprop_rev = SVN_INVALID_REVNUM; } else { - replay_ctx->revprop_target = report_target; - replay_ctx->revprop_rev = rev; + rev_ctx->revprop_target = report_target; + rev_ctx->revprop_rev = rev; } - SVN_ERR(svn_ra_serf__deliver_props(&replay_ctx->propfind_handler, - replay_ctx->revs_props, session, - session->conns[0], - replay_ctx->revprop_target, - replay_ctx->revprop_rev, - "0", all_props, - NULL, - replay_ctx->src_rev_pool)); + SVN_ERR(svn_ra_serf__create_propfind_handler( + &rev_ctx->propfind_handler, + session, + rev_ctx->revprop_target, + rev_ctx->revprop_rev, + "0", all_props, + svn_ra_serf__deliver_svn_props, + rev_ctx->rev_props, + rev_pool)); /* Spin up the serf request for the PROPFIND. */ - svn_ra_serf__request_create(replay_ctx->propfind_handler); + svn_ra_serf__request_create(rev_ctx->propfind_handler); /* Send the replay REPORT request. */ if (session->supports_rev_rsrc_replay) { - replay_target = apr_psprintf(pool, "%s/%ld", + replay_target = apr_psprintf(rev_pool, "%s/%ld", session->rev_stub, rev); } else @@ -850,41 +744,24 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, replay_target = session->session_url.path; } - handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler)); + xmlctx = svn_ra_serf__xml_context_create(replay_ttable, + replay_opened, replay_closed, + replay_cdata, rev_ctx, + rev_pool); + + handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, + rev_pool); - handler->handler_pool = replay_ctx->src_rev_pool; handler->method = "REPORT"; handler->path = replay_target; handler->body_delegate = create_replay_body; - handler->body_delegate_baton = replay_ctx; - handler->conn = session->conns[0]; - handler->session = session; - - parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool, - sizeof(*parser_ctx)); - - /* Setup the XML parser context. - Because we have not one but a list of requests, the 'done' property - on the replay_ctx is not of much use. Instead, use 'done_list'. - On each handled response (succesfully or not), the parser will add - done_item to done_list, so by keeping track of the state of - done_list we know how many requests have been handled completely. - */ - parser_ctx->pool = replay_ctx->src_rev_pool; - parser_ctx->user_data = replay_ctx; - parser_ctx->start = start_replay; - parser_ctx->end = end_replay; - parser_ctx->cdata = cdata_replay; - parser_ctx->done = &replay_ctx->done; - parser_ctx->done_list = &done_reports; - parser_ctx->done_item = &replay_ctx->done_item; - handler->response_handler = svn_ra_serf__handle_xml_parser; - handler->response_baton = parser_ctx; - replay_ctx->report_handler = handler; - - /* This is only needed to handle errors during XML parsing. */ - replay_ctx->parser_ctx = parser_ctx; + handler->body_delegate_baton = rev_ctx; + handler->body_type = "text/xml"; + + handler->done_delegate = replay_done; + handler->done_delegate_baton = rev_ctx; + rev_ctx->report_handler = handler; svn_ra_serf__request_create(handler); rev++; @@ -892,26 +769,12 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session, } /* Run the serf loop. */ - SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool)); - - /* Substract the number of completely handled responses from our - total nr. of open requests', so we'll know when to stop this loop. - Since the message is completely handled, we can destroy its pool. */ - done_list = done_reports; - while (done_list) - { - replay_context_t *ctx = (replay_context_t *)done_list->data; - svn_ra_serf__handler_t *done_handler = ctx->report_handler; - - done_list = done_list->next; - SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline, - done_handler->path, - done_handler->location)); - svn_pool_destroy(ctx->src_rev_pool); - active_reports--; - } + done = FALSE; + SVN_ERR(svn_ra_serf__context_run_wait(&done, session, scratch_pool)); - done_reports = NULL; + /* The done handler of reports decrements active_reports when a report + is done. This same handler reports (fatal) report errors, so we can + just loop here. */ } return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_ra_serf/serf.c b/contrib/subversion/subversion/libsvn_ra_serf/serf.c index 66f996250..3c47d5eae 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/serf.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/serf.c @@ -39,6 +39,7 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_path.h" +#include "svn_props.h" #include "svn_time.h" #include "svn_version.h" @@ -63,7 +64,7 @@ ra_serf_version(void) #define RA_SERF_DESCRIPTION_VER \ N_("Module for accessing a repository via WebDAV protocol using serf.\n" \ - " - using serf %d.%d.%d") + " - using serf %d.%d.%d (compiled with %d.%d.%d)") /* Implements svn_ra__vtable_t.get_description(). */ static const char * @@ -72,7 +73,12 @@ ra_serf_get_description(apr_pool_t *pool) int major, minor, patch; serf_lib_version(&major, &minor, &patch); - return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch); + return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), + major, minor, patch, + SERF_MAJOR_VERSION, + SERF_MINOR_VERSION, + SERF_PATCH_VERSION + ); } /* Implements svn_ra__vtable_t.get_schemes(). */ @@ -144,10 +150,6 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config, runtime configuration variable. */ #define DEFAULT_HTTP_TIMEOUT 600 -/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */ -#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests" - - static svn_error_t * load_config(svn_ra_serf__session_t *session, apr_hash_t *config_hash, @@ -161,6 +163,10 @@ load_config(svn_ra_serf__session_t *session, const char *exceptions; apr_port_t proxy_port; svn_tristate_t chunked_requests; +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + apr_int64_t log_components; + apr_int64_t log_level; +#endif if (config_hash) { @@ -179,17 +185,17 @@ load_config(svn_ra_serf__session_t *session, svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL); - if (session->wc_callbacks->auth_baton) + if (session->auth_baton) { if (config_client) { - svn_auth_set_parameter(session->wc_callbacks->auth_baton, + svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, config_client); } if (config) { - svn_auth_set_parameter(session->wc_callbacks->auth_baton, + svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, config); } @@ -237,18 +243,25 @@ load_config(svn_ra_serf__session_t *session, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); - /* Should we use chunked transfer encoding. */ + /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, SVN_CONFIG_SECTION_GLOBAL, - OPTION_HTTP_CHUNKED_REQUESTS, + SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", svn_tristate_unknown)); - if (config) - server_group = svn_config_find_group(config, - session->session_url.hostname, - SVN_CONFIG_SECTION_GROUPS, pool); - else - server_group = NULL; +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + SVN_ERR(svn_config_get_int64(config, &log_components, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, + SERF_LOGCOMP_NONE)); + SVN_ERR(svn_config_get_int64(config, &log_level, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_SERF_LOG_LEVEL, + SERF_LOG_INFO)); +#endif + + server_group = svn_auth_get_parameter(session->auth_baton, + SVN_AUTH_PARAM_SERVER_GROUP); if (server_group) { @@ -259,9 +272,6 @@ load_config(svn_ra_serf__session_t *session, svn_config_get(config, &timeout_str, server_group, SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str); - svn_auth_set_parameter(session->wc_callbacks->auth_baton, - SVN_AUTH_PARAM_SERVER_GROUP, server_group); - /* Load the group proxy server settings, overriding global settings. We intentionally ignore 'http-proxy-exceptions' here because, well, if this site was an exception, why is @@ -300,12 +310,42 @@ load_config(svn_ra_serf__session_t *session, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, session->max_connections)); - /* Should we use chunked transfer encoding. */ + /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, server_group, - OPTION_HTTP_CHUNKED_REQUESTS, + SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", chunked_requests)); + +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + SVN_ERR(svn_config_get_int64(config, &log_components, + server_group, + SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, + log_components)); + SVN_ERR(svn_config_get_int64(config, &log_level, + server_group, + SVN_CONFIG_OPTION_SERF_LOG_LEVEL, + log_level)); +#endif + } + +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + if (log_components != SERF_LOGCOMP_NONE) + { + serf_log_output_t *output; + apr_status_t status; + + status = serf_logging_create_stream_output(&output, + session->context, + (apr_uint32_t)log_level, + (apr_uint32_t)log_components, + SERF_LOG_DEFAULT_LAYOUT, + stderr, + pool); + + if (!status) + serf_logging_add_output(session->context, output); } +#endif /* Don't allow the http-max-connections value to be larger than our compiled-in limit, or to be too small to operate. Broken @@ -439,8 +479,10 @@ svn_ra_serf__open(svn_ra_session_t *session, const char *session_URL, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_status_t status; svn_ra_serf__session_t *serf_sess; @@ -451,10 +493,15 @@ svn_ra_serf__open(svn_ra_session_t *session, if (corrected_url) *corrected_url = NULL; - serf_sess = apr_pcalloc(pool, sizeof(*serf_sess)); - serf_sess->pool = svn_pool_create(pool); + serf_sess = apr_pcalloc(result_pool, sizeof(*serf_sess)); + serf_sess->pool = result_pool; + if (config) + SVN_ERR(svn_config_copy_config(&serf_sess->config, config, result_pool)); + else + serf_sess->config = NULL; serf_sess->wc_callbacks = callbacks; serf_sess->wc_callback_baton = callback_baton; + serf_sess->auth_baton = auth_baton; serf_sess->progress_func = callbacks->progress_func; serf_sess->progress_baton = callbacks->progress_baton; serf_sess->cancel_func = callbacks->cancel_func; @@ -467,19 +514,8 @@ svn_ra_serf__open(svn_ra_session_t *session, serf_sess->pool)); - status = apr_uri_parse(serf_sess->pool, session_URL, &url); - if (status) - { - return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - _("Illegal URL '%s'"), - session_URL); - } - /* Depending the version of apr-util in use, for root paths url.path - will be NULL or "", where serf requires "/". */ - if (url.path == NULL || url.path[0] == '\0') - { - url.path = apr_pstrdup(serf_sess->pool, "/"); - } + SVN_ERR(svn_ra_serf__uri_parse(&url, session_URL, serf_sess->pool)); + if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); @@ -511,13 +547,16 @@ svn_ra_serf__open(svn_ra_session_t *session, /* create the user agent string */ if (callbacks->get_client_string) - SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool)); + SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, + scratch_pool)); if (client_string) - serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ", - client_string, (char *)NULL); + serf_sess->useragent = apr_pstrcat(result_pool, + get_user_agent_string(scratch_pool), + " ", + client_string, SVN_VA_NULL); else - serf_sess->useragent = get_user_agent_string(pool); + serf_sess->useragent = get_user_agent_string(result_pool); /* go ahead and tell serf about the connection. */ status = @@ -538,7 +577,24 @@ svn_ra_serf__open(svn_ra_session_t *session, session->priv = serf_sess; - err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); + /* The following code explicitly works around a bug in serf <= r2319 / 1.3.8 + where serf doesn't report the request as failed/cancelled when the + authorization request handler fails to handle the request. + + As long as we allocate the request in a subpool of the serf connection + pool, we know that the handler is always cleaned before the connection. + + Luckily our caller now passes us two pools which handle this case. + */ +#if defined(SVN_DEBUG) && !SERF_VERSION_AT_LEAST(1,4,0) + /* Currently ensured by svn_ra_open4(). + If failing causes segfault in basic_tests.py 48, "basic auth test" */ + SVN_ERR_ASSERT((serf_sess->pool != scratch_pool) + && apr_pool_is_ancestor(serf_sess->pool, scratch_pool)); +#endif + + err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, + result_pool, scratch_pool); /* serf should produce a usable error code instead of APR_EGENERAL */ if (err && err->apr_err == APR_EGENERAL) @@ -552,20 +608,188 @@ svn_ra_serf__open(svn_ra_session_t *session, problems in any proxy. */ if ((corrected_url == NULL || *corrected_url == NULL) && serf_sess->detect_chunking && !serf_sess->http10) - SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool)); + SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, scratch_pool)); return SVN_NO_ERROR; } -/* Implements svn_ra__vtable_t.reparent(). */ +/* Implements svn_ra__vtable_t.dup_session */ static svn_error_t * +ra_serf_dup_session(svn_ra_session_t *new_session, + svn_ra_session_t *old_session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *old_sess = old_session->priv; + svn_ra_serf__session_t *new_sess; + apr_status_t status; + + new_sess = apr_pmemdup(result_pool, old_sess, sizeof(*new_sess)); + + new_sess->pool = result_pool; + + if (new_sess->config) + SVN_ERR(svn_config_copy_config(&new_sess->config, new_sess->config, + result_pool)); + + /* max_connections */ + /* using_ssl */ + /* using_compression */ + /* http10 */ + /* using_chunked_requests */ + /* detect_chunking */ + + if (new_sess->useragent) + new_sess->useragent = apr_pstrdup(result_pool, new_sess->useragent); + + if (new_sess->vcc_url) + new_sess->vcc_url = apr_pstrdup(result_pool, new_sess->vcc_url); + + new_sess->auth_state = NULL; + new_sess->auth_attempts = 0; + + /* Callback functions to get info from WC */ + /* wc_callbacks */ + /* wc_callback_baton */ + + /* progress_func */ + /* progress_baton */ + + /* cancel_func */ + /* cancel_baton */ + + /* shim_callbacks */ + + new_sess->pending_error = NULL; + + /* authn_types */ + + /* Keys and values are static */ + if (new_sess->capabilities) + new_sess->capabilities = apr_hash_copy(result_pool, new_sess->capabilities); + + if (new_sess->activity_collection_url) + { + new_sess->activity_collection_url + = apr_pstrdup(result_pool, new_sess->activity_collection_url); + } + + /* using_proxy */ + + if (new_sess->proxy_username) + { + new_sess->proxy_username + = apr_pstrdup(result_pool, new_sess->proxy_username); + } + + if (new_sess->proxy_password) + { + new_sess->proxy_username + = apr_pstrdup(result_pool, new_sess->proxy_password); + } + + new_sess->proxy_auth_attempts = 0; + + /* trust_default_ca */ + + if (new_sess->ssl_authorities) + { + new_sess->ssl_authorities = apr_pstrdup(result_pool, + new_sess->ssl_authorities); + } + + if (new_sess->uuid) + new_sess->uuid = apr_pstrdup(result_pool, new_sess->uuid); + + /* timeout */ + /* supports_deadprop_count */ + + if (new_sess->me_resource) + new_sess->me_resource = apr_pstrdup(result_pool, new_sess->me_resource); + if (new_sess->rev_stub) + new_sess->rev_stub = apr_pstrdup(result_pool, new_sess->rev_stub); + if (new_sess->txn_stub) + new_sess->txn_stub = apr_pstrdup(result_pool, new_sess->txn_stub); + if (new_sess->txn_root_stub) + new_sess->txn_root_stub = apr_pstrdup(result_pool, + new_sess->txn_root_stub); + if (new_sess->vtxn_stub) + new_sess->vtxn_stub = apr_pstrdup(result_pool, new_sess->vtxn_stub); + if (new_sess->vtxn_root_stub) + new_sess->vtxn_root_stub = apr_pstrdup(result_pool, + new_sess->vtxn_root_stub); + + /* Keys and values are static */ + if (new_sess->supported_posts) + new_sess->supported_posts = apr_hash_copy(result_pool, + new_sess->supported_posts); + + /* ### Can we copy this? */ + SVN_ERR(svn_ra_serf__blncache_create(&new_sess->blncache, + new_sess->pool)); + + if (new_sess->server_allows_bulk) + new_sess->server_allows_bulk = apr_pstrdup(result_pool, + new_sess->server_allows_bulk); + + new_sess->repos_root_str = apr_pstrdup(result_pool, + new_sess->repos_root_str); + SVN_ERR(svn_ra_serf__uri_parse(&new_sess->repos_root, + new_sess->repos_root_str, + result_pool)); + + new_sess->session_url_str = apr_pstrdup(result_pool, new_session_url); + + SVN_ERR(svn_ra_serf__uri_parse(&new_sess->session_url, + new_sess->session_url_str, + result_pool)); + + /* svn_boolean_t supports_inline_props */ + /* supports_rev_rsrc_replay */ + + new_sess->context = serf_context_create(result_pool); + + SVN_ERR(load_config(new_sess, old_sess->config, result_pool)); + + new_sess->conns[0] = apr_pcalloc(result_pool, + sizeof(*new_sess->conns[0])); + new_sess->conns[0]->bkt_alloc = + serf_bucket_allocator_create(result_pool, NULL, NULL); + new_sess->conns[0]->session = new_sess; + new_sess->conns[0]->last_status_code = -1; + + /* go ahead and tell serf about the connection. */ + status = + serf_connection_create2(&new_sess->conns[0]->conn, + new_sess->context, + new_sess->session_url, + svn_ra_serf__conn_setup, new_sess->conns[0], + svn_ra_serf__conn_closed, new_sess->conns[0], + result_pool); + if (status) + return svn_ra_serf__wrap_err(status, NULL); + + /* Set the progress callback. */ + serf_context_set_progress_cb(new_sess->context, svn_ra_serf__progress, + new_sess); + + new_sess->num_conns = 1; + new_sess->cur_conn = 0; + + new_session->priv = new_sess; + + return SVN_NO_ERROR; +} + +/* Implements svn_ra__vtable_t.reparent(). */ +svn_error_t * svn_ra_serf__reparent(svn_ra_session_t *ra_session, const char *url, apr_pool_t *pool) { svn_ra_serf__session_t *session = ra_session->priv; apr_uri_t new_url; - apr_status_t status; /* If it's the URL we already have, wave our hands and do nothing. */ if (strcmp(session->session_url_str, url) == 0) @@ -576,7 +800,7 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session, if (!session->repos_root_str) { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); } if (!svn_uri__is_ancestor(session->repos_root_str, url)) @@ -587,25 +811,11 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session, "URL '%s'"), url, session->repos_root_str); } - status = apr_uri_parse(pool, url, &new_url); - if (status) - { - return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - _("Illegal repository URL '%s'"), url); - } + SVN_ERR(svn_ra_serf__uri_parse(&new_url, url, pool)); - /* Depending the version of apr-util in use, for root paths url.path - will be NULL or "", where serf requires "/". */ /* ### Maybe we should use a string buffer for these strings so we ### don't allocate memory in the session on every reparent? */ - if (new_url.path == NULL || new_url.path[0] == '\0') - { - session->session_url.path = apr_pstrdup(session->pool, "/"); - } - else - { - session->session_url.path = apr_pstrdup(session->pool, new_url.path); - } + session->session_url.path = apr_pstrdup(session->pool, new_url.path); session->session_url_str = apr_pstrdup(session->pool, url); return SVN_NO_ERROR; @@ -634,20 +844,24 @@ svn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session, latest_revnum, session, pool)); } -/* Implements svn_ra__vtable_t.rev_proplist(). */ +/* Implementation of svn_ra_serf__rev_proplist(). */ static svn_error_t * -svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, - svn_revnum_t rev, - apr_hash_t **ret_props, - apr_pool_t *pool) +serf__rev_proplist(svn_ra_session_t *ra_session, + svn_revnum_t rev, + const svn_ra_serf__dav_props_t *fetch_props, + apr_hash_t **ret_props, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_serf__session_t *session = ra_session->priv; apr_hash_t *props; const char *propfind_path; + svn_ra_serf__handler_t *handler; if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) { - propfind_path = apr_psprintf(pool, "%s/%ld", session->rev_stub, rev); + propfind_path = apr_psprintf(scratch_pool, "%s/%ld", session->rev_stub, + rev); /* svn_ra_serf__retrieve_props() wants to added the revision as a Label to the PROPFIND, which isn't really necessary when @@ -658,507 +872,79 @@ svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, else { /* Use the VCC as the propfind target path. */ - SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool)); - } - - /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash. */ - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - propfind_path, rev, "0", all_props, - pool, pool)); - - SVN_ERR(svn_ra_serf__select_revprops(ret_props, propfind_path, rev, props, - pool, pool)); - - return SVN_NO_ERROR; -} - -/* Implements svn_ra__vtable_t.rev_prop(). */ -static svn_error_t * -svn_ra_serf__rev_prop(svn_ra_session_t *session, - svn_revnum_t rev, - const char *name, - svn_string_t **value, - apr_pool_t *pool) -{ - apr_hash_t *props; - - SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool)); - - *value = svn_hash_gets(props, name); - - return SVN_NO_ERROR; -} - -static svn_error_t * -fetch_path_props(apr_hash_t **props, - svn_ra_serf__session_t *session, - const char *session_relpath, - svn_revnum_t revision, - const svn_ra_serf__dav_props_t *desired_props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const char *url; - - url = session->session_url.path; - - /* If we have a relative path, append it. */ - if (session_relpath) - url = svn_path_url_add_component2(url, session_relpath, scratch_pool); - - /* If we were given a specific revision, get a URL that refers to that - specific revision (rather than floating with HEAD). */ - if (SVN_IS_VALID_REVNUM(revision)) - { - SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, - session, NULL /* conn */, - url, revision, - scratch_pool, scratch_pool)); - } - - /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant. - Or we started with SVN_INVALID_REVNUM and URL may be floating. */ - SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0], - url, SVN_INVALID_REVNUM, - desired_props, - result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - -/* Implements svn_ra__vtable_t.check_path(). */ -static svn_error_t * -svn_ra_serf__check_path(svn_ra_session_t *ra_session, - const char *rel_path, - svn_revnum_t revision, - svn_node_kind_t *kind, - apr_pool_t *pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_t *props; - - svn_error_t *err = fetch_path_props(&props, session, rel_path, - revision, check_path_props, - pool, pool); - - if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *kind = svn_node_none; + SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, + scratch_pool)); } - else - { - /* Any other error, raise to caller. */ - if (err) - return svn_error_trace(err); - SVN_ERR(svn_ra_serf__get_resource_type(kind, props)); - } - - return SVN_NO_ERROR; -} + props = apr_hash_make(result_pool); + SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, + propfind_path, rev, "0", + fetch_props, + svn_ra_serf__deliver_svn_props, + props, + scratch_pool)); + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); -struct dirent_walker_baton_t { - /* Update the fields in this entry. */ - svn_dirent_t *entry; + svn_ra_serf__keep_only_regular_props(props, scratch_pool); - svn_tristate_t *supports_deadprop_count; - - /* If allocations are necessary, then use this pool. */ - apr_pool_t *result_pool; -}; - -static svn_error_t * -dirent_walker(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - struct dirent_walker_baton_t *dwb = baton; - - if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) - { - dwb->entry->has_props = TRUE; - } - else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) - { - dwb->entry->has_props = TRUE; - } - else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) - { - if(strcmp(name, "deadprop-count") == 0) - { - if (*val->data) - { - apr_int64_t deadprop_count; - SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data)); - dwb->entry->has_props = deadprop_count > 0; - if (dwb->supports_deadprop_count) - *dwb->supports_deadprop_count = svn_tristate_true; - } - else if (dwb->supports_deadprop_count) - *dwb->supports_deadprop_count = svn_tristate_false; - } - } - else if (strcmp(ns, "DAV:") == 0) - { - if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) - { - dwb->entry->created_rev = SVN_STR_TO_REV(val->data); - } - else if (strcmp(name, "creator-displayname") == 0) - { - dwb->entry->last_author = val->data; - } - else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) - { - SVN_ERR(svn_time_from_cstring(&dwb->entry->time, - val->data, - dwb->result_pool)); - } - else if (strcmp(name, "getcontentlength") == 0) - { - /* 'getcontentlength' property is empty for directories. */ - if (val->len) - { - SVN_ERR(svn_cstring_atoi64(&dwb->entry->size, val->data)); - } - } - else if (strcmp(name, "resourcetype") == 0) - { - if (strcmp(val->data, "collection") == 0) - { - dwb->entry->kind = svn_node_dir; - } - else - { - dwb->entry->kind = svn_node_file; - } - } - } + *ret_props = props; return SVN_NO_ERROR; } -struct path_dirent_visitor_t { - apr_hash_t *full_paths; - apr_hash_t *base_paths; - const char *orig_path; - svn_tristate_t supports_deadprop_count; - apr_pool_t *result_pool; -}; - -static svn_error_t * -path_dirent_walker(void *baton, - const char *path, apr_ssize_t path_len, - const char *ns, apr_ssize_t ns_len, - const char *name, apr_ssize_t name_len, - const svn_string_t *val, - apr_pool_t *pool) -{ - struct path_dirent_visitor_t *dirents = baton; - struct dirent_walker_baton_t dwb; - svn_dirent_t *entry; - - /* Skip our original path. */ - if (strcmp(path, dirents->orig_path) == 0) - { - return SVN_NO_ERROR; - } - - entry = apr_hash_get(dirents->full_paths, path, path_len); - - if (!entry) - { - const char *base_name; - - entry = svn_dirent_create(pool); - - apr_hash_set(dirents->full_paths, path, path_len, entry); - - base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool), - pool); - - svn_hash_sets(dirents->base_paths, base_name, entry); - } - - dwb.entry = entry; - dwb.supports_deadprop_count = &dirents->supports_deadprop_count; - dwb.result_pool = dirents->result_pool; - return svn_error_trace(dirent_walker(&dwb, ns, name, val, pool)); -} - -static const svn_ra_serf__dav_props_t * -get_dirent_props(apr_uint32_t dirent_fields, - svn_ra_serf__session_t *session, - apr_pool_t *pool) -{ - svn_ra_serf__dav_props_t *prop; - apr_array_header_t *props = apr_array_make - (pool, 7, sizeof(svn_ra_serf__dav_props_t)); - - if (session->supports_deadprop_count != svn_tristate_false - || ! (dirent_fields & SVN_DIRENT_HAS_PROPS)) - { - if (dirent_fields & SVN_DIRENT_KIND) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "resourcetype"; - } - - if (dirent_fields & SVN_DIRENT_SIZE) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "getcontentlength"; - } - - if (dirent_fields & SVN_DIRENT_HAS_PROPS) - { - prop = apr_array_push(props); - prop->namespace = SVN_DAV_PROP_NS_DAV; - prop->name = "deadprop-count"; - } - - if (dirent_fields & SVN_DIRENT_CREATED_REV) - { - svn_ra_serf__dav_props_t *p = apr_array_push(props); - p->namespace = "DAV:"; - p->name = SVN_DAV__VERSION_NAME; - } - - if (dirent_fields & SVN_DIRENT_TIME) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = SVN_DAV__CREATIONDATE; - } - - if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "creator-displayname"; - } - } - else - { - /* We found an old subversion server that can't handle - the deadprop-count property in the way we expect. - - The neon behavior is to retrieve all properties in this case */ - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "allprop"; - } - - prop = apr_array_push(props); - prop->namespace = NULL; - prop->name = NULL; - - return (svn_ra_serf__dav_props_t *) props->elts; -} - -/* Implements svn_ra__vtable_t.stat(). */ +/* Implements svn_ra__vtable_t.rev_proplist(). */ static svn_error_t * -svn_ra_serf__stat(svn_ra_session_t *ra_session, - const char *rel_path, - svn_revnum_t revision, - svn_dirent_t **dirent, - apr_pool_t *pool) +svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, + svn_revnum_t rev, + apr_hash_t **ret_props, + apr_pool_t *result_pool) { - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_t *props; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); svn_error_t *err; - struct dirent_walker_baton_t dwb; - svn_tristate_t deadprop_count = svn_tristate_unknown; - - err = fetch_path_props(&props, - session, rel_path, revision, - get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool, pool); - if (err) - { - if (err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *dirent = NULL; - return SVN_NO_ERROR; - } - else - return svn_error_trace(err); - } - dwb.entry = svn_dirent_create(pool); - dwb.supports_deadprop_count = &deadprop_count; - dwb.result_pool = pool; - SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); + err = serf__rev_proplist(ra_session, rev, all_props, ret_props, + result_pool, scratch_pool); - if (deadprop_count == svn_tristate_false - && session->supports_deadprop_count == svn_tristate_unknown - && !dwb.entry->has_props) - { - /* We have to requery as the server didn't give us the right - information */ - session->supports_deadprop_count = svn_tristate_false; - - SVN_ERR(fetch_path_props(&props, - session, rel_path, SVN_INVALID_REVNUM, - get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool, pool)); - - SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); - } - - if (deadprop_count != svn_tristate_unknown) - session->supports_deadprop_count = deadprop_count; - - *dirent = dwb.entry; - - return SVN_NO_ERROR; + svn_pool_destroy(scratch_pool); + return svn_error_trace(err); } -/* Reads the 'resourcetype' property from the list PROPS and checks if the - * resource at PATH@REVISION really is a directory. Returns - * SVN_ERR_FS_NOT_DIRECTORY if not. - */ -static svn_error_t * -resource_is_directory(apr_hash_t *props) -{ - svn_node_kind_t kind; - - SVN_ERR(svn_ra_serf__get_resource_type(&kind, props)); - - if (kind != svn_node_dir) - { - return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, - _("Can't get entries of non-directory")); - } - - return SVN_NO_ERROR; -} -/* Implements svn_ra__vtable_t.get_dir(). */ -static svn_error_t * -svn_ra_serf__get_dir(svn_ra_session_t *ra_session, - apr_hash_t **dirents, - svn_revnum_t *fetched_rev, - apr_hash_t **ret_props, - const char *rel_path, - svn_revnum_t revision, - apr_uint32_t dirent_fields, - apr_pool_t *pool) +/* Implements svn_ra__vtable_t.rev_prop(). */ +svn_error_t * +svn_ra_serf__rev_prop(svn_ra_session_t *session, + svn_revnum_t rev, + const char *name, + svn_string_t **value, + apr_pool_t *result_pool) { - svn_ra_serf__session_t *session = ra_session->priv; - const char *path; - - path = session->session_url.path; - - /* If we have a relative path, URI encode and append it. */ - if (rel_path) - { - path = svn_path_url_add_component2(path, rel_path, pool); - } - - /* If the user specified a peg revision other than HEAD, we have to fetch - the baseline collection url for that revision. If not, we can use the - public url. */ - if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) - { - SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev, - session, NULL /* conn */, - path, revision, - pool, pool)); - revision = SVN_INVALID_REVNUM; - } - /* REVISION is always SVN_INVALID_REVNUM */ - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); - - /* If we're asked for children, fetch them now. */ - if (dirents) + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + apr_hash_t *props; + svn_ra_serf__dav_props_t specific_props[2]; + const svn_ra_serf__dav_props_t *fetch_props = all_props; + + /* The DAV propfind doesn't allow property fetches for any property name + as there is no defined way to quote values. If we are just fetching a + "svn:property" we can safely do this. In other cases we just fetch all + revision properties and filter the right one out */ + if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX)-1) == 0 + && !strchr(name + sizeof(SVN_PROP_PREFIX)-1, ':')) { - struct path_dirent_visitor_t dirent_walk; - apr_hash_t *props; - const char *rtype; - - /* Always request node kind to check that path is really a - * directory. - */ - dirent_fields |= SVN_DIRENT_KIND; - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - path, SVN_INVALID_REVNUM, "1", - get_dirent_props(dirent_fields, - session, pool), - pool, pool)); - - /* Check if the path is really a directory. */ - rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype"); - if (rtype == NULL || strcmp(rtype, "collection") != 0) - return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, - _("Can't get entries of non-directory")); - - /* We're going to create two hashes to help the walker along. - * We're going to return the 2nd one back to the caller as it - * will have the basenames it expects. - */ - dirent_walk.full_paths = apr_hash_make(pool); - dirent_walk.base_paths = apr_hash_make(pool); - dirent_walk.orig_path = svn_urlpath__canonicalize(path, pool); - dirent_walk.supports_deadprop_count = svn_tristate_unknown; - dirent_walk.result_pool = pool; - - SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, - path_dirent_walker, &dirent_walk, - pool)); - - if (dirent_walk.supports_deadprop_count == svn_tristate_false - && session->supports_deadprop_count == svn_tristate_unknown - && dirent_fields & SVN_DIRENT_HAS_PROPS) - { - /* We have to requery as the server didn't give us the right - information */ - session->supports_deadprop_count = svn_tristate_false; - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, - session->conns[0], - path, SVN_INVALID_REVNUM, "1", - get_dirent_props(dirent_fields, - session, pool), - pool, pool)); - - apr_hash_clear(dirent_walk.full_paths); - apr_hash_clear(dirent_walk.base_paths); - - SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, - path_dirent_walker, - &dirent_walk, pool)); - } - - *dirents = dirent_walk.base_paths; + specific_props[0].xmlns = SVN_DAV_PROP_NS_SVN; + specific_props[0].name = name + sizeof(SVN_PROP_PREFIX)-1; + specific_props[1].xmlns = NULL; + specific_props[1].name = NULL; - if (dirent_walk.supports_deadprop_count != svn_tristate_unknown) - session->supports_deadprop_count = dirent_walk.supports_deadprop_count; + fetch_props = specific_props; } - /* If we're asked for the directory properties, fetch them too. */ - if (ret_props) - { - apr_hash_t *props; - - SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0], - path, SVN_INVALID_REVNUM, - all_props, - pool, pool)); + SVN_ERR(serf__rev_proplist(session, rev, fetch_props, &props, + result_pool, scratch_pool)); - /* Check if the path is really a directory. */ - SVN_ERR(resource_is_directory(props)); + *value = svn_hash_gets(props, name); - /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props() - ### put them into POOL, so we're okay. */ - SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool)); - } + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } @@ -1173,7 +959,7 @@ svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session, if (!session->repos_root_str) { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); } *url = session->repos_root_str; @@ -1209,7 +995,7 @@ svn_ra_serf__get_uuid(svn_ra_session_t *ra_session, /* We're not interested in vcc_url and relative_url, but this call also stores the repository's uuid in the session. */ - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); if (!session->uuid) { return svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL, @@ -1229,6 +1015,7 @@ static const svn_ra__vtable_t serf_vtable = { ra_serf_get_description, ra_serf_get_schemes, svn_ra_serf__open, + ra_serf_dup_session, svn_ra_serf__reparent, svn_ra_serf__get_session_url, svn_ra_serf__get_latest_revnum, diff --git a/contrib/subversion/subversion/libsvn_ra_serf/stat.c b/contrib/subversion/subversion/libsvn_ra_serf/stat.c new file mode 100644 index 000000000..b6d10c51f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_serf/stat.c @@ -0,0 +1,615 @@ +/* + * stat.c : file and directory stat and read functions + * + * ==================================================================== + * 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 + +#include + +#include "svn_private_config.h" +#include "svn_pools.h" +#include "svn_xml.h" +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_dirent_uri.h" +#include "svn_hash.h" +#include "svn_path.h" +#include "svn_props.h" +#include "svn_time.h" +#include "svn_version.h" + +#include "private/svn_dav_protocol.h" +#include "private/svn_dep_compat.h" +#include "private/svn_fspath.h" + +#include "ra_serf.h" + + + +/* Implements svn_ra__vtable_t.check_path(). */ +svn_error_t * +svn_ra_serf__check_path(svn_ra_session_t *ra_session, + const char *relpath, + svn_revnum_t revision, + svn_node_kind_t *kind, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_hash_t *props; + svn_error_t *err; + const char *url; + + url = session->session_url.path; + + /* If we have a relative path, append it. */ + if (relpath) + url = svn_path_url_add_component2(url, relpath, scratch_pool); + + /* If we were given a specific revision, get a URL that refers to that + specific revision (rather than floating with HEAD). */ + if (SVN_IS_VALID_REVNUM(revision)) + { + SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, + session, + url, revision, + scratch_pool, scratch_pool)); + } + + /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant. + Or we started with SVN_INVALID_REVNUM and URL may be floating. */ + err = svn_ra_serf__fetch_node_props(&props, session, + url, SVN_INVALID_REVNUM, + check_path_props, + scratch_pool, scratch_pool); + + if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *kind = svn_node_none; + } + else + { + apr_hash_t *dav_props; + const char *res_type; + + /* Any other error, raise to caller. */ + SVN_ERR(err); + + dav_props = apr_hash_get(props, "DAV:", 4); + res_type = svn_prop_get_value(dav_props, "resourcetype"); + if (!res_type) + { + /* How did this happen? */ + return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, + _("The PROPFIND response did not include the " + "requested resourcetype value")); + } + + if (strcmp(res_type, "collection") == 0) + *kind = svn_node_dir; + else + *kind = svn_node_file; + } + + return SVN_NO_ERROR; +} + + +/* Baton for fill_dirent_propfunc() */ +struct fill_dirent_baton_t +{ + /* Update the fields in this entry. */ + svn_dirent_t *entry; + + svn_tristate_t *supports_deadprop_count; + + /* If allocations are necessary, then use this pool. */ + apr_pool_t *result_pool; +}; + +/* Implements svn_ra_serf__prop_func_t */ +static svn_error_t * +fill_dirent_propfunc(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *val, + apr_pool_t *scratch_pool) +{ + struct fill_dirent_baton_t *fdb = baton; + + if (strcmp(ns, "DAV:") == 0) + { + if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) + { + apr_int64_t rev; + SVN_ERR(svn_cstring_atoi64(&rev, val->data)); + + fdb->entry->created_rev = (svn_revnum_t)rev; + } + else if (strcmp(name, "creator-displayname") == 0) + { + fdb->entry->last_author = apr_pstrdup(fdb->result_pool, val->data); + } + else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) + { + SVN_ERR(svn_time_from_cstring(&fdb->entry->time, + val->data, + fdb->result_pool)); + } + else if (strcmp(name, "getcontentlength") == 0) + { + /* 'getcontentlength' property is empty for directories. */ + if (val->len) + { + SVN_ERR(svn_cstring_atoi64(&fdb->entry->size, val->data)); + } + } + else if (strcmp(name, "resourcetype") == 0) + { + if (strcmp(val->data, "collection") == 0) + { + fdb->entry->kind = svn_node_dir; + } + else + { + fdb->entry->kind = svn_node_file; + } + } + } + else if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) + { + fdb->entry->has_props = TRUE; + } + else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) + { + fdb->entry->has_props = TRUE; + } + else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) + { + if(strcmp(name, "deadprop-count") == 0) + { + if (*val->data) + { + apr_int64_t deadprop_count; + SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data)); + fdb->entry->has_props = deadprop_count > 0; + if (fdb->supports_deadprop_count) + *fdb->supports_deadprop_count = svn_tristate_true; + } + else if (fdb->supports_deadprop_count) + *fdb->supports_deadprop_count = svn_tristate_false; + } + } + + return SVN_NO_ERROR; +} + +static const svn_ra_serf__dav_props_t * +get_dirent_props(apr_uint32_t dirent_fields, + svn_ra_serf__session_t *session, + apr_pool_t *pool) +{ + svn_ra_serf__dav_props_t *prop; + apr_array_header_t *props = apr_array_make + (pool, 7, sizeof(svn_ra_serf__dav_props_t)); + + if (session->supports_deadprop_count != svn_tristate_false + || ! (dirent_fields & SVN_DIRENT_HAS_PROPS)) + { + if (dirent_fields & SVN_DIRENT_KIND) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "resourcetype"; + } + + if (dirent_fields & SVN_DIRENT_SIZE) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "getcontentlength"; + } + + if (dirent_fields & SVN_DIRENT_HAS_PROPS) + { + prop = apr_array_push(props); + prop->xmlns = SVN_DAV_PROP_NS_DAV; + prop->name = "deadprop-count"; + } + + if (dirent_fields & SVN_DIRENT_CREATED_REV) + { + svn_ra_serf__dav_props_t *p = apr_array_push(props); + p->xmlns = "DAV:"; + p->name = SVN_DAV__VERSION_NAME; + } + + if (dirent_fields & SVN_DIRENT_TIME) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = SVN_DAV__CREATIONDATE; + } + + if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) + { + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "creator-displayname"; + } + } + else + { + /* We found an old subversion server that can't handle + the deadprop-count property in the way we expect. + + The neon behavior is to retrieve all properties in this case */ + prop = apr_array_push(props); + prop->xmlns = "DAV:"; + prop->name = "allprop"; + } + + prop = apr_array_push(props); + prop->xmlns = NULL; + prop->name = NULL; + + return (svn_ra_serf__dav_props_t *) props->elts; +} + +/* Implements svn_ra__vtable_t.stat(). */ +svn_error_t * +svn_ra_serf__stat(svn_ra_session_t *ra_session, + const char *relpath, + svn_revnum_t revision, + svn_dirent_t **dirent, + apr_pool_t *pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + svn_error_t *err; + struct fill_dirent_baton_t fdb; + svn_tristate_t deadprop_count = svn_tristate_unknown; + svn_ra_serf__handler_t *handler; + const char *url; + + url = session->session_url.path; + + /* If we have a relative path, append it. */ + if (relpath) + url = svn_path_url_add_component2(url, relpath, pool); + + /* If we were given a specific revision, get a URL that refers to that + specific revision (rather than floating with HEAD). */ + if (SVN_IS_VALID_REVNUM(revision)) + { + SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, + session, + url, revision, + pool, pool)); + } + + fdb.entry = svn_dirent_create(pool); + fdb.supports_deadprop_count = &deadprop_count; + fdb.result_pool = pool; + + SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, url, + SVN_INVALID_REVNUM, "0", + get_dirent_props(SVN_DIRENT_ALL, + session, + pool), + fill_dirent_propfunc, &fdb, pool)); + + err = svn_ra_serf__context_run_one(handler, pool); + + if (err) + { + if (err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *dirent = NULL; + return SVN_NO_ERROR; + } + else + return svn_error_trace(err); + } + + if (deadprop_count == svn_tristate_false + && session->supports_deadprop_count == svn_tristate_unknown + && !fdb.entry->has_props) + { + /* We have to requery as the server didn't give us the right + information */ + session->supports_deadprop_count = svn_tristate_false; + + /* Run the same handler again */ + SVN_ERR(svn_ra_serf__context_run_one(handler, pool)); + } + + if (deadprop_count != svn_tristate_unknown) + session->supports_deadprop_count = deadprop_count; + + *dirent = fdb.entry; + + return SVN_NO_ERROR; +} + +/* Baton for get_dir_dirents_cb and get_dir_props_cb */ +struct get_dir_baton_t +{ + apr_pool_t *result_pool; + apr_hash_t *dirents; + apr_hash_t *ret_props; + svn_boolean_t is_directory; + svn_tristate_t supports_deadprop_count; + const char *path; +}; + +/* Implements svn_ra_serf__prop_func_t */ +static svn_error_t * +get_dir_dirents_cb(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + struct get_dir_baton_t *db = baton; + const char *relpath; + + relpath = svn_fspath__skip_ancestor(db->path, path); + + if (relpath && relpath[0] != '\0') + { + struct fill_dirent_baton_t fdb; + + relpath = svn_path_uri_decode(relpath, scratch_pool); + fdb.entry = svn_hash_gets(db->dirents, relpath); + + if (!fdb.entry) + { + fdb.entry = svn_dirent_create(db->result_pool); + svn_hash_sets(db->dirents, + apr_pstrdup(db->result_pool, relpath), + fdb.entry); + } + + fdb.result_pool = db->result_pool; + fdb.supports_deadprop_count = &db->supports_deadprop_count; + SVN_ERR(fill_dirent_propfunc(&fdb, path, ns, name, value, scratch_pool)); + } + else if (relpath && !db->is_directory) + { + if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0) + { + if (strcmp(value->data, "collection") != 0) + { + /* Tell a lie to exit early */ + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get properties of non-directory")); + } + else + db->is_directory = TRUE; + } + } + + return SVN_NO_ERROR; +} + +/* Implements svn_ra_serf__prop_func */ +static svn_error_t * +get_dir_props_cb(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *value, + apr_pool_t *scratch_pool) +{ + struct get_dir_baton_t *db = baton; + const char *propname; + + propname = svn_ra_serf__svnname_from_wirename(ns, name, db->result_pool); + if (propname) + { + svn_hash_sets(db->ret_props, propname, + svn_string_dup(value, db->result_pool)); + return SVN_NO_ERROR; + } + + if (!db->is_directory) + { + if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0) + { + if (strcmp(value->data, "collection") != 0) + { + /* Tell a lie to exit early */ + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get properties of non-directory")); + } + else + db->is_directory = TRUE; + } + } + + return SVN_NO_ERROR; +} + +/* Implements svn_ra__vtable_t.get_dir(). */ +svn_error_t * +svn_ra_serf__get_dir(svn_ra_session_t *ra_session, + apr_hash_t **dirents, + svn_revnum_t *fetched_rev, + apr_hash_t **ret_props, + const char *rel_path, + svn_revnum_t revision, + apr_uint32_t dirent_fields, + apr_pool_t *result_pool) +{ + svn_ra_serf__session_t *session = ra_session->priv; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + svn_ra_serf__handler_t *dirent_handler = NULL; + svn_ra_serf__handler_t *props_handler = NULL; + const char *path; + struct get_dir_baton_t gdb; + svn_error_t *err = SVN_NO_ERROR; + + gdb.result_pool = result_pool; + gdb.is_directory = FALSE; + gdb.supports_deadprop_count = svn_tristate_unknown; + + path = session->session_url.path; + + /* If we have a relative path, URI encode and append it. */ + if (rel_path) + { + path = svn_path_url_add_component2(path, rel_path, scratch_pool); + } + + /* If the user specified a peg revision other than HEAD, we have to fetch + the baseline collection url for that revision. If not, we can use the + public url. */ + if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) + { + SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev, + session, + path, revision, + scratch_pool, scratch_pool)); + revision = SVN_INVALID_REVNUM; + } + /* REVISION is always SVN_INVALID_REVNUM */ + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); + + gdb.path = path; + + /* If we're asked for children, fetch them now. */ + if (dirents) + { + /* Always request node kind to check that path is really a + * directory. */ + if (!ret_props) + dirent_fields |= SVN_DIRENT_KIND; + + gdb.dirents = apr_hash_make(result_pool); + + SVN_ERR(svn_ra_serf__create_propfind_handler( + &dirent_handler, session, + path, SVN_INVALID_REVNUM, "1", + get_dirent_props(dirent_fields, + session, + scratch_pool), + get_dir_dirents_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(dirent_handler); + } + else + gdb.dirents = NULL; + + if (ret_props) + { + gdb.ret_props = apr_hash_make(result_pool); + SVN_ERR(svn_ra_serf__create_propfind_handler( + &props_handler, session, + path, SVN_INVALID_REVNUM, "0", + all_props, + get_dir_props_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(props_handler); + } + else + gdb.ret_props = NULL; + + if (dirent_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&dirent_handler->done, + session, + scratch_pool)); + + if (err) + { + svn_pool_clear(scratch_pool); /* Unregisters outstanding requests */ + return err; + } + + if (gdb.supports_deadprop_count == svn_tristate_false + && session->supports_deadprop_count == svn_tristate_unknown + && dirent_fields & SVN_DIRENT_HAS_PROPS) + { + /* We have to requery as the server didn't give us the right + information */ + session->supports_deadprop_count = svn_tristate_false; + + apr_hash_clear(gdb.dirents); + + SVN_ERR(svn_ra_serf__create_propfind_handler( + &dirent_handler, session, + path, SVN_INVALID_REVNUM, "1", + get_dirent_props(dirent_fields, + session, + scratch_pool), + get_dir_dirents_cb, &gdb, + scratch_pool)); + + svn_ra_serf__request_create(dirent_handler); + } + } + + if (props_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&props_handler->done, + session, + scratch_pool)); + } + + /* And dirent again for the case when we had to send the request again */ + if (! err && dirent_handler) + { + err = svn_error_trace( + svn_ra_serf__context_run_wait(&dirent_handler->done, + session, + scratch_pool)); + } + + if (!err && gdb.supports_deadprop_count != svn_tristate_unknown) + session->supports_deadprop_count = gdb.supports_deadprop_count; + + svn_pool_destroy(scratch_pool); /* Unregisters outstanding requests */ + + SVN_ERR(err); + + if (!gdb.is_directory) + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, + _("Can't get entries of non-directory")); + + if (ret_props) + *ret_props = gdb.ret_props; + + if (dirents) + *dirents = gdb.dirents; + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/update.c b/contrib/subversion/subversion/libsvn_ra_serf/update.c index 88488ff20..8313af019 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/update.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/update.c @@ -49,6 +49,7 @@ #include "ra_serf.h" #include "../libsvn_ra/ra_loader.h" + /* * This enum represents the current state of our XML parsing for a REPORT. @@ -63,23 +64,180 @@ * the tag is 'closed', the pool will be reused. */ typedef enum report_state_e { - NONE = 0, - INITIAL = 0, - UPDATE_REPORT, - TARGET_REVISION, - OPEN_DIR, - ADD_DIR, - ABSENT_DIR, - OPEN_FILE, - ADD_FILE, - ABSENT_FILE, - PROP, - IGNORE_PROP_NAME, - NEED_PROP_NAME, - TXDELTA + INITIAL = XML_STATE_INITIAL /* = 0 */, + UPDATE_REPORT, + TARGET_REVISION, + + OPEN_DIR, + ADD_DIR, + + OPEN_FILE, + ADD_FILE, + + DELETE_ENTRY, + ABSENT_DIR, + ABSENT_FILE, + + SET_PROP, + REMOVE_PROP, + + PROP, + + FETCH_FILE, + FETCH_PROPS, + TXDELTA, + + CHECKED_IN, + CHECKED_IN_HREF, + + MD5_CHECKSUM, + + VERSION_NAME, + CREATIONDATE, + CREATOR_DISPLAYNAME } report_state_e; +#define D_ "DAV:" +#define S_ SVN_XML_NAMESPACE +#define V_ SVN_DAV_PROP_NS_DAV +static const svn_ra_serf__xml_transition_t update_ttable[] = { + { INITIAL, S_, "update-report", UPDATE_REPORT, + FALSE, { "?inline-props", "?send-all", NULL }, TRUE }, + + { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION, + FALSE, { "rev", NULL }, TRUE }, + + { UPDATE_REPORT, S_, "open-directory", OPEN_DIR, + FALSE, { "rev", NULL }, TRUE }, + + { OPEN_DIR, S_, "open-directory", OPEN_DIR, + FALSE, { "rev", "name", NULL }, TRUE }, + + { ADD_DIR, S_, "open-directory", OPEN_DIR, + FALSE, { "rev", "name", NULL }, TRUE }, + + { OPEN_DIR, S_, "add-directory", ADD_DIR, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/ + NULL }, TRUE }, + + { ADD_DIR, S_, "add-directory", ADD_DIR, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/ + NULL }, TRUE }, + + { OPEN_DIR, S_, "open-file", OPEN_FILE, + FALSE, { "rev", "name", NULL }, TRUE }, + + { ADD_DIR, S_, "open-file", OPEN_FILE, + FALSE, { "rev", "name", NULL }, TRUE }, + + { OPEN_DIR, S_, "add-file", ADD_FILE, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", + "?sha1-checksum", NULL }, TRUE }, + + { ADD_DIR, S_, "add-file", ADD_FILE, + FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", + "?sha1-checksum", NULL }, TRUE }, + + { OPEN_DIR, S_, "delete-entry", DELETE_ENTRY, + FALSE, { "?rev", "name", NULL }, TRUE }, + + { ADD_DIR, S_, "delete-entry", DELETE_ENTRY, + FALSE, { "?rev", "name", NULL }, TRUE }, + + { OPEN_DIR, S_, "absent-directory", ABSENT_DIR, + FALSE, { "name", NULL }, TRUE }, + + { ADD_DIR, S_, "absent-directory", ABSENT_DIR, + FALSE, { "name", NULL }, TRUE }, + + { OPEN_DIR, S_, "absent-file", ABSENT_FILE, + FALSE, { "name", NULL }, TRUE }, + + { ADD_DIR, S_, "absent-file", ABSENT_FILE, + FALSE, { "name", NULL }, TRUE }, + + + { OPEN_DIR, D_, "checked-in", CHECKED_IN, + FALSE, { NULL }, FALSE }, + + { ADD_DIR, D_, "checked-in", CHECKED_IN, + FALSE, { NULL }, FALSE }, + + { OPEN_FILE, D_, "checked-in", CHECKED_IN, + FALSE, { NULL }, FALSE }, + + { ADD_FILE, D_, "checked-in", CHECKED_IN, + FALSE, { NULL }, FALSE }, + + + { OPEN_DIR, S_, "set-prop", SET_PROP, + TRUE, { "name", "?encoding", NULL }, TRUE }, + + { ADD_DIR, S_, "set-prop", SET_PROP, + TRUE, { "name", "?encoding", NULL }, TRUE }, + + { OPEN_FILE, S_, "set-prop", SET_PROP, + TRUE, { "name", "?encoding", NULL }, TRUE }, + + { ADD_FILE, S_, "set-prop", SET_PROP, + TRUE, { "name", "?encoding", NULL }, TRUE }, + + + { OPEN_DIR, S_, "remove-prop", REMOVE_PROP, + TRUE, { "name", NULL }, TRUE }, + + { ADD_DIR, S_, "remove-prop", REMOVE_PROP, + TRUE, { "name", NULL }, TRUE }, + + { OPEN_FILE, S_, "remove-prop", REMOVE_PROP, + TRUE, { "name", NULL }, TRUE }, + + { ADD_FILE, S_, "remove-prop", REMOVE_PROP, + TRUE, { "name", NULL }, TRUE }, + + { OPEN_FILE, S_, "prop", PROP, + FALSE, { NULL }, FALSE }, + { OPEN_DIR, S_, "prop", PROP, + FALSE, { NULL }, FALSE }, + { ADD_FILE, S_, "prop", PROP, + FALSE, { NULL }, FALSE }, + { ADD_DIR, S_, "prop", PROP, + FALSE, { NULL }, FALSE }, + + { OPEN_FILE, S_, "txdelta", TXDELTA, + FALSE, { "?base-checksum" }, TRUE }, + + { ADD_FILE, S_, "txdelta", TXDELTA, + FALSE, { "?base-checksum" }, TRUE }, + + { OPEN_FILE, S_, "fetch-file", FETCH_FILE, + FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE}, + + { ADD_FILE, S_, "fetch-file", FETCH_FILE, + FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE }, + + { CHECKED_IN, D_, "href", CHECKED_IN_HREF, + TRUE, { NULL }, TRUE }, + + { PROP, V_, "md5-checksum", MD5_CHECKSUM, + TRUE, { NULL }, TRUE }, + + /* These are only reported for <= 1.6.x mod_dav_svn */ + { OPEN_DIR, S_, "fetch-props", FETCH_PROPS, + FALSE, { NULL }, FALSE }, + { OPEN_FILE, S_, "fetch-props", FETCH_PROPS, + FALSE, { NULL }, FALSE }, + + { PROP, D_, "version-name", VERSION_NAME, + TRUE, { NULL }, TRUE }, + { PROP, D_, "creationdate", CREATIONDATE, + TRUE, { NULL }, TRUE }, + { PROP, D_, "creator-displayname", CREATOR_DISPLAYNAME, + TRUE, { NULL }, TRUE }, + { 0 } +}; + /* While we process the REPORT response, we will queue up GET and PROPFIND requests. For a very large checkout, it is very easy to queue requests faster than they are resolved. Thus, we need to pause the XML processing @@ -97,175 +255,120 @@ typedef enum report_state_e { #define REQUEST_COUNT_TO_PAUSE 50 #define REQUEST_COUNT_TO_RESUME 40 +#define SPILLBUF_BLOCKSIZE 4096 +#define SPILLBUF_MAXBUFFSIZE 131072 + +#define PARSE_CHUNK_SIZE 8000 /* Copied from xml.c ### Needs tuning */ /* Forward-declare our report context. */ typedef struct report_context_t report_context_t; - +typedef struct body_create_baton_t body_create_baton_t; /* * This structure represents the information for a directory. */ -typedef struct report_dir_t +typedef struct dir_baton_t { - /* Our parent directory. - * - * This value is NULL when we are the root. - */ - struct report_dir_t *parent_dir; + struct dir_baton_t *parent_dir; /* NULL when root */ - apr_pool_t *pool; + apr_pool_t *pool; /* Subpool for this directory */ /* Pointer back to our original report context. */ - report_context_t *report_context; + report_context_t *ctx; - /* Our name sans any parents. */ - const char *base_name; - - /* the expanded directory name (including all parent names) */ - const char *name; + const char *relpath; /* session relative path */ + const char *base_name; /* Name of item "" for root */ /* the canonical url for this directory after updating. (received) */ const char *url; - /* The original repos_relpath of this url (from the working copy) - or NULL if the repos_relpath can be calculated from the edit root. */ + /* The original repos_relpath of this url (via the reporter) + directly, or via an ancestor. */ const char *repos_relpath; - /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */ - svn_revnum_t base_rev; + svn_revnum_t base_rev; /* base revision or NULL for Add */ + + const char *copyfrom_path; /* NULL for open */ + svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */ /* controlling dir baton - this is only created in ensure_dir_opened() */ + svn_boolean_t dir_opened; void *dir_baton; - apr_pool_t *dir_baton_pool; /* How many references to this directory do we still have open? */ apr_size_t ref_count; - /* Namespace list allocated out of this ->pool. */ - svn_ra_serf__ns_t *ns_list; - - /* hashtable for all of the properties (shared within a dir) */ - apr_hash_t *props; - - /* hashtable for all to-be-removed properties (shared within a dir) */ - apr_hash_t *removed_props; - - /* The propfind request for our current directory */ + svn_boolean_t fetch_props; /* Use PROPFIND request? */ svn_ra_serf__handler_t *propfind_handler; + apr_hash_t *remove_props; - /* Has the server told us to fetch the dir props? */ - svn_boolean_t fetch_props; - - /* Have we closed the directory tag (meaning no more additions)? */ - svn_boolean_t tag_closed; - - /* The children of this directory */ - struct report_dir_t *children; - - /* The next sibling of this directory */ - struct report_dir_t *sibling; -} report_dir_t; +} dir_baton_t; /* - * This structure represents the information for a file. - * - * A directory may have a report_info_t associated with it as well. - * - * This structure is created as we parse the REPORT response and - * once the element is completed, we create a report_fetch_t structure - * to give to serf to retrieve this file. - */ -typedef struct report_info_t +* This structure represents the information for a file. +* +* This structure is created as we parse the REPORT response and +* once the element is completed, we may create a fetch_ctx_t structure +* to give to serf to retrieve this file. +*/ +typedef struct file_baton_t { - apr_pool_t *pool; - - /* The enclosing directory. - * - * If this structure refers to a directory, the dir it points to will be - * itself. - */ - report_dir_t *dir; + dir_baton_t *parent_dir; /* The parent */ + apr_pool_t *pool; /* Subpool for this file*/ - /* Our name sans any directory info. */ + const char *relpath; /* session relative path */ const char *base_name; - /* the expanded file name (including all parent directory names) */ - const char *name; - - /* the canonical url for this file. */ + /* the canonical url for this directory after updating. (received) */ const char *url; + /* The original repos_relpath of this url as reported. */ + const char *repos_relpath; + /* lock token, if we had one to start off with. */ const char *lock_token; - /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */ - svn_revnum_t base_rev; - - /* our delta base, if present (NULL if we're adding the file) */ - const char *delta_base; + svn_revnum_t base_rev; /* SVN_INVALID_REVNUM for Add */ - /* Path of original item if add with history */ - const char *copyfrom_path; + const char *copyfrom_path; /* NULL for open */ + svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */ - /* Revision of original item if add with history */ - svn_revnum_t copyfrom_rev; + /* controlling dir baton - this is only created in ensure_file_opened() */ + svn_boolean_t file_opened; + void *file_baton; - /* The propfind request for our current file (if present) */ + svn_boolean_t fetch_props; /* Use PROPFIND request? */ svn_ra_serf__handler_t *propfind_handler; - - /* Has the server told us to fetch the file props? */ - svn_boolean_t fetch_props; + svn_boolean_t found_lock_prop; + apr_hash_t *remove_props; /* Has the server told us to go fetch - only valid if we had it already */ svn_boolean_t fetch_file; - /* The properties for this file */ - apr_hash_t *props; + /* controlling file_baton and textdelta handler */ + svn_txdelta_window_handler_t txdelta; + void *txdelta_baton; - /* pool passed to update->add_file, etc. */ - apr_pool_t *editor_pool; + svn_checksum_t *base_md5_checksum; + svn_checksum_t *final_md5_checksum; + svn_checksum_t *final_sha1_checksum; - /* controlling file_baton and textdelta handler */ - void *file_baton; - const char *base_checksum; - const char *final_sha1_checksum; - svn_txdelta_window_handler_t textdelta; - void *textdelta_baton; - svn_stream_t *svndiff_decoder; - svn_stream_t *base64_decoder; - - /* Checksum for close_file */ - const char *final_checksum; - - /* Stream containing file contents already cached in the working - copy (which may be used to avoid a GET request for the same). */ - svn_stream_t *cached_contents; - - /* temporary property for this file which is currently being parsed - * It will eventually be stored in our parent directory's property hash. - */ - const char *prop_ns; - const char *prop_name; - svn_stringbuf_t *prop_value; - const char *prop_encoding; -} report_info_t; + svn_stream_t *txdelta_stream; /* Stream that feeds windows when + written to within txdelta*/ +} file_baton_t; /* * This structure represents a single request to GET (fetch) a file with * its associated Serf session/connection. */ -typedef struct report_fetch_t { +typedef struct fetch_ctx_t { /* The handler representing this particular fetch. */ svn_ra_serf__handler_t *handler; - /* The session we should use to fetch the file. */ - svn_ra_serf__session_t *sess; - - /* The connection we should use to fetch file. */ - svn_ra_serf__connection_t *conn; + svn_boolean_t using_compression; /* Stores the information for the file we want to fetch. */ - report_info_t *info; + file_baton_t *file; /* Have we read our response headers yet? */ svn_boolean_t read_headers; @@ -279,22 +382,13 @@ typedef struct report_fetch_t { /* This is the amount of data that we have read so far. */ apr_off_t read_size; - /* If we're receiving an svndiff, this will be non-NULL. */ - svn_stream_t *delta_stream; - /* If we're writing this file to a stream, this will be non-NULL. */ - svn_stream_t *target_stream; - - /* Are we done fetching this file? */ - svn_boolean_t done; + svn_stream_t *result_stream; - /* Discard the rest of the content? */ - svn_boolean_t discard; - - svn_ra_serf__list_t **done_list; - svn_ra_serf__list_t done_item; + /* The base-rev header */ + const char *delta_base; -} report_fetch_t; +} fetch_ctx_t; /* * The master structure for a REPORT request and response. @@ -303,7 +397,6 @@ struct report_context_t { apr_pool_t *pool; svn_ra_serf__session_t *sess; - svn_ra_serf__connection_t *conn; /* Source path and destination path */ const char *source; @@ -315,6 +408,10 @@ struct report_context_t { /* What is the target revision that we want for this REPORT? */ svn_revnum_t target_rev; + /* Where are we (used while parsing) */ + dir_baton_t *cur_dir; + file_baton_t *cur_file; + /* Have we been asked to ignore ancestry or textdeltas? */ svn_boolean_t ignore_ancestry; svn_boolean_t text_deltas; @@ -332,180 +429,315 @@ struct report_context_t { /* Path -> const char *repos_relpath mapping */ apr_hash_t *switched_paths; - /* Boolean indicating whether "" is switched. - (This indicates that the we are updating a single file) */ - svn_boolean_t root_is_switched; - /* Our master update editor and baton. */ - const svn_delta_editor_t *update_editor; - void *update_baton; + const svn_delta_editor_t *editor; + void *editor_baton; /* The file holding request body for the REPORT. * * ### todo: It will be better for performance to store small * request bodies (like 4k) in memory and bigger bodies on disk. */ - apr_file_t *body_file; - - /* root directory object */ - report_dir_t *root_dir; + svn_stream_t *body_template; + body_create_baton_t *body; /* number of pending GET requests */ unsigned int num_active_fetches; - /* completed fetches (contains report_fetch_t) */ - svn_ra_serf__list_t *done_fetches; - /* number of pending PROPFIND requests */ unsigned int num_active_propfinds; - /* completed PROPFIND requests (contains svn_ra_serf__handler_t) */ - svn_ra_serf__list_t *done_propfinds; - svn_ra_serf__list_t *done_dir_propfinds; - - /* list of outstanding prop changes (contains report_dir_t) */ - svn_ra_serf__list_t *active_dir_propfinds; - - /* list of files that only have prop changes (contains report_info_t) */ - svn_ra_serf__list_t *file_propchanges_only; - - /* The path to the REPORT request */ - const char *path; - /* Are we done parsing the REPORT response? */ svn_boolean_t done; /* Did we receive all data from the network? */ svn_boolean_t report_received; - /* Did we get a complete (non-truncated) report? */ - svn_boolean_t report_completed; - - /* The XML parser context for the REPORT response. */ - svn_ra_serf__xml_parser_t *parser_ctx; - /* Did we close the root directory? */ svn_boolean_t closed_root; }; +/* Baton for collecting REPORT body. Depending on the size this + work is backed by a memory buffer (via serf buckets) or by + a file */ +struct body_create_baton_t +{ + apr_pool_t *result_pool; + apr_size_t total_bytes; -#ifdef NOT_USED_YET + apr_pool_t *scratch_pool; -#define D_ "DAV:" -#define S_ SVN_XML_NAMESPACE -static const svn_ra_serf__xml_transition_t update_ttable[] = { - { INITIAL, S_, "update-report", UPDATE_REPORT, - FALSE, { NULL }, FALSE }, + serf_bucket_alloc_t *alloc; + serf_bucket_t *collect_bucket; - { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION, - FALSE, { "rev", NULL }, TRUE }, + const void *all_data; + apr_file_t *file; +}; - { UPDATE_REPORT, S_, "open-directory", OPEN_DIR, - FALSE, { "rev", NULL }, TRUE }, - { OPEN_DIR, S_, "open-directory", OPEN_DIR, - FALSE, { "rev", "name", NULL }, TRUE }, +#define MAX_BODY_IN_RAM (256*1024) - { OPEN_DIR, S_, "add-directory", ADD_DIR, - FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE }, +/* Fold all previously collected data in a single buffer allocated in + RESULT_POOL and clear all intermediate state */ +static const char * +body_allocate_all(body_create_baton_t *body, + apr_pool_t *result_pool) +{ + char *buffer = apr_pcalloc(result_pool, body->total_bytes); + const char *data; + apr_size_t sz; + apr_status_t s; + apr_size_t remaining = body->total_bytes; + char *next = buffer; - { ADD_DIR, S_, "add-directory", ADD_DIR, - FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE }, + while (!(s = serf_bucket_read(body->collect_bucket, remaining, &data, &sz))) + { + memcpy(next, data, sz); + remaining -= sz; + next += sz; - { OPEN_DIR, S_, "open-file", OPEN_FILE, - FALSE, { "rev", "name", NULL }, TRUE }, + if (! remaining) + break; + } - { OPEN_DIR, S_, "add-file", ADD_FILE, - FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE }, + if (!SERF_BUCKET_READ_ERROR(s)) + { + memcpy(next, data, sz); + } - { ADD_DIR, S_, "add-file", ADD_FILE, - FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE }, + serf_bucket_destroy(body->collect_bucket); + body->collect_bucket = NULL; - { OPEN_DIR, S_, "delete-entry", OPEN_FILE, - FALSE, { "?rev", "name", NULL }, TRUE }, + return (s != APR_EOF) ? NULL : buffer; +} - { OPEN_DIR, S_, "absent-directory", ABSENT_DIR, - FALSE, { "name", NULL }, TRUE }, +/* Noop function. Make serf take care of freeing in error situations */ +static void serf_free_no_error(void *unfreed_baton, void *block) {} - { ADD_DIR, S_, "absent-directory", ABSENT_DIR, - FALSE, { "name", NULL }, TRUE }, +/* Stream write function for body creation */ +static svn_error_t * +body_write_fn(void *baton, + const char *data, + apr_size_t *len) +{ + body_create_baton_t *bcb = baton; - { OPEN_DIR, S_, "absent-file", ABSENT_FILE, - FALSE, { "name", NULL }, TRUE }, + if (!bcb->scratch_pool) + bcb->scratch_pool = svn_pool_create(bcb->result_pool); - { ADD_DIR, S_, "absent-file", ABSENT_FILE, - FALSE, { "name", NULL }, TRUE }, + if (bcb->file) + { + SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL, + bcb->scratch_pool)); + svn_pool_clear(bcb->scratch_pool); - { 0 } -}; + bcb->total_bytes += *len; + } + else if (*len + bcb->total_bytes > MAX_BODY_IN_RAM) + { + SVN_ERR(svn_io_open_unique_file3(&bcb->file, NULL, NULL, + svn_io_file_del_on_pool_cleanup, + bcb->result_pool, bcb->scratch_pool)); + if (bcb->total_bytes) + { + const char *all = body_allocate_all(bcb, bcb->scratch_pool); + SVN_ERR(svn_io_file_write_full(bcb->file, all, bcb->total_bytes, + NULL, bcb->scratch_pool)); + } -/* Conforms to svn_ra_serf__xml_opened_t */ -static svn_error_t * -update_opened(svn_ra_serf__xml_estate_t *xes, - void *baton, - int entered_state, - const svn_ra_serf__dav_props_t *tag, - apr_pool_t *scratch_pool) -{ - report_context_t *ctx = baton; + SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL, + bcb->scratch_pool)); + bcb->total_bytes += *len; + } + else + { + if (!bcb->alloc) + bcb->alloc = serf_bucket_allocator_create(bcb->scratch_pool, + serf_free_no_error, NULL); + + if (!bcb->collect_bucket) + bcb->collect_bucket = serf_bucket_aggregate_create(bcb->alloc); + + serf_bucket_aggregate_append(bcb->collect_bucket, + serf_bucket_simple_copy_create(data, *len, + bcb->alloc)); + + bcb->total_bytes += *len; + } return SVN_NO_ERROR; } +/* Stream close function for collecting body */ +static svn_error_t * +body_done_fn(void *baton) +{ + body_create_baton_t *bcb = baton; + if (bcb->file) + { + /* 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 handle. + */ + + SVN_ERR(svn_io_file_flush(bcb->file, bcb->scratch_pool)); + apr_file_buffer_set(bcb->file, NULL, 0); + } + else if (bcb->collect_bucket) + bcb->all_data = body_allocate_all(bcb, bcb->result_pool); + if (bcb->scratch_pool) + svn_pool_destroy(bcb->scratch_pool); + + return SVN_NO_ERROR; +} -/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * -update_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) +create_dir_baton(dir_baton_t **new_dir, + report_context_t *ctx, + const char *name, + apr_pool_t *scratch_pool) { - report_context_t *ctx = baton; + dir_baton_t *parent = ctx->cur_dir; + apr_pool_t *dir_pool; + dir_baton_t *dir; - if (leaving_state == TARGET_REVISION) + if (parent) + dir_pool = svn_pool_create(parent->pool); + else + dir_pool = svn_pool_create(ctx->pool); + + dir = apr_pcalloc(dir_pool, sizeof(*dir)); + dir->pool = dir_pool; + dir->ctx = ctx; + + if (parent) { - const char *rev = svn_hash_gets(attrs, "rev"); + dir->parent_dir = parent; + parent->ref_count++; + } - SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton, - SVN_STR_TO_REV(rev), - ctx->sess->pool)); + dir->relpath = parent ? svn_relpath_join(parent->relpath, name, dir_pool) + : apr_pstrdup(dir_pool, name); + dir->base_name = svn_relpath_basename(dir->relpath, NULL); + + dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->relpath); + if (!dir->repos_relpath) + { + if (parent) + dir->repos_relpath = svn_relpath_join(parent->repos_relpath, name, + dir_pool); + else + dir->repos_relpath = svn_uri_skip_ancestor(ctx->sess->repos_root_str, + ctx->sess->session_url_str, + dir_pool); } + dir->base_rev = SVN_INVALID_REVNUM; + dir->copyfrom_rev = SVN_INVALID_REVNUM; + + dir->ref_count = 1; + + ctx->cur_dir = dir; + + *new_dir = dir; return SVN_NO_ERROR; } - -/* Conforms to svn_ra_serf__xml_cdata_t */ static svn_error_t * -update_cdata(svn_ra_serf__xml_estate_t *xes, - void *baton, - int current_state, - const char *data, - apr_size_t len, - apr_pool_t *scratch_pool) +create_file_baton(file_baton_t **new_file, + report_context_t *ctx, + const char *name, + apr_pool_t *scratch_pool) { - report_context_t *ctx = baton; + dir_baton_t *parent = ctx->cur_dir; + apr_pool_t *file_pool; + file_baton_t *file; - return SVN_NO_ERROR; -} + file_pool = svn_pool_create(parent->pool); -#endif /* NOT_USED_YET */ + file = apr_pcalloc(file_pool, sizeof(*file)); + file->pool = file_pool; + file->parent_dir = parent; + parent->ref_count++; -/* Returns best connection for fetching files/properties. */ -static svn_ra_serf__connection_t * -get_best_connection(report_context_t *ctx) -{ - svn_ra_serf__connection_t *conn; - int first_conn = 1; + file->relpath = svn_relpath_join(parent->relpath, name, file_pool); + file->base_name = svn_relpath_basename(file->relpath, NULL); - /* Skip the first connection if the REPORT response hasn't been completely - received yet or if we're being told to limit our connections to + file->repos_relpath = svn_hash_gets(ctx->switched_paths, file->relpath); + if (!file->repos_relpath) + file->repos_relpath = svn_relpath_join(parent->repos_relpath, name, + file_pool); + + /* Sane defaults */ + file->base_rev = SVN_INVALID_REVNUM; + file->copyfrom_rev = SVN_INVALID_REVNUM; + + *new_file = file; + + ctx->cur_file = file; + + return SVN_NO_ERROR; +} + +/** Minimum nr. of outstanding requests needed before a new connection is + * opened. */ +#define REQS_PER_CONN 8 + +/** This function creates a new connection for this serf session, but only + * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is + * only one main connection open. + */ +static svn_error_t * +open_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs) +{ + /* For each REQS_PER_CONN outstanding requests open a new connection, with + * a minimum of 1 extra connection. */ + if (sess->num_conns == 1 || + ((num_active_reqs / REQS_PER_CONN) > sess->num_conns)) + { + int cur = sess->num_conns; + apr_status_t status; + + sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur])); + sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool, + NULL, NULL); + sess->conns[cur]->last_status_code = -1; + sess->conns[cur]->session = sess; + status = serf_connection_create2(&sess->conns[cur]->conn, + sess->context, + sess->session_url, + svn_ra_serf__conn_setup, + sess->conns[cur], + svn_ra_serf__conn_closed, + sess->conns[cur], + sess->pool); + if (status) + return svn_ra_serf__wrap_err(status, NULL); + + sess->num_conns++; + } + + return SVN_NO_ERROR; +} + +/* Returns best connection for fetching files/properties. */ +static svn_ra_serf__connection_t * +get_best_connection(report_context_t *ctx) +{ + svn_ra_serf__connection_t *conn; + int first_conn = 1; + + /* Skip the first connection if the REPORT response hasn't been completely + received yet or if we're being told to limit our connections to 2 (because this could be an attempt to ensure that we do all our auxiliary GETs/PROPFINDs on a single connection). @@ -520,391 +752,199 @@ get_best_connection(report_context_t *ctx) if (ctx->report_received && (ctx->sess->max_connections > 2)) first_conn = 0; - /* Currently, we just cycle connections. In the future we could - store the number of pending requests on each connection, or - perform other heuristics, to achieve better connection usage. - (As an optimization, if there's only one available auxiliary - connection to use, don't bother doing all the cur_conn math -- - just return that one connection.) */ + /* If there's only one available auxiliary connection to use, don't bother + doing all the cur_conn math -- just return that one connection. */ if (ctx->sess->num_conns - first_conn == 1) { conn = ctx->sess->conns[first_conn]; } else { +#if SERF_VERSION_AT_LEAST(1, 4, 0) + /* Often one connection is slower than others, e.g. because the server + process/thread has to do more work for the particular set of requests. + In the worst case, when REQUEST_COUNT_TO_RESUME requests are queued + on such a slow connection, ra_serf will completely stop sending + requests. + + The method used here selects the connection with the least amount of + pending requests, thereby giving more work to lightly loaded server + processes. + */ + int i, best_conn = first_conn; + unsigned int min = INT_MAX; + for (i = first_conn; i < ctx->sess->num_conns; i++) + { + serf_connection_t *sc = ctx->sess->conns[i]->conn; + unsigned int pending = serf_connection_pending_requests(sc); + if (pending < min) + { + min = pending; + best_conn = i; + } + } + conn = ctx->sess->conns[best_conn]; +#else + /* We don't know how many requests are pending per connection, so just + cycle them. */ conn = ctx->sess->conns[ctx->sess->cur_conn]; ctx->sess->cur_conn++; if (ctx->sess->cur_conn >= ctx->sess->num_conns) ctx->sess->cur_conn = first_conn; +#endif } return conn; } - - -/** Report state management helper **/ - -static report_info_t * -push_state(svn_ra_serf__xml_parser_t *parser, - report_context_t *ctx, - report_state_e state) -{ - report_info_t *info; - apr_pool_t *info_parent_pool; - - svn_ra_serf__xml_push_state(parser, state); - - info = parser->state->private; - - /* Our private pool needs to be disjoint from the state pool. */ - if (!info) - { - info_parent_pool = ctx->pool; - } - else - { - info_parent_pool = info->pool; - } - - if (state == OPEN_DIR || state == ADD_DIR) - { - report_info_t *new_info; - - new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info)); - new_info->pool = svn_pool_create(info_parent_pool); - new_info->lock_token = NULL; - new_info->prop_value = svn_stringbuf_create_empty(new_info->pool); - - new_info->dir = apr_pcalloc(new_info->pool, sizeof(*new_info->dir)); - new_info->dir->pool = new_info->pool; - - /* Create the root property tree. */ - new_info->dir->props = apr_hash_make(new_info->pool); - new_info->props = new_info->dir->props; - new_info->dir->removed_props = apr_hash_make(new_info->pool); - - new_info->dir->report_context = ctx; - - if (info) - { - info->dir->ref_count++; - - new_info->dir->parent_dir = info->dir; - - /* Point our ns_list at our parents to try to reuse it. */ - new_info->dir->ns_list = info->dir->ns_list; - - /* Add ourselves to our parent's list */ - new_info->dir->sibling = info->dir->children; - info->dir->children = new_info->dir; - } - else - { - /* Allow us to be found later. */ - ctx->root_dir = new_info->dir; - } - - parser->state->private = new_info; - } - else if (state == OPEN_FILE || state == ADD_FILE) - { - report_info_t *new_info; - - new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info)); - new_info->pool = svn_pool_create(info_parent_pool); - new_info->file_baton = NULL; - new_info->lock_token = NULL; - new_info->fetch_file = FALSE; - new_info->prop_value = svn_stringbuf_create_empty(new_info->pool); - - /* Point at our parent's directory state. */ - new_info->dir = info->dir; - info->dir->ref_count++; - - new_info->props = apr_hash_make(new_info->pool); - - parser->state->private = new_info; - } - - return parser->state->private; -} - - -/** Wrappers around our various property walkers **/ - -static svn_error_t * -set_file_props(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - report_info_t *info = baton; - const svn_delta_editor_t *editor = info->dir->report_context->update_editor; - const char *prop_name; - - prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); - if (prop_name != NULL) - return svn_error_trace(editor->change_file_prop(info->file_baton, - prop_name, - val, - scratch_pool)); - return SVN_NO_ERROR; -} - - -static svn_error_t * -set_dir_props(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - report_dir_t *dir = baton; - const svn_delta_editor_t *editor = dir->report_context->update_editor; - const char *prop_name; - - prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); - if (prop_name != NULL) - return svn_error_trace(editor->change_dir_prop(dir->dir_baton, - prop_name, - val, - scratch_pool)); - return SVN_NO_ERROR; -} - - -static svn_error_t * -remove_file_props(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - report_info_t *info = baton; - const svn_delta_editor_t *editor = info->dir->report_context->update_editor; - const char *prop_name; - - prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); - if (prop_name != NULL) - return svn_error_trace(editor->change_file_prop(info->file_baton, - prop_name, - NULL, - scratch_pool)); - return SVN_NO_ERROR; -} - - -static svn_error_t * -remove_dir_props(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - report_dir_t *dir = baton; - const svn_delta_editor_t *editor = dir->report_context->update_editor; - const char *prop_name; - - prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); - if (prop_name != NULL) - return svn_error_trace(editor->change_dir_prop(dir->dir_baton, - prop_name, - NULL, - scratch_pool)); - return SVN_NO_ERROR; -} - /** Helpers to open and close directories */ static svn_error_t* -ensure_dir_opened(report_dir_t *dir) +ensure_dir_opened(dir_baton_t *dir, + apr_pool_t *scratch_pool) { - report_context_t *ctx = dir->report_context; + report_context_t *ctx = dir->ctx; - /* if we're already open, return now */ - if (dir->dir_baton) - { - return SVN_NO_ERROR; - } + if (dir->dir_opened) + return SVN_NO_ERROR; if (dir->base_name[0] == '\0') { - dir->dir_baton_pool = svn_pool_create(dir->pool); - if (ctx->destination && ctx->sess->wc_callbacks->invalidate_wc_props) { SVN_ERR(ctx->sess->wc_callbacks->invalidate_wc_props( ctx->sess->wc_callback_baton, ctx->update_target, - SVN_RA_SERF__WC_CHECKED_IN_URL, dir->pool)); + SVN_RA_SERF__WC_CHECKED_IN_URL, scratch_pool)); } - SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev, - dir->dir_baton_pool, - &dir->dir_baton)); + SVN_ERR(ctx->editor->open_root(ctx->editor_baton, dir->base_rev, + dir->pool, + &dir->dir_baton)); } else { - SVN_ERR(ensure_dir_opened(dir->parent_dir)); - - dir->dir_baton_pool = svn_pool_create(dir->parent_dir->dir_baton_pool); + SVN_ERR(ensure_dir_opened(dir->parent_dir, scratch_pool)); if (SVN_IS_VALID_REVNUM(dir->base_rev)) { - SVN_ERR(ctx->update_editor->open_directory(dir->name, - dir->parent_dir->dir_baton, - dir->base_rev, - dir->dir_baton_pool, - &dir->dir_baton)); + SVN_ERR(ctx->editor->open_directory(dir->relpath, + dir->parent_dir->dir_baton, + dir->base_rev, + dir->pool, + &dir->dir_baton)); } else { - SVN_ERR(ctx->update_editor->add_directory(dir->name, - dir->parent_dir->dir_baton, - NULL, SVN_INVALID_REVNUM, - dir->dir_baton_pool, - &dir->dir_baton)); + SVN_ERR(ctx->editor->add_directory(dir->relpath, + dir->parent_dir->dir_baton, + dir->copyfrom_path, + dir->copyfrom_rev, + dir->pool, + &dir->dir_baton)); } } + dir->dir_opened = TRUE; + return SVN_NO_ERROR; } static svn_error_t * -close_dir(report_dir_t *dir) +maybe_close_dir(dir_baton_t *dir) { - report_dir_t *prev; - report_dir_t *sibling; - - /* ### is there a better pool... this is tossed at end-of-func */ - apr_pool_t *scratch_pool = dir->dir_baton_pool; - - SVN_ERR_ASSERT(! dir->ref_count); - - SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->base_name, - dir->base_rev, - set_dir_props, dir, - scratch_pool)); + apr_pool_t *scratch_pool = dir->pool; + dir_baton_t *parent = dir->parent_dir; + report_context_t *ctx = dir->ctx; - SVN_ERR(svn_ra_serf__walk_all_props(dir->removed_props, dir->base_name, - dir->base_rev, remove_dir_props, dir, - scratch_pool)); - - if (dir->fetch_props) + if (--dir->ref_count) { - SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url, - dir->report_context->target_rev, - set_dir_props, dir, - scratch_pool)); + return SVN_NO_ERROR; } - SVN_ERR(dir->report_context->update_editor->close_directory( - dir->dir_baton, scratch_pool)); + SVN_ERR(ensure_dir_opened(dir, dir->pool)); - /* remove us from our parent's children list */ - if (dir->parent_dir) + if (dir->remove_props) { - prev = NULL; - sibling = dir->parent_dir->children; - - while (sibling != dir) - { - prev = sibling; - sibling = sibling->sibling; - if (!sibling) - SVN_ERR_MALFUNCTION(); - } + apr_hash_index_t *hi; - if (!prev) - { - dir->parent_dir->children = dir->sibling; - } - else + for (hi = apr_hash_first(scratch_pool, dir->remove_props); + hi; + hi = apr_hash_next(hi)) { - prev->sibling = dir->sibling; + SVN_ERR(ctx->editor->change_file_prop(dir->dir_baton, + apr_hash_this_key(hi), + NULL /* value */, + scratch_pool)); } } - svn_pool_destroy(dir->dir_baton_pool); - svn_pool_destroy(dir->pool); - - return SVN_NO_ERROR; -} - -static svn_error_t *close_all_dirs(report_dir_t *dir) -{ - while (dir->children) - { - SVN_ERR(close_all_dirs(dir->children)); - dir->ref_count--; - } - - SVN_ERR_ASSERT(! dir->ref_count); + SVN_ERR(dir->ctx->editor->close_directory(dir->dir_baton, scratch_pool)); - SVN_ERR(ensure_dir_opened(dir)); + svn_pool_destroy(dir->pool /* scratch_pool */); - return close_dir(dir); + if (parent) + return svn_error_trace(maybe_close_dir(parent)); + else + return SVN_NO_ERROR; } - -/** Routines called when we are fetching a file */ - -/* This function works around a bug in some older versions of - * mod_dav_svn in that it will not send remove-prop in the update - * report when a lock property disappears when send-all is false. - * - * Therefore, we'll try to look at our properties and see if there's - * an active lock. If not, then we'll assume there isn't a lock - * anymore. - */ -static void -check_lock(report_info_t *info) +static svn_error_t * +ensure_file_opened(file_baton_t *file, + apr_pool_t *scratch_pool) { - const char *lock_val; + const svn_delta_editor_t *editor = file->parent_dir->ctx->editor; - lock_val = svn_ra_serf__get_ver_prop(info->props, info->url, - info->dir->report_context->target_rev, - "DAV:", "lockdiscovery"); + if (file->file_opened) + return SVN_NO_ERROR; + + /* Ensure our parent is open. */ + SVN_ERR(ensure_dir_opened(file->parent_dir, scratch_pool)); - if (lock_val) + /* Open (or add) the file. */ + if (SVN_IS_VALID_REVNUM(file->base_rev)) { - char *new_lock; - new_lock = apr_pstrdup(info->editor_pool, lock_val); - apr_collapse_spaces(new_lock, new_lock); - lock_val = new_lock; + SVN_ERR(editor->open_file(file->relpath, + file->parent_dir->dir_baton, + file->base_rev, + file->pool, + &file->file_baton)); } - - if (!lock_val || lock_val[0] == '\0') + else { - svn_string_t *str; + SVN_ERR(editor->add_file(file->relpath, + file->parent_dir->dir_baton, + file->copyfrom_path, + file->copyfrom_rev, + file->pool, + &file->file_baton)); + } - str = svn_string_ncreate("", 1, info->editor_pool); + file->file_opened = TRUE; - svn_ra_serf__set_ver_prop(info->dir->removed_props, info->base_name, - info->base_rev, "DAV:", "lock-token", - str, info->dir->pool); - } + return SVN_NO_ERROR; } + +/** Routines called when we are fetching a file */ + static svn_error_t * headers_fetch(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { - report_fetch_t *fetch_ctx = baton; + fetch_ctx_t *fetch_ctx = baton; /* note that we have old VC URL */ - if (SVN_IS_VALID_REVNUM(fetch_ctx->info->base_rev) && - fetch_ctx->info->delta_base) + if (fetch_ctx->delta_base) { serf_bucket_headers_setn(headers, SVN_DAV_DELTA_BASE_HEADER, - fetch_ctx->info->delta_base); + fetch_ctx->delta_base); serf_bucket_headers_setn(headers, "Accept-Encoding", "svndiff1;q=0.9,svndiff;q=0.8"); } - else if (fetch_ctx->sess->using_compression) + else if (fetch_ctx->using_compression) { serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip"); } @@ -918,7 +958,7 @@ cancel_fetch(serf_request_t *request, int status_code, void *baton) { - report_fetch_t *fetch_ctx = baton; + fetch_ctx_t *fetch_ctx = baton; /* Uh-oh. Our connection died on us. * @@ -948,30 +988,6 @@ cancel_fetch(serf_request_t *request, SVN_ERR_MALFUNCTION(); } -static svn_error_t * -error_fetch(serf_request_t *request, - report_fetch_t *fetch_ctx, - svn_error_t *err) -{ - fetch_ctx->done = TRUE; - - fetch_ctx->done_item.data = fetch_ctx; - fetch_ctx->done_item.next = *fetch_ctx->done_list; - *fetch_ctx->done_list = &fetch_ctx->done_item; - - /* Discard the rest of this request - (This makes sure it doesn't error when the request is aborted later) */ - serf_request_set_handler(request, - svn_ra_serf__response_discard_handler, NULL); - - /* Some errors would be handled by serf; make sure they really make - the update fail by wrapping it in a different error. */ - if (!SERF_BUCKET_READ_ERROR(err->apr_err)) - return svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); - - return err; -} - /* Wield the editor referenced by INFO to open (or add) the file file also associated with INFO, setting properties on the file and calling the editor's apply_textdelta() function on it if necessary @@ -980,95 +996,92 @@ error_fetch(serf_request_t *request, Callers will probably want to also see the function that serves the opposite purpose of this one, close_updated_file(). */ static svn_error_t * -open_updated_file(report_info_t *info, - svn_boolean_t force_apply_textdelta, +open_file_txdelta(file_baton_t *file, apr_pool_t *scratch_pool) { - report_context_t *ctx = info->dir->report_context; - const svn_delta_editor_t *update_editor = ctx->update_editor; - - /* Ensure our parent is open. */ - SVN_ERR(ensure_dir_opened(info->dir)); - info->editor_pool = svn_pool_create(info->dir->dir_baton_pool); - - /* Expand our full name now if we haven't done so yet. */ - if (!info->name) - { - info->name = svn_relpath_join(info->dir->name, info->base_name, - info->editor_pool); - } + const svn_delta_editor_t *editor = file->parent_dir->ctx->editor; - /* Open (or add) the file. */ - if (SVN_IS_VALID_REVNUM(info->base_rev)) - { - SVN_ERR(update_editor->open_file(info->name, - info->dir->dir_baton, - info->base_rev, - info->editor_pool, - &info->file_baton)); - } - else - { - SVN_ERR(update_editor->add_file(info->name, - info->dir->dir_baton, - info->copyfrom_path, - info->copyfrom_rev, - info->editor_pool, - &info->file_baton)); - } + SVN_ERR_ASSERT(file->txdelta == NULL); - /* Check for lock information. */ - if (info->lock_token) - check_lock(info); + SVN_ERR(ensure_file_opened(file, scratch_pool)); /* Get (maybe) a textdelta window handler for transmitting file content changes. */ - if (info->fetch_file || force_apply_textdelta) - { - SVN_ERR(update_editor->apply_textdelta(info->file_baton, - info->base_checksum, - info->editor_pool, - &info->textdelta, - &info->textdelta_baton)); - } + SVN_ERR(editor->apply_textdelta(file->file_baton, + svn_checksum_to_cstring( + file->base_md5_checksum, + scratch_pool), + file->pool, + &file->txdelta, + &file->txdelta_baton)); return SVN_NO_ERROR; } -/* Close the file associated with INFO->file_baton, and cleanup other - bits of that structure managed by open_updated_file(). */ +/* Close the file, handling loose ends and cleanup */ static svn_error_t * -close_updated_file(report_info_t *info, - apr_pool_t *scratch_pool) +close_file(file_baton_t *file, + apr_pool_t *scratch_pool) { - report_context_t *ctx = info->dir->report_context; + dir_baton_t *parent_dir = file->parent_dir; + report_context_t *ctx = parent_dir->ctx; + + SVN_ERR(ensure_file_opened(file, scratch_pool)); /* Set all of the properties we received */ - SVN_ERR(svn_ra_serf__walk_all_props(info->props, - info->base_name, - info->base_rev, - set_file_props, info, - scratch_pool)); - SVN_ERR(svn_ra_serf__walk_all_props(info->dir->removed_props, - info->base_name, - info->base_rev, - remove_file_props, info, - scratch_pool)); - if (info->fetch_props) + if (file->remove_props) + { + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, file->remove_props); + hi; + hi = apr_hash_next(hi)) + { + SVN_ERR(ctx->editor->change_file_prop(file->file_baton, + apr_hash_this_key(hi), + NULL /* value */, + scratch_pool)); + } + } + + /* Check for lock information. */ + + /* This works around a bug in some older versions of mod_dav_svn in that it + * will not send remove-prop in the update report when a lock property + * disappears when send-all is false. + + ### Given that we only fetch props on additions, is this really necessary? + Or is it covering up old local copy bugs where we copied locks to other + paths? */ + if (!ctx->add_props_included + && file->lock_token && !file->found_lock_prop + && SVN_IS_VALID_REVNUM(file->base_rev) /* file_is_added */) + { + SVN_ERR(ctx->editor->change_file_prop(file->file_baton, + SVN_PROP_ENTRY_LOCK_TOKEN, + NULL, + scratch_pool)); + } + + if (file->url) { - SVN_ERR(svn_ra_serf__walk_all_props(info->props, - info->url, - ctx->target_rev, - set_file_props, info, - scratch_pool)); + SVN_ERR(ctx->editor->change_file_prop(file->file_baton, + SVN_RA_SERF__WC_CHECKED_IN_URL, + svn_string_create(file->url, + scratch_pool), + scratch_pool)); } /* Close the file via the editor. */ - SVN_ERR(info->dir->report_context->update_editor->close_file( - info->file_baton, info->final_checksum, scratch_pool)); + SVN_ERR(ctx->editor->close_file(file->file_baton, + svn_checksum_to_cstring( + file->final_md5_checksum, + scratch_pool), + scratch_pool)); - /* We're done with our editor pool. */ - svn_pool_destroy(info->editor_pool); + svn_pool_destroy(file->pool); + + SVN_ERR(maybe_close_dir(parent_dir)); /* Remove reference */ return SVN_NO_ERROR; } @@ -1083,8 +1096,8 @@ handle_fetch(serf_request_t *request, const char *data; apr_size_t len; apr_status_t status; - report_fetch_t *fetch_ctx = handler_baton; - svn_error_t *err; + fetch_ctx_t *fetch_ctx = handler_baton; + file_baton_t *file = fetch_ctx->file; /* ### new field. make sure we didn't miss some initialization. */ SVN_ERR_ASSERT(fetch_ctx->handler != NULL); @@ -1093,50 +1106,53 @@ handle_fetch(serf_request_t *request, { serf_bucket_t *hdrs; const char *val; - report_info_t *info; + + /* If the error code wasn't 200, something went wrong. Don't use the + * returned data as its probably an error message. Just bail out instead. + */ + if (fetch_ctx->handler->sline.code != 200) + { + fetch_ctx->handler->discard_body = TRUE; + return SVN_NO_ERROR; /* Will return an error in the DONE handler */ + } hdrs = serf_bucket_response_get_headers(response); val = serf_bucket_headers_get(hdrs, "Content-Type"); - info = fetch_ctx->info; if (val && svn_cstring_casecmp(val, SVN_SVNDIFF_MIME_TYPE) == 0) { - fetch_ctx->delta_stream = - svn_txdelta_parse_svndiff(info->textdelta, - info->textdelta_baton, - TRUE, info->editor_pool); + fetch_ctx->result_stream = + svn_txdelta_parse_svndiff(file->txdelta, + file->txdelta_baton, + TRUE, file->pool); /* Validate the delta base claimed by the server matches what we asked for! */ val = serf_bucket_headers_get(hdrs, SVN_DAV_DELTA_BASE_HEADER); - if (val && (strcmp(val, info->delta_base) != 0)) + if (val && fetch_ctx->delta_base == NULL) + { + /* We recieved response with delta base header while we didn't + requested it -- report it as error. */ + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("GET request returned unexpected " + "delta base: %s"), val); + } + else if (val && (strcmp(val, fetch_ctx->delta_base) != 0)) { - err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("GET request returned unexpected " - "delta base: %s"), val); - return error_fetch(request, fetch_ctx, err); + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("GET request returned unexpected " + "delta base: %s"), val); } } else { - fetch_ctx->delta_stream = NULL; + fetch_ctx->result_stream = NULL; } fetch_ctx->read_headers = TRUE; } - /* If the error code wasn't 200, something went wrong. Don't use the returned - data as its probably an error message. Just bail out instead. */ - if (fetch_ctx->handler->sline.code != 200) - { - err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("GET request failed: %d %s"), - fetch_ctx->handler->sline.code, - fetch_ctx->handler->sline.reason); - return error_fetch(request, fetch_ctx, err); - } - - while (1) + while (TRUE) { svn_txdelta_window_t delta_window = { 0 }; svn_txdelta_op_t delta_op; @@ -1163,10 +1179,9 @@ handle_fetch(serf_request_t *request, } /* Skip on to the next iteration of this loop. */ - if (APR_STATUS_IS_EAGAIN(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } + if (status /* includes EAGAIN */) + return svn_ra_serf__wrap_err(status, NULL); + continue; } @@ -1176,17 +1191,12 @@ handle_fetch(serf_request_t *request, /* Update data and len to just provide the new data. */ skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size); data += skip; - len -= skip; + len -= (apr_size_t)skip; } - if (fetch_ctx->delta_stream) - { - err = svn_stream_write(fetch_ctx->delta_stream, data, &len); - if (err) - { - return error_fetch(request, fetch_ctx, err); - } - } + if (fetch_ctx->result_stream) + SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, &len)); + /* otherwise, manually construct the text delta window. */ else if (len) { @@ -1203,378 +1213,271 @@ handle_fetch(serf_request_t *request, delta_window.new_data = &window_data; /* write to the file located in the info. */ - err = fetch_ctx->info->textdelta(&delta_window, - fetch_ctx->info->textdelta_baton); - if (err) - { - return error_fetch(request, fetch_ctx, err); - } + SVN_ERR(file->txdelta(&delta_window, file->txdelta_baton)); } if (APR_STATUS_IS_EOF(status)) { - report_info_t *info = fetch_ctx->info; - - if (fetch_ctx->delta_stream) - err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream)); + if (fetch_ctx->result_stream) + SVN_ERR(svn_stream_close(fetch_ctx->result_stream)); else - err = svn_error_trace(info->textdelta(NULL, - info->textdelta_baton)); - if (err) - { - return error_fetch(request, fetch_ctx, err); - } - - err = close_updated_file(info, info->pool); - if (err) - { - return svn_error_trace(error_fetch(request, fetch_ctx, err)); - } - - fetch_ctx->done = TRUE; - - fetch_ctx->done_item.data = fetch_ctx; - fetch_ctx->done_item.next = *fetch_ctx->done_list; - *fetch_ctx->done_list = &fetch_ctx->done_item; - - /* We're done with our pool. */ - svn_pool_destroy(info->pool); - - if (status) - return svn_ra_serf__wrap_err(status, NULL); - } - if (APR_STATUS_IS_EAGAIN(status)) - { - return svn_ra_serf__wrap_err(status, NULL); + SVN_ERR(file->txdelta(NULL, file->txdelta_baton)); } + + /* Report EOF, EEAGAIN and other special errors to serf */ + if (status) + return svn_ra_serf__wrap_err(status, NULL); } - /* not reached */ } -/* Implements svn_ra_serf__response_handler_t */ -static svn_error_t * -handle_stream(serf_request_t *request, - serf_bucket_t *response, - void *handler_baton, - apr_pool_t *pool) -{ - report_fetch_t *fetch_ctx = handler_baton; - svn_error_t *err; - apr_status_t status; - - /* ### new field. make sure we didn't miss some initialization. */ - SVN_ERR_ASSERT(fetch_ctx->handler != NULL); +/* --------------------------------------------------------- */ - err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline, - fetch_ctx->info->name, - fetch_ctx->handler->location); - if (err) - { - fetch_ctx->handler->done = TRUE; +/** Wrappers around our various property walkers **/ - err = svn_error_compose_create( - err, - svn_ra_serf__handle_discard_body(request, response, NULL, pool)); +/* Implements svn_ra_serf__prop_func */ +static svn_error_t * +set_file_props(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *val, + apr_pool_t *scratch_pool) +{ + file_baton_t *file = baton; + report_context_t *ctx = file->parent_dir->ctx; + const char *prop_name; - return svn_error_trace(err); - } + prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); - while (1) + if (!prop_name) { - const char *data; - apr_size_t len; - - status = serf_bucket_read(response, 8000, &data, &len); - if (SERF_BUCKET_READ_ERROR(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - - fetch_ctx->read_size += len; - - if (fetch_ctx->aborted_read) + /* This works around a bug in some older versions of + * mod_dav_svn in that it will not send remove-prop in the update + * report when a lock property disappears when send-all is false. + * + * Therefore, we'll try to look at our properties and see if there's + * an active lock. If not, then we'll assume there isn't a lock + * anymore. + */ + /* assert(!ctx->add_props_included); // Or we wouldn't be here */ + if (file->lock_token + && !file->found_lock_prop + && val + && strcmp(ns, "DAV:") == 0 + && strcmp(name, "lockdiscovery") == 0) { - /* We haven't caught up to where we were before. */ - if (fetch_ctx->read_size < fetch_ctx->aborted_read_size) - { - /* Eek. What did the file shrink or something? */ - if (APR_STATUS_IS_EOF(status)) - { - SVN_ERR_MALFUNCTION(); - } - - /* Skip on to the next iteration of this loop. */ - if (APR_STATUS_IS_EAGAIN(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - continue; - } - - /* Woo-hoo. We're back. */ - fetch_ctx->aborted_read = FALSE; + char *new_lock; + new_lock = apr_pstrdup(scratch_pool, val->data); + apr_collapse_spaces(new_lock, new_lock); - /* Increment data and len by the difference. */ - data += fetch_ctx->read_size - fetch_ctx->aborted_read_size; - len += fetch_ctx->read_size - fetch_ctx->aborted_read_size; + if (new_lock[0] != '\0') + file->found_lock_prop = TRUE; } - if (len) - { - apr_size_t written_len; - - written_len = len; + return SVN_NO_ERROR; + } - SVN_ERR(svn_stream_write(fetch_ctx->target_stream, data, - &written_len)); - } + SVN_ERR(ensure_file_opened(file, scratch_pool)); - if (APR_STATUS_IS_EOF(status)) - { - fetch_ctx->done = TRUE; - } + SVN_ERR(ctx->editor->change_file_prop(file->file_baton, + prop_name, val, + scratch_pool)); - if (status) - { - return svn_ra_serf__wrap_err(status, NULL); - } - } - /* not reached */ + return SVN_NO_ERROR; } -/* Close the directory represented by DIR -- and any suitable parents - thereof -- if we are able to do so. This is the case whenever: - - - there are no remaining open items within the directory, and - - the directory's XML close tag has been processed (so we know - there are no more children to worry about in the future), and - - either: - - we aren't fetching properties for this directory, or - - we've already finished fetching those properties. -*/ +/* Implements svn_ra_serf__response_done_delegate_t */ static svn_error_t * -maybe_close_dir_chain(report_dir_t *dir) +file_props_done(serf_request_t *request, + void *baton, + apr_pool_t *scratch_pool) { - report_dir_t *cur_dir = dir; - - SVN_ERR(ensure_dir_opened(cur_dir)); - - while (cur_dir - && !cur_dir->ref_count - && cur_dir->tag_closed - && (!cur_dir->fetch_props || cur_dir->propfind_handler->done)) - { - report_dir_t *parent = cur_dir->parent_dir; - report_context_t *report_context = cur_dir->report_context; - svn_boolean_t propfind_in_done_list = FALSE; - svn_ra_serf__list_t *done_list; - - /* Make sure there are no references to this dir in the - active_dir_propfinds list. If there are, don't close the - directory -- which would delete the pool from which the - relevant active_dir_propfinds list item is allocated -- and - of course don't crawl upward to check the parents for - a closure opportunity, either. */ - done_list = report_context->active_dir_propfinds; - while (done_list) - { - if (done_list->data == cur_dir) - { - propfind_in_done_list = TRUE; - break; - } - done_list = done_list->next; - } - if (propfind_in_done_list) - break; + file_baton_t *file = baton; + svn_ra_serf__handler_t *handler = file->propfind_handler; - SVN_ERR(close_dir(cur_dir)); - if (parent) - { - parent->ref_count--; - } - else - { - report_context->closed_root = TRUE; - } - cur_dir = parent; - } + if (handler->server_error) + return svn_error_trace(svn_ra_serf__server_error_create(handler, + scratch_pool)); - return SVN_NO_ERROR; -} + if (handler->sline.code != 207) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); -/* Open the file associated with INFO for editing, pass along any - propchanges we've recorded for it, and then close the file. */ -static svn_error_t * -handle_propchange_only(report_info_t *info, - apr_pool_t *scratch_pool) -{ - SVN_ERR(open_updated_file(info, FALSE, scratch_pool)); - SVN_ERR(close_updated_file(info, scratch_pool)); + file->parent_dir->ctx->num_active_propfinds--; - /* We're done with our pool. */ - svn_pool_destroy(info->pool); + file->fetch_props = FALSE; - info->dir->ref_count--; + if (file->fetch_file) + return SVN_NO_ERROR; /* Still processing file request */ - /* See if the parent directory of this file (and perhaps even - parents of that) can be closed now. */ - SVN_ERR(maybe_close_dir_chain(info->dir)); + /* Closing the file will automatically deliver the propfind props. + * + * Note that closing the directory may dispose the pool containing the + * handler, which is only a valid operation in this callback, as only + * after this callback our serf plumbing assumes the request is done. */ - return SVN_NO_ERROR; + return svn_error_trace(close_file(file, scratch_pool)); } -/* "Fetch" a file whose contents were made available via the - get_wc_contents() callback (as opposed to requiring a GET to the - server), and feed the information through the associated update - editor. In editor-speak, this will add/open the file, transmit any - property changes, handle the contents, and then close the file. */ static svn_error_t * -handle_local_content(report_info_t *info, - apr_pool_t *scratch_pool) +file_fetch_done(serf_request_t *request, + void *baton, + apr_pool_t *scratch_pool) { - SVN_ERR(svn_txdelta_send_stream(info->cached_contents, info->textdelta, - info->textdelta_baton, NULL, scratch_pool)); - SVN_ERR(svn_stream_close(info->cached_contents)); - info->cached_contents = NULL; - SVN_ERR(close_updated_file(info, scratch_pool)); + fetch_ctx_t *fetch_ctx = baton; + file_baton_t *file = fetch_ctx->file; + svn_ra_serf__handler_t *handler = fetch_ctx->handler; - /* We're done with our pool. */ - svn_pool_destroy(info->pool); + if (handler->server_error) + return svn_error_trace(svn_ra_serf__server_error_create(handler, + scratch_pool)); - info->dir->ref_count--; + if (handler->sline.code != 200) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); - /* See if the parent directory of this fetched item (and - perhaps even parents of that) can be closed now. */ - SVN_ERR(maybe_close_dir_chain(info->dir)); + file->parent_dir->ctx->num_active_fetches--; - return SVN_NO_ERROR; -} + file->fetch_file = FALSE; -/* --------------------------------------------------------- */ + if (file->fetch_props) + return SVN_NO_ERROR; /* Still processing PROPFIND request */ + + /* Closing the file will automatically deliver the propfind props. + * + * Note that closing the directory may dispose the pool containing the + * handler, fetch_ctx, etc. which is only a valid operation in this + * callback, as only after this callback our serf plumbing assumes the + * request is done. */ + return svn_error_trace(close_file(file, scratch_pool)); +} +/* Initiates additional requests needed for a file when not in "send-all" mode. + */ static svn_error_t * -fetch_file(report_context_t *ctx, report_info_t *info) +fetch_for_file(file_baton_t *file, + apr_pool_t *scratch_pool) { + report_context_t *ctx = file->parent_dir->ctx; svn_ra_serf__connection_t *conn; svn_ra_serf__handler_t *handler; + /* Open extra connections if we have enough requests to send. */ + if (ctx->sess->num_conns < ctx->sess->max_connections) + SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches + + ctx->num_active_propfinds)); + /* What connection should we go on? */ conn = get_best_connection(ctx); - /* If needed, create the PROPFIND to retrieve the file's properties. */ - info->propfind_handler = NULL; - if (info->fetch_props) - { - SVN_ERR(svn_ra_serf__deliver_props(&info->propfind_handler, info->props, - ctx->sess, conn, info->url, - ctx->target_rev, "0", all_props, - &ctx->done_propfinds, - info->dir->pool)); - SVN_ERR_ASSERT(info->propfind_handler); - - /* Create a serf request for the PROPFIND. */ - svn_ra_serf__request_create(info->propfind_handler); - - ctx->num_active_propfinds++; - } + /* Note that we (still) use conn for both requests.. Should we send + them out on different connections? */ - /* If we've been asked to fetch the file or it's an add, do so. - * Otherwise, handle the case where only the properties changed. - */ - if (info->fetch_file && ctx->text_deltas) + if (file->fetch_file) { - svn_stream_t *contents = NULL; + SVN_ERR(open_file_txdelta(file, scratch_pool)); - /* Open the file for editing. */ - SVN_ERR(open_updated_file(info, FALSE, info->pool)); - - if (info->textdelta == svn_delta_noop_window_handler) + if (!ctx->text_deltas + || file->txdelta == svn_delta_noop_window_handler) { - /* There is nobody looking for an actual stream. - - Just report an empty stream instead of fetching - to be ingored data */ - info->cached_contents = svn_stream_empty(info->pool); + SVN_ERR(file->txdelta(NULL, file->txdelta_baton)); + file->fetch_file = FALSE; } - else if (ctx->sess->wc_callbacks->get_wc_contents - && info->final_sha1_checksum) - { - svn_error_t *err = NULL; - svn_checksum_t *checksum = NULL; - - /* Parse the optional SHA1 checksum (1.7+) */ - err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1, - info->final_sha1_checksum, - info->pool); - /* Okay so far? Let's try to get a stream on some readily - available matching content. */ - if (!err && checksum) - { - err = ctx->sess->wc_callbacks->get_wc_contents( - ctx->sess->wc_callback_baton, &contents, - checksum, info->pool); + if (file->fetch_file + && file->final_sha1_checksum + && ctx->sess->wc_callbacks->get_wc_contents) + { + svn_error_t *err; + svn_stream_t *cached_contents = NULL; - if (! err) - info->cached_contents = contents; - } + err = ctx->sess->wc_callbacks->get_wc_contents( + ctx->sess->wc_callback_baton, + &cached_contents, + file->final_sha1_checksum, + scratch_pool); - if (err) + if (err || !cached_contents) + svn_error_clear(err); /* ### Can we return some/most errors? */ + else { - /* Meh. Maybe we'll care one day why we're in an - errorful state, but this codepath is optional. */ - svn_error_clear(err); + /* ### For debugging purposes we could validate the md5 here, + but our implementations in libsvn_client already do that + for us... */ + SVN_ERR(svn_txdelta_send_stream(cached_contents, + file->txdelta, + file->txdelta_baton, + NULL, scratch_pool)); + SVN_ERR(svn_stream_close(cached_contents)); + file->fetch_file = FALSE; } } - /* If the working copy can provide cached contents for this - file, we don't have to fetch them from the server. */ - if (info->cached_contents) + if (file->fetch_file) { - /* If we'll be doing a PROPFIND for this file... */ - if (info->propfind_handler) + fetch_ctx_t *fetch_ctx; + + /* Let's fetch the file with a GET request... */ + SVN_ERR_ASSERT(file->url && file->repos_relpath); + + /* Otherwise, we use a GET request for the file's contents. */ + + fetch_ctx = apr_pcalloc(file->pool, sizeof(*fetch_ctx)); + fetch_ctx->file = file; + fetch_ctx->using_compression = ctx->sess->using_compression; + + /* Can we somehow get away with just obtaining a DIFF? */ + if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess)) { - /* ... then we'll just leave ourselves a little "todo" - about that fact (and we'll deal with the file content - stuff later, after we've handled that PROPFIND - response. */ - svn_ra_serf__list_t *list_item; - - list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item)); - list_item->data = info; - list_item->next = ctx->file_propchanges_only; - ctx->file_propchanges_only = list_item; + /* If this file is switched vs the editor root we should provide + its real url instead of the one calculated from the session root. + */ + if (SVN_IS_VALID_REVNUM(file->base_rev)) + { + fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s", + ctx->sess->rev_root_stub, + file->base_rev, + svn_path_uri_encode( + file->repos_relpath, + scratch_pool)); + } + else if (file->copyfrom_path) + { + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(file->copyfrom_rev)); + + fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s", + ctx->sess->rev_root_stub, + file->copyfrom_rev, + svn_path_uri_encode( + file->copyfrom_path+1, + scratch_pool)); + } } - else + else if (ctx->sess->wc_callbacks->get_wc_prop) { - /* Otherwise, if we've no PROPFIND to do, we might as - well take care of those locally accessible file - contents now. */ - SVN_ERR(handle_local_content(info, info->pool)); + /* If we have a WC, we might be able to dive all the way into the WC + * to get the previous URL so we can do a differential GET with the + * base URL. + */ + const svn_string_t *value = NULL; + SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop( + ctx->sess->wc_callback_baton, + file->relpath, + SVN_RA_SERF__WC_CHECKED_IN_URL, + &value, scratch_pool)); + + fetch_ctx->delta_base = value + ? apr_pstrdup(file->pool, value->data) + : NULL; } - } - else - { - /* Otherwise, we use a GET request for the file's contents. */ - report_fetch_t *fetch_ctx; - fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx)); - fetch_ctx->info = info; - fetch_ctx->done_list = &ctx->done_fetches; - fetch_ctx->sess = ctx->sess; - fetch_ctx->conn = conn; + handler = svn_ra_serf__create_handler(ctx->sess, file->pool); - handler = apr_pcalloc(info->dir->pool, sizeof(*handler)); - - handler->handler_pool = info->dir->pool; handler->method = "GET"; - handler->path = fetch_ctx->info->url; + handler->path = file->url; - handler->conn = conn; - handler->session = ctx->sess; + handler->conn = conn; /* Explicit scheduling */ handler->custom_accept_encoding = TRUE; + handler->no_dav_headers = TRUE; handler->header_delegate = headers_fetch; handler->header_delegate_baton = fetch_ctx; @@ -1584,6 +1487,9 @@ fetch_file(report_context_t *ctx, report_info_t *info) handler->response_error = cancel_fetch; handler->response_error_baton = fetch_ctx; + handler->done_delegate = file_fetch_done; + handler->done_delegate_baton = fetch_ctx; + fetch_ctx->handler = handler; svn_ra_serf__request_create(handler); @@ -1591,944 +1497,691 @@ fetch_file(report_context_t *ctx, report_info_t *info) ctx->num_active_fetches++; } } - else if (info->propfind_handler) - { - svn_ra_serf__list_t *list_item; - list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item)); - list_item->data = info; - list_item->next = ctx->file_propchanges_only; - ctx->file_propchanges_only = list_item; - } - else - { - /* No propfind or GET request. Just handle the prop changes now. */ - SVN_ERR(handle_propchange_only(info, info->pool)); - } - - if (ctx->num_active_fetches + ctx->num_active_propfinds - > REQUEST_COUNT_TO_PAUSE) - ctx->parser_ctx->paused = TRUE; - - return SVN_NO_ERROR; -} - - -/** XML callbacks for our update-report response parsing */ - -static svn_error_t * -start_report(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - const char **attrs, - apr_pool_t *scratch_pool) -{ - report_context_t *ctx = parser->user_data; - report_state_e state; - - state = parser->state->current_state; - - if (state == NONE && strcmp(name.name, "update-report") == 0) - { - const char *val; - - val = svn_xml_get_attr_value("inline-props", attrs); - if (val && (strcmp(val, "true") == 0)) - ctx->add_props_included = TRUE; - - val = svn_xml_get_attr_value("send-all", attrs); - if (val && (strcmp(val, "true") == 0)) - { - ctx->send_all_mode = TRUE; - - /* All properties are included in send-all mode. */ - ctx->add_props_included = TRUE; - } - } - else if (state == NONE && strcmp(name.name, "target-revision") == 0) - { - const char *rev; - - rev = svn_xml_get_attr_value("rev", attrs); - - if (!rev) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in target-revision element")); - } - - SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton, - SVN_STR_TO_REV(rev), - ctx->sess->pool)); - } - else if (state == NONE && strcmp(name.name, "open-directory") == 0) + /* If needed, create the PROPFIND to retrieve the file's properties. */ + if (file->fetch_props) { - const char *rev; - report_info_t *info; - - rev = svn_xml_get_attr_value("rev", attrs); - - if (!rev) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-directory element")); - } + SVN_ERR(svn_ra_serf__create_propfind_handler(&file->propfind_handler, + ctx->sess, file->url, + ctx->target_rev, "0", + all_props, + set_file_props, file, + file->pool)); + file->propfind_handler->conn = conn; /* Explicit scheduling */ - info = push_state(parser, ctx, OPEN_DIR); + file->propfind_handler->done_delegate = file_props_done; + file->propfind_handler->done_delegate_baton = file; - info->base_rev = SVN_STR_TO_REV(rev); - info->dir->base_rev = info->base_rev; - info->fetch_props = TRUE; - - info->dir->base_name = ""; - info->dir->name = ""; - - info->base_name = info->dir->base_name; - info->name = info->dir->name; - - info->dir->repos_relpath = svn_hash_gets(ctx->switched_paths, ""); + /* Create a serf request for the PROPFIND. */ + svn_ra_serf__request_create(file->propfind_handler); - if (!info->dir->repos_relpath) - SVN_ERR(svn_ra_serf__get_relative_path(&info->dir->repos_relpath, - ctx->sess->session_url.path, - ctx->sess, ctx->conn, - info->dir->pool)); - } - else if (state == NONE) - { - /* do nothing as we haven't seen our valid start tag yet. */ + ctx->num_active_propfinds++; } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "open-directory") == 0) - { - const char *rev, *dirname; - report_dir_t *dir; - report_info_t *info; - - rev = svn_xml_get_attr_value("rev", attrs); - - if (!rev) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-directory element")); - } - - dirname = svn_xml_get_attr_value("name", attrs); - if (!dirname) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in open-directory element")); - } + if (file->fetch_props || file->fetch_file) + return SVN_NO_ERROR; - info = push_state(parser, ctx, OPEN_DIR); - dir = info->dir; + /* Somehow we are done; probably via the local cache. + Close the file and release memory, etc. */ - info->base_rev = SVN_STR_TO_REV(rev); - dir->base_rev = info->base_rev; + return svn_error_trace(close_file(file, scratch_pool)); +} - info->fetch_props = FALSE; +/* Implements svn_ra_serf__prop_func */ +static svn_error_t * +set_dir_prop(void *baton, + const char *path, + const char *ns, + const char *name, + const svn_string_t *val, + apr_pool_t *scratch_pool) +{ + dir_baton_t *dir = baton; + report_context_t *ctx = dir->ctx; + const char *prop_name; - dir->base_name = apr_pstrdup(dir->pool, dirname); - info->base_name = dir->base_name; + prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool); + if (prop_name == NULL) + return SVN_NO_ERROR; - /* Expand our name. */ - dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name, - dir->pool); - info->name = dir->name; + SVN_ERR(ensure_dir_opened(dir, scratch_pool)); - dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->name); + SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton, + prop_name, val, + scratch_pool)); + return SVN_NO_ERROR; +} - if (!dir->repos_relpath) - dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath, - dir->base_name, dir->pool); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "add-directory") == 0) - { - const char *dir_name, *cf, *cr; - report_dir_t *dir; - report_info_t *info; +/* Implements svn_ra_serf__response_done_delegate_t */ +static svn_error_t * +dir_props_done(serf_request_t *request, + void *baton, + apr_pool_t *scratch_pool) +{ + dir_baton_t *dir = baton; + svn_ra_serf__handler_t *handler = dir->propfind_handler; - dir_name = svn_xml_get_attr_value("name", attrs); - if (!dir_name) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in add-directory element")); - } - cf = svn_xml_get_attr_value("copyfrom-path", attrs); - cr = svn_xml_get_attr_value("copyfrom-rev", attrs); + if (handler->server_error) + return svn_ra_serf__server_error_create(handler, scratch_pool); - info = push_state(parser, ctx, ADD_DIR); + if (handler->sline.code != 207) + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); - dir = info->dir; + dir->ctx->num_active_propfinds--; - dir->base_name = apr_pstrdup(dir->pool, dir_name); - info->base_name = dir->base_name; + /* Closing the directory will automatically deliver the propfind props. + * + * Note that closing the directory may dispose the pool containing the + * handler, which is only a valid operation in this callback, as after + * this callback serf assumes the request is done. */ - /* Expand our name. */ - dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name, - dir->pool); - info->name = dir->name; + return svn_error_trace(maybe_close_dir(dir)); +} - info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL; - info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM; +/* Initiates additional requests needed for a directory when not in "send-all" + * mode */ +static svn_error_t * +fetch_for_dir(dir_baton_t *dir, + apr_pool_t *scratch) +{ + report_context_t *ctx = dir->ctx; + svn_ra_serf__connection_t *conn; - /* Mark that we don't have a base. */ - info->base_rev = SVN_INVALID_REVNUM; - dir->base_rev = info->base_rev; + /* Open extra connections if we have enough requests to send. */ + if (ctx->sess->num_conns < ctx->sess->max_connections) + SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches + + ctx->num_active_propfinds)); - /* If the server isn't included properties for added items, - we'll need to fetch them ourselves. */ - if (! ctx->add_props_included) - dir->fetch_props = TRUE; + /* What connection should we go on? */ + conn = get_best_connection(ctx); - dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath, - dir->base_name, dir->pool); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "open-file") == 0) + /* If needed, create the PROPFIND to retrieve the file's properties. */ + if (dir->fetch_props) { - const char *file_name, *rev; - report_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); - - if (!file_name) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in open-file element")); - } + SVN_ERR(svn_ra_serf__create_propfind_handler(&dir->propfind_handler, + ctx->sess, dir->url, + ctx->target_rev, "0", + all_props, + set_dir_prop, dir, + dir->pool)); - rev = svn_xml_get_attr_value("rev", attrs); + dir->propfind_handler->conn = conn; + dir->propfind_handler->done_delegate = dir_props_done; + dir->propfind_handler->done_delegate_baton = dir; - if (!rev) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing revision attr in open-file element")); - } - - info = push_state(parser, ctx, OPEN_FILE); - - info->base_rev = SVN_STR_TO_REV(rev); - info->fetch_props = FALSE; + /* Create a serf request for the PROPFIND. */ + svn_ra_serf__request_create(dir->propfind_handler); - info->base_name = apr_pstrdup(info->pool, file_name); - info->name = NULL; + ctx->num_active_propfinds++; } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "add-file") == 0) - { - const char *file_name, *cf, *cr; - report_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); - cf = svn_xml_get_attr_value("copyfrom-path", attrs); - cr = svn_xml_get_attr_value("copyfrom-rev", attrs); - - if (!file_name) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in add-file element")); - } - - info = push_state(parser, ctx, ADD_FILE); - - info->base_rev = SVN_INVALID_REVNUM; - - /* If the server isn't in "send-all" mode, we should expect to - fetch contents for added files. */ - if (! ctx->send_all_mode) - info->fetch_file = TRUE; + else + SVN_ERR_MALFUNCTION(); - /* If the server isn't included properties for added items, - we'll need to fetch them ourselves. */ - if (! ctx->add_props_included) - info->fetch_props = TRUE; + return SVN_NO_ERROR; +} - info->base_name = apr_pstrdup(info->pool, file_name); - info->name = NULL; + +/** XML callbacks for our update-report response parsing */ - info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL; - info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM; +/* Conforms to svn_ra_serf__xml_opened_t */ +static svn_error_t * +update_opened(svn_ra_serf__xml_estate_t *xes, + void *baton, + int entered_state, + const svn_ra_serf__dav_props_t *tag, + apr_pool_t *scratch_pool) +{ + report_context_t *ctx = baton; + apr_hash_t *attrs; - info->final_sha1_checksum = - svn_xml_get_attr_value("sha1-checksum", attrs); - if (info->final_sha1_checksum) - info->final_sha1_checksum = apr_pstrdup(info->pool, - info->final_sha1_checksum); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "delete-entry") == 0) + switch (entered_state) { - const char *file_name; - const char *rev_str; - report_info_t *info; - apr_pool_t *tmppool; - const char *full_path; - svn_revnum_t delete_rev = SVN_INVALID_REVNUM; - - file_name = svn_xml_get_attr_value("name", attrs); - - if (!file_name) + case UPDATE_REPORT: { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in delete-entry element")); - } - - rev_str = svn_xml_get_attr_value("rev", attrs); - if (rev_str) /* Not available on older repositories! */ - delete_rev = SVN_STR_TO_REV(rev_str); - - info = parser->state->private; - - SVN_ERR(ensure_dir_opened(info->dir)); + const char *val; - tmppool = svn_pool_create(info->dir->dir_baton_pool); + attrs = svn_ra_serf__xml_gather_since(xes, UPDATE_REPORT); + val = svn_hash_gets(attrs, "inline-props"); - full_path = svn_relpath_join(info->dir->name, file_name, tmppool); + if (val && (strcmp(val, "true") == 0)) + ctx->add_props_included = TRUE; - SVN_ERR(ctx->update_editor->delete_entry(full_path, - delete_rev, - info->dir->dir_baton, - tmppool)); + val = svn_hash_gets(attrs, "send-all"); - svn_pool_destroy(tmppool); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "absent-directory") == 0) - { - const char *file_name; - report_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); + if (val && (strcmp(val, "true") == 0)) + { + ctx->send_all_mode = TRUE; - if (!file_name) - { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in absent-directory element")); + /* All properties are included in send-all mode. */ + ctx->add_props_included = TRUE; + } } + break; - info = parser->state->private; - - SVN_ERR(ensure_dir_opened(info->dir)); - - SVN_ERR(ctx->update_editor->absent_directory( - svn_relpath_join(info->name, file_name, - info->dir->pool), - info->dir->dir_baton, - info->dir->pool)); - } - else if ((state == OPEN_DIR || state == ADD_DIR) && - strcmp(name.name, "absent-file") == 0) - { - const char *file_name; - report_info_t *info; - - file_name = svn_xml_get_attr_value("name", attrs); - - if (!file_name) + case OPEN_DIR: + case ADD_DIR: { - return svn_error_create( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in absent-file element")); - } - - info = parser->state->private; + dir_baton_t *dir; + const char *name; + attrs = svn_ra_serf__xml_gather_since(xes, entered_state); - SVN_ERR(ensure_dir_opened(info->dir)); + name = svn_hash_gets(attrs, "name"); + if (!name) + name = ""; - SVN_ERR(ctx->update_editor->absent_file( - svn_relpath_join(info->name, file_name, - info->dir->pool), - info->dir->dir_baton, - info->dir->pool)); - } - else if (state == OPEN_DIR || state == ADD_DIR) - { - report_info_t *info; - - if (strcmp(name.name, "checked-in") == 0) - { - info = push_state(parser, ctx, IGNORE_PROP_NAME); - info->prop_ns = name.namespace; - info->prop_name = apr_pstrdup(parser->state->pool, name.name); - info->prop_encoding = NULL; - svn_stringbuf_setempty(info->prop_value); - } - else if (strcmp(name.name, "set-prop") == 0 || - strcmp(name.name, "remove-prop") == 0) - { - const char *full_prop_name; - const char *colon; - - info = push_state(parser, ctx, PROP); + SVN_ERR(create_dir_baton(&dir, ctx, name, scratch_pool)); - full_prop_name = svn_xml_get_attr_value("name", attrs); - if (!full_prop_name) + if (entered_state == OPEN_DIR) { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in %s element"), - name.name); - } + apr_int64_t base_rev; - colon = strchr(full_prop_name, ':'); - - if (colon) - colon++; + SVN_ERR(svn_cstring_atoi64(&base_rev, + svn_hash_gets(attrs, "rev"))); + dir->base_rev = (svn_revnum_t)base_rev; + } else - colon = full_prop_name; - - info->prop_ns = apr_pstrmemdup(info->dir->pool, full_prop_name, - colon - full_prop_name); - info->prop_name = apr_pstrdup(parser->state->pool, colon); - info->prop_encoding = svn_xml_get_attr_value("encoding", attrs); - svn_stringbuf_setempty(info->prop_value); - } - else if (strcmp(name.name, "prop") == 0) - { - /* need to fetch it. */ - push_state(parser, ctx, NEED_PROP_NAME); - } - else if (strcmp(name.name, "fetch-props") == 0) - { - info = parser->state->private; - - info->dir->fetch_props = TRUE; - } - else - { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Unknown tag '%s' while at state %d"), - name.name, state); - } - - } - else if (state == OPEN_FILE || state == ADD_FILE) - { - report_info_t *info; + { + dir->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path"); - if (strcmp(name.name, "checked-in") == 0) - { - info = push_state(parser, ctx, IGNORE_PROP_NAME); - info->prop_ns = name.namespace; - info->prop_name = apr_pstrdup(parser->state->pool, name.name); - info->prop_encoding = NULL; - svn_stringbuf_setempty(info->prop_value); - } - else if (strcmp(name.name, "prop") == 0) - { - /* need to fetch it. */ - push_state(parser, ctx, NEED_PROP_NAME); - } - else if (strcmp(name.name, "fetch-props") == 0) - { - info = parser->state->private; + if (dir->copyfrom_path) + { + apr_int64_t copyfrom_rev; + const char *copyfrom_rev_str; + dir->copyfrom_path = svn_fspath__canonicalize( + dir->copyfrom_path, + dir->pool); - info->fetch_props = TRUE; - } - else if (strcmp(name.name, "fetch-file") == 0) - { - info = parser->state->private; - info->base_checksum = svn_xml_get_attr_value("base-checksum", attrs); + copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev"); + + if (!copyfrom_rev_str) + return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND, + NULL, + _("Missing '%s' attribute"), + "copyfrom-rev"); - if (info->base_checksum) - info->base_checksum = apr_pstrdup(info->pool, info->base_checksum); + SVN_ERR(svn_cstring_atoi64(©from_rev, copyfrom_rev_str)); - info->final_sha1_checksum = - svn_xml_get_attr_value("sha1-checksum", attrs); - if (info->final_sha1_checksum) - info->final_sha1_checksum = apr_pstrdup(info->pool, - info->final_sha1_checksum); + dir->copyfrom_rev = (svn_revnum_t)copyfrom_rev; + } - info->fetch_file = TRUE; + if (! ctx->add_props_included) + dir->fetch_props = TRUE; + } } - else if (strcmp(name.name, "set-prop") == 0 || - strcmp(name.name, "remove-prop") == 0) + break; + case OPEN_FILE: + case ADD_FILE: { - const char *full_prop_name; - const char *colon; + file_baton_t *file; + + attrs = svn_ra_serf__xml_gather_since(xes, entered_state); - info = push_state(parser, ctx, PROP); + SVN_ERR(create_file_baton(&file, ctx, svn_hash_gets(attrs, "name"), + scratch_pool)); - full_prop_name = svn_xml_get_attr_value("name", attrs); - if (!full_prop_name) + if (entered_state == OPEN_FILE) { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Missing name attr in %s element"), - name.name); - } - colon = strchr(full_prop_name, ':'); + apr_int64_t base_rev; - if (colon) - colon++; + SVN_ERR(svn_cstring_atoi64(&base_rev, + svn_hash_gets(attrs, "rev"))); + file->base_rev = (svn_revnum_t)base_rev; + } else - colon = full_prop_name; + { + const char *sha1_checksum; + file->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path"); + + if (file->copyfrom_path) + { + apr_int64_t copyfrom_rev; + const char *copyfrom_rev_str; + + file->copyfrom_path = svn_fspath__canonicalize( + file->copyfrom_path, + file->pool); + + copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev"); + + if (!copyfrom_rev_str) + return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND, + NULL, + _("Missing '%s' attribute"), + "copyfrom-rev"); - info->prop_ns = apr_pstrmemdup(info->dir->pool, full_prop_name, - colon - full_prop_name); - info->prop_name = apr_pstrdup(parser->state->pool, colon); - info->prop_encoding = svn_xml_get_attr_value("encoding", attrs); - svn_stringbuf_setempty(info->prop_value); + SVN_ERR(svn_cstring_atoi64(©from_rev, copyfrom_rev_str)); + + file->copyfrom_rev = (svn_revnum_t)copyfrom_rev; + } + + sha1_checksum = svn_hash_gets(attrs, "sha1-checksum"); + if (sha1_checksum) + { + SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum, + svn_checksum_sha1, + sha1_checksum, + file->pool)); + } + + /* If the server isn't in "send-all" mode, we should expect to + fetch contents for added files. */ + if (! ctx->send_all_mode) + file->fetch_file = TRUE; + + /* If the server isn't included properties for added items, + we'll need to fetch them ourselves. */ + if (! ctx->add_props_included) + file->fetch_props = TRUE; + } } - else if (strcmp(name.name, "txdelta") == 0) + break; + + case TXDELTA: { + file_baton_t *file = ctx->cur_file; + const char *base_checksum; + /* Pre 1.2, mod_dav_svn was using tags (in addition to s and such) when *not* in "send-all" mode. As a client, we're smart enough to know that's wrong, so we'll just ignore these tags. */ - if (ctx->send_all_mode) - { - const svn_delta_editor_t *update_editor = ctx->update_editor; + if (! ctx->send_all_mode) + break; - info = push_state(parser, ctx, TXDELTA); + file->fetch_file = FALSE; - if (! info->file_baton) - { - SVN_ERR(open_updated_file(info, FALSE, info->pool)); - } + attrs = svn_ra_serf__xml_gather_since(xes, entered_state); + base_checksum = svn_hash_gets(attrs, "base-checksum"); + + if (base_checksum) + SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum, + svn_checksum_md5, base_checksum, + file->pool)); + + SVN_ERR(open_file_txdelta(ctx->cur_file, scratch_pool)); - info->base_checksum = svn_xml_get_attr_value("base-checksum", - attrs); - SVN_ERR(update_editor->apply_textdelta(info->file_baton, - info->base_checksum, - info->editor_pool, - &info->textdelta, - &info->textdelta_baton)); - info->svndiff_decoder = svn_txdelta_parse_svndiff( - info->textdelta, - info->textdelta_baton, - TRUE, info->pool); - info->base64_decoder = svn_base64_decode(info->svndiff_decoder, - info->pool); + if (ctx->cur_file->txdelta != svn_delta_noop_window_handler) + { + svn_stream_t *decoder; + + decoder = svn_txdelta_parse_svndiff(file->txdelta, + file->txdelta_baton, + TRUE /* error early close*/, + file->pool); + + file->txdelta_stream = svn_base64_decode(decoder, file->pool); } } - else + break; + + case FETCH_PROPS: { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Unknown tag '%s' while at state %d"), - name.name, state); - } - } - else if (state == IGNORE_PROP_NAME) - { - report_info_t *info = push_state(parser, ctx, PROP); - info->prop_encoding = svn_xml_get_attr_value("encoding", attrs); - } - else if (state == NEED_PROP_NAME) - { - report_info_t *info; + /* Subversion <= 1.6 servers will return a fetch-props element on + open-file and open-dir when non entry props were changed in + !send-all mode. In turn we fetch the full set of properties + and send all of those as *changes* to the editor. So these + editors have to be aware that they receive-non property changes. + (In case of incomplete directories they have to be aware anyway) - info = push_state(parser, ctx, PROP); + In r1063337 this behavior was changed in mod_dav_svn to always + send property changes inline in these cases. (See issue #3657) - info->prop_ns = name.namespace; - info->prop_name = apr_pstrdup(parser->state->pool, name.name); - info->prop_encoding = svn_xml_get_attr_value("encoding", attrs); - svn_stringbuf_setempty(info->prop_value); + Note that before that change the property changes to the last_* + entry props were already inlined via specific xml elements. */ + if (ctx->cur_file) + ctx->cur_file->fetch_props = TRUE; + else if (ctx->cur_dir) + ctx->cur_dir->fetch_props = TRUE; + } + break; } return SVN_NO_ERROR; } + + +/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * -end_report(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - apr_pool_t *scratch_pool) +update_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) { - report_context_t *ctx = parser->user_data; - report_state_e state; - - state = parser->state->current_state; + report_context_t *ctx = baton; - if (state == NONE) + switch (leaving_state) { - if (strcmp(name.name, "update-report") == 0) - { - ctx->report_completed = TRUE; - } - else + case UPDATE_REPORT: + ctx->done = TRUE; + break; + case TARGET_REVISION: { - /* nothing to close yet. */ - return SVN_NO_ERROR; - } - } + const char *revstr = svn_hash_gets(attrs, "rev"); + apr_int64_t rev; - if (((state == OPEN_DIR && (strcmp(name.name, "open-directory") == 0)) || - (state == ADD_DIR && (strcmp(name.name, "add-directory") == 0)))) - { - const char *checked_in_url; - report_info_t *info = parser->state->private; - - /* We've now closed this directory; note it. */ - info->dir->tag_closed = TRUE; + SVN_ERR(svn_cstring_atoi64(&rev, revstr)); - /* go fetch info->file_name from DAV:checked-in */ - checked_in_url = - svn_ra_serf__get_ver_prop(info->dir->props, info->base_name, - info->base_rev, "DAV:", "checked-in"); - - /* If we were expecting to have the properties and we aren't able to - * get it, bail. - */ - if (!checked_in_url && - (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props)) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("The REPORT or PROPFIND response did not " - "include the requested checked-in value")); + SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton, + (svn_revnum_t)rev, + scratch_pool)); } + break; - info->dir->url = checked_in_url; + case CHECKED_IN_HREF: + if (ctx->cur_file) + ctx->cur_file->url = apr_pstrdup(ctx->cur_file->pool, cdata->data); + else + ctx->cur_dir->url = apr_pstrdup(ctx->cur_dir->pool, cdata->data); + break; - /* At this point, we should have the checked-in href. - * If needed, create the PROPFIND to retrieve the dir's properties. - */ - if (info->dir->fetch_props) - { - svn_ra_serf__list_t *list_item; - - SVN_ERR(svn_ra_serf__deliver_props(&info->dir->propfind_handler, - info->dir->props, ctx->sess, - get_best_connection(ctx), - info->dir->url, - ctx->target_rev, "0", - all_props, - &ctx->done_dir_propfinds, - info->dir->pool)); - SVN_ERR_ASSERT(info->dir->propfind_handler); - - /* Create a serf request for the PROPFIND. */ - svn_ra_serf__request_create(info->dir->propfind_handler); - - ctx->num_active_propfinds++; - - list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item)); - list_item->data = info->dir; - list_item->next = ctx->active_dir_propfinds; - ctx->active_dir_propfinds = list_item; - - if (ctx->num_active_fetches + ctx->num_active_propfinds - > REQUEST_COUNT_TO_PAUSE) - ctx->parser_ctx->paused = TRUE; - } - else + case SET_PROP: + case REMOVE_PROP: { - info->dir->propfind_handler = NULL; - } + const char *name = svn_hash_gets(attrs, "name"); + const char *encoding; + const svn_string_t *value; - /* See if this directory (and perhaps even parents of that) can - be closed now. This is likely to be the case only if we - didn't need to contact the server for supplemental - information required to handle any of this directory's - children. */ - SVN_ERR(maybe_close_dir_chain(info->dir)); - svn_ra_serf__xml_pop_state(parser); - } - else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0) - { - report_info_t *info = parser->state->private; + if (leaving_state == REMOVE_PROP) + value = NULL; + else if ((encoding = svn_hash_gets(attrs, "encoding"))) + { + if (strcmp(encoding, "base64") != 0) + return svn_error_createf(SVN_ERR_XML_UNKNOWN_ENCODING, NULL, + _("Got unrecognized encoding '%s'"), + encoding); - /* Expand our full name now if we haven't done so yet. */ - if (!info->name) - { - info->name = svn_relpath_join(info->dir->name, info->base_name, - info->pool); - } + value = svn_base64_decode_string(cdata, scratch_pool); + } + else + value = cdata; - if (info->lock_token && !info->fetch_props) - info->fetch_props = TRUE; + if (ctx->cur_file) + { + file_baton_t *file = ctx->cur_file; - /* If possible, we'd like to fetch only a delta against a - * version of the file we already have in our working copy, - * rather than fetching a fulltext. - * - * In HTTP v2, we can simply construct the URL we need given the - * repos_relpath and base revision number. - */ - if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess)) - { - const char *repos_relpath; + if (value + || ctx->add_props_included + || SVN_IS_VALID_REVNUM(file->base_rev)) + { + SVN_ERR(ensure_file_opened(file, scratch_pool)); - /* If this file is switched vs the editor root we should provide - its real url instead of the one calculated from the session root. - */ - repos_relpath = svn_hash_gets(ctx->switched_paths, info->name); + SVN_ERR(ctx->editor->change_file_prop(file->file_baton, + name, + value, + scratch_pool)); + } + else + { + if (!file->remove_props) + file->remove_props = apr_hash_make(file->pool); - if (!repos_relpath) + svn_hash_sets(file->remove_props, + apr_pstrdup(file->pool, name), + ""); + } + } + else { - if (ctx->root_is_switched) + dir_baton_t *dir = ctx->cur_dir; + + if (value + || ctx->add_props_included + || SVN_IS_VALID_REVNUM(dir->base_rev)) { - /* We are updating a direct target (most likely a file) - that is switched vs its parent url */ - SVN_ERR_ASSERT(*svn_relpath_dirname(info->name, info->pool) - == '\0'); + SVN_ERR(ensure_dir_opened(dir, scratch_pool)); - repos_relpath = svn_hash_gets(ctx->switched_paths, ""); + SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton, + name, + value, + scratch_pool)); } else - repos_relpath = svn_relpath_join(info->dir->repos_relpath, - info->base_name, info->pool); - } - - info->delta_base = apr_psprintf(info->pool, "%s/%ld/%s", - ctx->sess->rev_root_stub, - info->base_rev, - svn_path_uri_encode(repos_relpath, - info->pool)); - } - else if (ctx->sess->wc_callbacks->get_wc_prop) - { - /* If we have a WC, we might be able to dive all the way into the WC - * to get the previous URL so we can do a differential GET with the - * base URL. - */ - const svn_string_t *value = NULL; - SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop( - ctx->sess->wc_callback_baton, info->name, - SVN_RA_SERF__WC_CHECKED_IN_URL, &value, info->pool)); + { + if (!dir->remove_props) + dir->remove_props = apr_hash_make(dir->pool); - info->delta_base = value ? value->data : NULL; + svn_hash_sets(dir->remove_props, + apr_pstrdup(dir->pool, name), + ""); + } + } } + break; - /* go fetch info->name from DAV:checked-in */ - info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name, - info->base_rev, "DAV:", "checked-in"); - if (!info->url) + case OPEN_DIR: + case ADD_DIR: { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("The REPORT or PROPFIND response did not " - "include the requested checked-in value")); - } + dir_baton_t *dir = ctx->cur_dir; + ctx->cur_dir = ctx->cur_dir->parent_dir; - /* If the server is in "send-all" mode, we might have opened the - file when we started seeing content for it. If we didn't get - any content for it, we still need to open the file. But in - any case, we can then immediately close it. */ - if (ctx->send_all_mode) - { - if (! info->file_baton) + if (dir->fetch_props && ! dir->url) + { + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("The REPORT response did not " + "include the requested checked-in " + "value")); + } + + if (!dir->fetch_props) + { + SVN_ERR(maybe_close_dir(dir)); + break; /* dir potentially no longer valid */ + } + else { - SVN_ERR(open_updated_file(info, FALSE, info->pool)); + /* Otherwise, if the server is *not* in "send-all" mode, we + are at a point where we can queue up the PROPFIND request */ + SVN_ERR(fetch_for_dir(dir, scratch_pool)); } - SVN_ERR(close_updated_file(info, info->pool)); - info->dir->ref_count--; } - /* Otherwise, if the server is *not* in "send-all" mode, we - should be at a point where we can queue up any auxiliary - content-fetching requests. */ - else + break; + + case OPEN_FILE: + case ADD_FILE: { - SVN_ERR(fetch_file(ctx, info)); - } + file_baton_t *file = ctx->cur_file; - svn_ra_serf__xml_pop_state(parser); - } - else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0) - { - report_info_t *info = parser->state->private; + ctx->cur_file = NULL; + /* go fetch info->name from DAV:checked-in */ - /* go fetch info->name from DAV:checked-in */ - info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name, - info->base_rev, "DAV:", "checked-in"); - if (!info->url) - { - return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("The REPORT or PROPFIND response did not " - "include the requested checked-in value")); - } + if ((file->fetch_file || file->fetch_props) && ! file->url) + { + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("The REPORT response did not " + "include the requested checked-in " + "value")); + } - /* If the server is in "send-all" mode, we might have opened the - file when we started seeing content for it. If we didn't get - any content for it, we still need to open the file. But in - any case, we can then immediately close it. */ - if (ctx->send_all_mode) - { - if (! info->file_baton) + /* If the server is in "send-all" mode or didn't get further work, + we can now close the file */ + if (! file->fetch_file && ! file->fetch_props) { - SVN_ERR(open_updated_file(info, FALSE, info->pool)); + SVN_ERR(close_file(file, scratch_pool)); + break; /* file is no longer valid */ + } + else + { + /* Otherwise, if the server is *not* in "send-all" mode, we + should be at a point where we can queue up any auxiliary + content-fetching requests. */ + SVN_ERR(fetch_for_file(file, scratch_pool)); } - SVN_ERR(close_updated_file(info, info->pool)); - info->dir->ref_count--; - } - /* Otherwise, if the server is *not* in "send-all" mode, we - should be at a point where we can queue up any auxiliary - content-fetching requests. */ - else - { - SVN_ERR(fetch_file(ctx, info)); } + break; - svn_ra_serf__xml_pop_state(parser); - } - else if (state == TXDELTA && strcmp(name.name, "txdelta") == 0) - { - report_info_t *info = parser->state->private; + case MD5_CHECKSUM: + SVN_ERR(svn_checksum_parse_hex(&ctx->cur_file->final_md5_checksum, + svn_checksum_md5, + cdata->data, + ctx->cur_file->pool)); + break; - /* Pre 1.2, mod_dav_svn was using tags (in addition to - s and such) when *not* in "send-all" mode. As a - client, we're smart enough to know that's wrong, so when not - in "receiving-all" mode, we'll ignore these tags. */ - if (ctx->send_all_mode) + case FETCH_FILE: { - SVN_ERR(svn_stream_close(info->base64_decoder)); - } + file_baton_t *file = ctx->cur_file; + const char *base_checksum = svn_hash_gets(attrs, "base-checksum"); + const char *sha1_checksum = svn_hash_gets(attrs, "sha1-checksum"); - svn_ra_serf__xml_pop_state(parser); - } - else if (state == PROP) - { - /* We need to move the prop_ns, prop_name, and prop_value into the - * same lifetime as the dir->pool. - */ - svn_ra_serf__ns_t *ns, *ns_name_match; - svn_boolean_t found = FALSE; - report_info_t *info; - report_dir_t *dir; - apr_hash_t *props; - const svn_string_t *set_val_str; - apr_pool_t *pool; - - info = parser->state->private; - dir = info->dir; - - /* We're going to be slightly tricky. We don't care what the ->url - * field is here at this point. So, we're going to stick a single - * copy of the property name inside of the ->url field. - */ - ns_name_match = NULL; - for (ns = dir->ns_list; ns; ns = ns->next) - { - if (strcmp(ns->namespace, info->prop_ns) == 0) - { - ns_name_match = ns; - if (strcmp(ns->url, info->prop_name) == 0) - { - found = TRUE; - break; - } - } + if (base_checksum) + SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum, + svn_checksum_md5, base_checksum, + file->pool)); + + /* Property is duplicated between add-file and fetch-file */ + if (sha1_checksum && !file->final_sha1_checksum) + SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum, + svn_checksum_sha1, + sha1_checksum, + file->pool)); + + /* Some 0.3x mod_dav_svn wrote both txdelta and fetch-file + elements in send-all mode. (See neon for history) */ + if (! ctx->send_all_mode) + file->fetch_file = TRUE; } + break; - if (!found) + case DELETE_ENTRY: { - ns = apr_palloc(dir->pool, sizeof(*ns)); - if (!ns_name_match) - { - ns->namespace = apr_pstrdup(dir->pool, info->prop_ns); - } + const char *name = svn_hash_gets(attrs, "name"); + const char *revstr; + apr_int64_t delete_rev; + + SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool)); + + revstr = svn_hash_gets(attrs, "rev"); + + if (revstr) + SVN_ERR(svn_cstring_atoi64(&delete_rev, revstr)); else - { - ns->namespace = ns_name_match->namespace; - } - ns->url = apr_pstrdup(dir->pool, info->prop_name); + delete_rev = SVN_INVALID_REVNUM; - ns->next = dir->ns_list; - dir->ns_list = ns; + SVN_ERR(ctx->editor->delete_entry( + svn_relpath_join(ctx->cur_dir->relpath, + name, + scratch_pool), + (svn_revnum_t)delete_rev, + ctx->cur_dir->dir_baton, + scratch_pool)); } + break; - if (strcmp(name.name, "remove-prop") != 0) + case ABSENT_DIR: { - props = info->props; - pool = info->pool; + const char *name = svn_hash_gets(attrs, "name"); + + SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool)); + + SVN_ERR(ctx->editor->absent_directory( + svn_relpath_join(ctx->cur_dir->relpath, + name, scratch_pool), + ctx->cur_dir->dir_baton, + scratch_pool)); } - else + break; + case ABSENT_FILE: { - props = dir->removed_props; - pool = dir->pool; - svn_stringbuf_setempty(info->prop_value); + const char *name = svn_hash_gets(attrs, "name"); + + SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool)); + + SVN_ERR(ctx->editor->absent_file( + svn_relpath_join(ctx->cur_dir->relpath, + name, scratch_pool), + ctx->cur_dir->dir_baton, + scratch_pool)); } + break; - if (info->prop_encoding) + case TXDELTA: { - if (strcmp(info->prop_encoding, "base64") == 0) - { - svn_string_t tmp; + file_baton_t *file = ctx->cur_file; - /* Don't use morph_info_string cuz we need prop_value to - remain usable. */ - tmp.data = info->prop_value->data; - tmp.len = info->prop_value->len; - - set_val_str = svn_base64_decode_string(&tmp, pool); - } - else + if (file->txdelta_stream) { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, - NULL, - _("Got unrecognized encoding '%s'"), - info->prop_encoding); + SVN_ERR(svn_stream_close(file->txdelta_stream)); + file->txdelta_stream = NULL; } } - else + break; + + case VERSION_NAME: + case CREATIONDATE: + case CREATOR_DISPLAYNAME: { - set_val_str = svn_string_create_from_buf(info->prop_value, pool); - } + /* Subversion <= 1.6 servers would return a fetch-props element on + open-file and open-dir when non entry props were changed in + !send-all mode. In turn we fetch the full set of properties and + send those as *changes* to the editor. So these editors have to + be aware that they receive non property changes. + (In case of incomplete directories they have to be aware anyway) + + In that case the last_* entry props are posted as 3 specific xml + elements, which we handle here. - svn_ra_serf__set_ver_prop(props, info->base_name, info->base_rev, - ns->namespace, ns->url, set_val_str, pool); + In r1063337 this behavior was changed in mod_dav_svn to always + send property changes inline in these cases. (See issue #3657) + */ - /* Advance handling: if we spotted the md5-checksum property on - the wire, remember it's value. */ - if (strcmp(ns->url, "md5-checksum") == 0 - && strcmp(ns->namespace, SVN_DAV_PROP_NS_DAV) == 0) - info->final_checksum = apr_pstrdup(info->pool, set_val_str->data); + const char *propname; - svn_ra_serf__xml_pop_state(parser); - } - else if (state == IGNORE_PROP_NAME || state == NEED_PROP_NAME) - { - svn_ra_serf__xml_pop_state(parser); + if (ctx->cur_file) + SVN_ERR(ensure_file_opened(ctx->cur_file, scratch_pool)); + else if (ctx->cur_dir) + SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool)); + else + break; + + switch (leaving_state) + { + case VERSION_NAME: + propname = SVN_PROP_ENTRY_COMMITTED_REV; + break; + case CREATIONDATE: + propname = SVN_PROP_ENTRY_COMMITTED_DATE; + break; + case CREATOR_DISPLAYNAME: + propname = SVN_PROP_ENTRY_LAST_AUTHOR; + break; + default: + SVN_ERR_MALFUNCTION(); /* Impossible to reach */ + } + + if (ctx->cur_file) + SVN_ERR(ctx->editor->change_file_prop(ctx->cur_file->file_baton, + propname, cdata, + scratch_pool)); + else + SVN_ERR(ctx->editor->change_dir_prop(ctx->cur_dir->dir_baton, + propname, cdata, + scratch_pool)); + } + break; } return SVN_NO_ERROR; } + +/* Conforms to svn_ra_serf__xml_cdata_t */ static svn_error_t * -cdata_report(svn_ra_serf__xml_parser_t *parser, +update_cdata(svn_ra_serf__xml_estate_t *xes, + void *baton, + int current_state, const char *data, apr_size_t len, apr_pool_t *scratch_pool) { - report_context_t *ctx = parser->user_data; - - UNUSED_CTX(ctx); - - if (parser->state->current_state == PROP) - { - report_info_t *info = parser->state->private; + report_context_t *ctx = baton; - svn_stringbuf_appendbytes(info->prop_value, data, len); - } - else if (parser->state->current_state == TXDELTA) + if (current_state == TXDELTA && ctx->cur_file + && ctx->cur_file->txdelta_stream) { - /* Pre 1.2, mod_dav_svn was using tags (in addition to - s and such) when *not* in "send-all" mode. As a - client, we're smart enough to know that's wrong, so when not - in "receiving-all" mode, we'll ignore these tags. */ - if (ctx->send_all_mode) - { - apr_size_t nlen = len; - report_info_t *info = parser->state->private; - - SVN_ERR(svn_stream_write(info->base64_decoder, data, &nlen)); - if (nlen != len) - { - /* Short write without associated error? "Can't happen." */ - return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, - _("Error writing to '%s': unexpected EOF"), - info->name); - } - } + SVN_ERR(svn_stream_write(ctx->cur_file->txdelta_stream, data, &len)); } return SVN_NO_ERROR; @@ -2544,7 +2197,8 @@ make_simple_xml_tag(svn_stringbuf_t **buf_p, const char *cdata, apr_pool_t *pool) { - svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname, NULL); + svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname, + SVN_VA_NULL); svn_xml_escape_cdata_cstring(buf_p, cdata, pool); svn_xml_make_close_tag(buf_p, pool, tagname); } @@ -2566,12 +2220,11 @@ set_path(void *report_baton, "lock-token", lock_token, "depth", svn_depth_to_word(depth), "start-empty", start_empty ? "true" : NULL, - NULL); + SVN_VA_NULL); svn_xml_escape_cdata_cstring(&buf, path, pool); svn_xml_make_close_tag(&buf, pool, "S:entry"); - SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len, - NULL, pool)); + SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len)); return SVN_NO_ERROR; } @@ -2586,8 +2239,7 @@ delete_path(void *report_baton, make_simple_xml_tag(&buf, "S:missing", path, pool); - SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len, - NULL, pool)); + SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len)); return SVN_NO_ERROR; } @@ -2619,12 +2271,10 @@ link_path(void *report_baton, _("Unable to parse URL '%s'"), url); } - SVN_ERR(svn_ra_serf__report_resource(&report_target, report->sess, - NULL, pool)); - SVN_ERR(svn_ra_serf__get_relative_path(&link, uri.path, report->sess, - NULL, pool)); + SVN_ERR(svn_ra_serf__report_resource(&report_target, report->sess, pool)); + SVN_ERR(svn_ra_serf__get_relative_path(&link, uri.path, report->sess, pool)); - link = apr_pstrcat(pool, "/", link, (char *)NULL); + link = apr_pstrcat(pool, "/", link, SVN_VA_NULL); svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry", "rev", apr_ltoa(pool, revision), @@ -2632,80 +2282,55 @@ link_path(void *report_baton, "depth", svn_depth_to_word(depth), "linkpath", link, "start-empty", start_empty ? "true" : NULL, - NULL); + SVN_VA_NULL); svn_xml_escape_cdata_cstring(&buf, path, pool); svn_xml_make_close_tag(&buf, pool, "S:entry"); - SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len, - NULL, pool)); + SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len)); /* Store the switch roots to allow generating repos_relpaths from just the working copy paths. (Needed for HTTPv2) */ path = apr_pstrdup(report->pool, path); - svn_hash_sets(report->switched_paths, - path, apr_pstrdup(report->pool, link + 1)); - - if (!*path) - report->root_is_switched = TRUE; - - return APR_SUCCESS; -} - -/** Minimum nr. of outstanding requests needed before a new connection is - * opened. */ -#define REQS_PER_CONN 8 + link = apr_pstrdup(report->pool, link + 1); + svn_hash_sets(report->switched_paths, path, link); -/** This function creates a new connection for this serf session, but only - * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is - * only one main connection open. - */ -static svn_error_t * -open_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs) -{ - /* For each REQS_PER_CONN outstanding requests open a new connection, with - * a minimum of 1 extra connection. */ - if (sess->num_conns == 1 || - ((num_active_reqs / REQS_PER_CONN) > sess->num_conns)) + if (!path[0] && report->update_target[0]) { - int cur = sess->num_conns; - apr_status_t status; - - sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur])); - sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool, - NULL, NULL); - sess->conns[cur]->last_status_code = -1; - sess->conns[cur]->session = sess; - status = serf_connection_create2(&sess->conns[cur]->conn, - sess->context, - sess->session_url, - svn_ra_serf__conn_setup, - sess->conns[cur], - svn_ra_serf__conn_closed, - sess->conns[cur], - sess->pool); - if (status) - return svn_ra_serf__wrap_err(status, NULL); - - sess->num_conns++; + /* The update root is switched. Make sure we store it the way + we expect it to find */ + svn_hash_sets(report->switched_paths, report->update_target, link); } - return SVN_NO_ERROR; + return APR_SUCCESS; } -/* Serf callback to create update request body bucket. */ +/* Serf callback to create update request body bucket. + Implements svn_ra_serf__request_body_delegate_t */ static svn_error_t * create_update_report_body(serf_bucket_t **body_bkt, void *baton, serf_bucket_alloc_t *alloc, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { report_context_t *report = baton; - apr_off_t offset; + body_create_baton_t *body = report->body; + + if (body->file) + { + apr_off_t offset; - offset = 0; - apr_file_seek(report->body_file, APR_SET, &offset); + offset = 0; + SVN_ERR(svn_io_file_seek(body->file, APR_SET, &offset, pool)); - *body_bkt = serf_bucket_file_create(report->body_file, alloc); + *body_bkt = serf_bucket_file_create(report->body->file, alloc); + } + else + { + *body_bkt = serf_bucket_simple_create(body->all_data, + body->total_bytes, + NULL, NULL, alloc); + } return SVN_NO_ERROR; } @@ -2714,7 +2339,8 @@ create_update_report_body(serf_bucket_t **body_bkt, static svn_error_t * setup_update_report_headers(serf_bucket_t *headers, void *baton, - apr_pool_t *pool) + apr_pool_t *pool /* request pool */, + apr_pool_t *scratch_pool) { report_context_t *report = baton; @@ -2732,368 +2358,347 @@ setup_update_report_headers(serf_bucket_t *headers, return SVN_NO_ERROR; } +/* Baton for update_delay_handler */ +typedef struct update_delay_baton_t +{ + report_context_t *report; + svn_spillbuf_t *spillbuf; + svn_ra_serf__response_handler_t inner_handler; + void *inner_handler_baton; +} update_delay_baton_t; + +/* Helper for update_delay_handler() and process_pending() to + call UDB->INNER_HANDLER with buffer pointed by DATA. */ static svn_error_t * -finish_report(void *report_baton, - apr_pool_t *pool) +process_buffer(update_delay_baton_t *udb, + serf_request_t *request, + const void *data, + apr_size_t len, + svn_boolean_t at_eof, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool) { - report_context_t *report = report_baton; - svn_ra_serf__session_t *sess = report->sess; - svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_parser_t *parser_ctx; - const char *report_target; - svn_stringbuf_t *buf = NULL; - apr_pool_t *iterpool = svn_pool_create(pool); + serf_bucket_t *tmp_bucket; svn_error_t *err; - apr_interval_time_t waittime_left = sess->timeout; - svn_xml_make_close_tag(&buf, iterpool, "S:update-report"); - SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len, - NULL, iterpool)); + /* ### This code (and the eagain bucket code) can probably be + ### simplified by using a bit of aggregate bucket magic. + ### See mail from Ivan to dev@s.a.o. */ + if (at_eof) + { + tmp_bucket = serf_bucket_simple_create(data, len, NULL, NULL, + alloc); + } + else + { + tmp_bucket = svn_ra_serf__create_bucket_with_eagain(data, len, + alloc); + } - /* 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(report->body_file); -#if APR_VERSION_AT_LEAST(1, 3, 0) - apr_file_buffer_set(report->body_file, NULL, 0); -#endif + /* If not at EOF create a bucket that finishes with EAGAIN, otherwise + use a standard bucket with default EOF handling */ + err = udb->inner_handler(request, tmp_bucket, + udb->inner_handler_baton, pool); - SVN_ERR(svn_ra_serf__report_resource(&report_target, sess, NULL, pool)); + /* And free the bucket explicitly to avoid growing request allocator + storage (in a loop) */ + serf_bucket_destroy(tmp_bucket); - /* create and deliver request */ - report->path = report_target; + return svn_error_trace(err); +} - handler = apr_pcalloc(pool, sizeof(*handler)); - handler->handler_pool = pool; - handler->method = "REPORT"; - handler->path = report->path; - handler->body_delegate = create_update_report_body; - handler->body_delegate_baton = report; - handler->body_type = "text/xml"; - handler->custom_accept_encoding = TRUE; - handler->header_delegate = setup_update_report_headers; - handler->header_delegate_baton = report; - handler->conn = sess->conns[0]; - handler->session = sess; +/* Delaying wrapping reponse handler, to avoid creating too many + requests to deliver efficiently */ +static svn_error_t * +update_delay_handler(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *scratch_pool) +{ + update_delay_baton_t *udb = handler_baton; + apr_status_t status; + apr_pool_t *iterpool = NULL; - parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); + if (! udb->spillbuf) + { + if (udb->report->send_all_mode) + { + /* Easy out... We only have one request, so avoid everything and just + call the inner handler. - parser_ctx->pool = pool; - parser_ctx->response_type = "update-report"; - parser_ctx->user_data = report; - parser_ctx->start = start_report; - parser_ctx->end = end_report; - parser_ctx->cdata = cdata_report; - parser_ctx->done = &report->done; + We will always get in the loop (below) on the first chunk, as only + the server can get us in true send-all mode */ - handler->response_handler = svn_ra_serf__handle_xml_parser; - handler->response_baton = parser_ctx; + return svn_error_trace(udb->inner_handler(request, response, + udb->inner_handler_baton, + scratch_pool)); + } - report->parser_ctx = parser_ctx; + while ((udb->report->num_active_fetches + udb->report->num_active_propfinds) + < REQUEST_COUNT_TO_RESUME) + { + const char *data; + apr_size_t len; + svn_boolean_t at_eof = FALSE; + svn_error_t *err; - svn_ra_serf__request_create(handler); + status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + return svn_ra_serf__wrap_err(status, NULL); + else if (APR_STATUS_IS_EOF(status)) + udb->report->report_received = at_eof = TRUE; - /* Open the first extra connection. */ - SVN_ERR(open_connection_if_needed(sess, 0)); + if (!iterpool) + iterpool = svn_pool_create(scratch_pool); + else + svn_pool_clear(iterpool); - sess->cur_conn = 1; + if (len == 0 && !at_eof) + return svn_ra_serf__wrap_err(status, NULL); - /* Note that we may have no active GET or PROPFIND requests, yet the - processing has not been completed. This could be from a delay on the - network or because we've spooled the entire response into our "pending" - content of the XML parser. The DONE flag will get set when all the - XML content has been received *and* parsed. */ - while (!report->done - || report->num_active_fetches - || report->num_active_propfinds) + err = process_buffer(udb, request, data, len, at_eof, + serf_request_get_alloc(request), + iterpool); + + if (err && SERF_BUCKET_READ_ERROR(err->apr_err)) + return svn_error_trace(err); + else if (err && APR_STATUS_IS_EAGAIN(err->apr_err)) + { + svn_error_clear(err); /* Throttling is working ok */ + } + else if (err && (APR_STATUS_IS_EOF(err->apr_err))) + { + svn_pool_destroy(iterpool); + return svn_error_trace(err); /* No buffering was necessary */ + } + else + { + /* SERF_ERROR_WAIT_CONN should be impossible? */ + return svn_error_trace(err); + } + } + + /* Let's start using the spill infrastructure */ + udb->spillbuf = svn_spillbuf__create(SPILLBUF_BLOCKSIZE, + SPILLBUF_MAXBUFFSIZE, + udb->report->pool); + } + + /* Read everything we can to a spillbuffer */ + do { - apr_pool_t *iterpool_inner; - svn_ra_serf__list_t *done_list; - int i; - apr_status_t status; + const char *data; + apr_size_t len; - /* Note: this throws out the old ITERPOOL_INNER. */ - svn_pool_clear(iterpool); + /* ### What blocksize should we pass? */ + status = serf_bucket_read(response, 8*PARSE_CHUNK_SIZE, &data, &len); + + if (!SERF_BUCKET_READ_ERROR(status)) + SVN_ERR(svn_spillbuf__write(udb->spillbuf, data, len, scratch_pool)); + } + while (status == APR_SUCCESS); + + if (APR_STATUS_IS_EOF(status)) + udb->report->report_received = TRUE; - if (sess->cancel_func) - SVN_ERR(sess->cancel_func(sess->cancel_baton)); + /* We handle feeding the data from the main context loop, which will be right + after processing the pending data */ - /* We need to be careful between the outer and inner ITERPOOLs, - and what items are allocated within. */ - iterpool_inner = svn_pool_create(iterpool); + if (status) + return svn_ra_serf__wrap_err(status, NULL); + else + return SVN_NO_ERROR; +} - status = serf_context_run(sess->context, - SVN_RA_SERF__CONTEXT_RUN_DURATION, - iterpool_inner); +/* Process pending data from the update report, if any */ +static svn_error_t * +process_pending(update_delay_baton_t *udb, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = NULL; + serf_bucket_alloc_t *alloc = NULL; - err = sess->pending_error; - sess->pending_error = SVN_NO_ERROR; + while ((udb->report->num_active_fetches + udb->report->num_active_propfinds) + < REQUEST_COUNT_TO_RESUME) + { + const char *data; + apr_size_t len; + svn_boolean_t at_eof; + svn_error_t *err; - if (!err && handler->done && handler->server_error) + if (!iterpool) { - err = handler->server_error->error; + iterpool = svn_pool_create(scratch_pool); + alloc = serf_bucket_allocator_create(scratch_pool, NULL, NULL); } + else + svn_pool_clear(iterpool); - /* If the context duration timeout is up, we'll subtract that - duration from the total time alloted for such things. If - there's no time left, we fail with a message indicating that - the connection timed out. */ - if (APR_STATUS_IS_TIMEUP(status)) - { - svn_error_clear(err); - err = SVN_NO_ERROR; - status = 0; + SVN_ERR(svn_spillbuf__read(&data, &len, udb->spillbuf, iterpool)); - if (sess->timeout) - { - if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) - { - waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; - } - else - { - return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, - _("Connection timed out")); - } - } - } + if (data == NULL && !udb->report->report_received) + break; + else if (data == NULL) + at_eof = TRUE; else - { - waittime_left = sess->timeout; - } + at_eof = FALSE; - if (status && handler->sline.code != 200) + err = process_buffer(udb, NULL /* allowed? */, data, len, + at_eof, alloc, iterpool); + + if (err && APR_STATUS_IS_EAGAIN(err->apr_err)) { - return svn_error_trace( - svn_error_compose_create( - svn_ra_serf__error_on_status(handler->sline, - handler->path, - handler->location), - err)); + svn_error_clear(err); /* Throttling is working */ } - SVN_ERR(err); - if (status) + else if (err && APR_STATUS_IS_EOF(err->apr_err)) { - return svn_ra_serf__wrap_err(status, _("Error retrieving REPORT")); + svn_error_clear(err); + + svn_pool_destroy(iterpool); + udb->spillbuf = NULL; + return SVN_NO_ERROR; } + else if (err) + return svn_error_trace(err); + } - /* Open extra connections if we have enough requests to send. */ - if (sess->num_conns < sess->max_connections) - SVN_ERR(open_connection_if_needed(sess, report->num_active_fetches + - report->num_active_propfinds)); + if (iterpool) + svn_pool_destroy(iterpool); - /* Prune completed file PROPFINDs. */ - done_list = report->done_propfinds; - while (done_list) - { - svn_ra_serf__list_t *next_done = done_list->next; + return SVN_NO_ERROR; +} - svn_pool_clear(iterpool_inner); +/* Process the 'update' editor report */ +static svn_error_t * +process_editor_report(report_context_t *ctx, + svn_ra_serf__handler_t *handler, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *sess = ctx->sess; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_interval_time_t waittime_left = sess->timeout; + update_delay_baton_t *ud; - report->num_active_propfinds--; + /* Now wrap the response handler with delay support to avoid sending + out too many requests at once */ + ud = apr_pcalloc(scratch_pool, sizeof(*ud)); + ud->report = ctx; - /* If we have some files that we won't be fetching the content - * for, ensure that we update the file with any altered props. - */ - if (report->file_propchanges_only) - { - svn_ra_serf__list_t *cur, *prev; + ud->inner_handler = handler->response_handler; + ud->inner_handler_baton = handler->response_baton; - prev = NULL; - cur = report->file_propchanges_only; + handler->response_handler = update_delay_handler; + handler->response_baton = ud; - while (cur) - { - report_info_t *item = cur->data; + /* Open the first extra connection. */ + SVN_ERR(open_connection_if_needed(sess, 0)); - if (item->propfind_handler == done_list->data) - { - break; - } + sess->cur_conn = 1; - prev = cur; - cur = cur->next; - } + /* Note that we may have no active GET or PROPFIND requests, yet the + processing has not been completed. This could be from a delay on the + network or because we've spooled the entire response into our "pending" + content of the XML parser. The DONE flag will get set when all the + XML content has been received *and* parsed. */ + while (!handler->done + || ctx->num_active_fetches + || ctx->num_active_propfinds + || !ctx->done) + { + svn_error_t *err; + int i; - /* If we found a match, set the new props and remove this - * propchange from our list. - */ - if (cur) - { - report_info_t *info = cur->data; - - if (!prev) - { - report->file_propchanges_only = cur->next; - } - else - { - prev->next = cur->next; - } - - /* If we've got cached file content for this file, - take care of the locally collected properties and - file content at once. Otherwise, just deal with - the collected properties. - - NOTE: These functions below could delete - info->dir->pool (via maybe_close_dir_chain()), - from which is allocated the list item in - report->file_propchanges_only. - */ - if (info->cached_contents) - { - SVN_ERR(handle_local_content(info, iterpool_inner)); - } - else - { - SVN_ERR(handle_propchange_only(info, iterpool_inner)); - } - } - } + svn_pool_clear(iterpool); - done_list = next_done; - } - report->done_propfinds = NULL; + err = svn_ra_serf__context_run(sess, &waittime_left, iterpool); - /* Prune completed fetches from our list. */ - done_list = report->done_fetches; - while (done_list) + if (handler->done && handler->server_error) { - report_fetch_t *done_fetch = done_list->data; - svn_ra_serf__list_t *next_done = done_list->next; - report_dir_t *cur_dir; - - /* Decrease the refcount in the parent directory of the file - whose fetch has completed. */ - cur_dir = done_fetch->info->dir; - cur_dir->ref_count--; + svn_error_clear(err); + err = svn_ra_serf__server_error_create(handler, iterpool); - /* Decrement our active fetch count. */ - report->num_active_fetches--; + SVN_ERR_ASSERT(err != NULL); + } - /* See if the parent directory of this fetched item (and - perhaps even parents of that) can be closed now. + SVN_ERR(err); - NOTE: This could delete cur_dir->pool, from which is - allocated the list item in report->done_fetches. - */ - SVN_ERR(maybe_close_dir_chain(cur_dir)); + /* If there is pending REPORT data, process it now. */ + if (ud->spillbuf) + SVN_ERR(process_pending(ud, iterpool)); - done_list = next_done; + /* Debugging purposes only! */ + for (i = 0; i < sess->num_conns; i++) + { + serf_debug__closed_conn(sess->conns[i]->bkt_alloc); } - report->done_fetches = NULL; + } - /* Prune completed directory PROPFINDs. */ - done_list = report->done_dir_propfinds; - while (done_list) - { - svn_ra_serf__list_t *next_done = done_list->next; + svn_pool_clear(iterpool); - report->num_active_propfinds--; + /* If we got a complete report, close the edit. Otherwise, abort it. */ + if (ctx->done) + SVN_ERR(ctx->editor->close_edit(ctx->editor_baton, iterpool)); + else + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("Missing update-report close tag")); - if (report->active_dir_propfinds) - { - svn_ra_serf__list_t *cur, *prev; + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} - prev = NULL; - cur = report->active_dir_propfinds; +static svn_error_t * +finish_report(void *report_baton, + apr_pool_t *pool) +{ + report_context_t *report = report_baton; + svn_ra_serf__session_t *sess = report->sess; + svn_ra_serf__handler_t *handler; + svn_ra_serf__xml_context_t *xmlctx; + const char *report_target; + svn_stringbuf_t *buf = NULL; + apr_pool_t *scratch_pool = svn_pool_create(pool); + svn_error_t *err; - while (cur) - { - report_dir_t *item = cur->data; + svn_xml_make_close_tag(&buf, scratch_pool, "S:update-report"); + SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len)); + SVN_ERR(svn_stream_close(report->body_template)); - if (item->propfind_handler == done_list->data) - { - break; - } + SVN_ERR(svn_ra_serf__report_resource(&report_target, sess, scratch_pool)); - prev = cur; - cur = cur->next; - } - SVN_ERR_ASSERT(cur); /* we expect to find a matching propfind! */ + xmlctx = svn_ra_serf__xml_context_create(update_ttable, + update_opened, update_closed, + update_cdata, + report, + scratch_pool); + handler = svn_ra_serf__create_expat_handler(sess, xmlctx, NULL, + scratch_pool); - /* If we found a match, set the new props and remove this - * propchange from our list. - */ - if (cur) - { - report_dir_t *cur_dir = cur->data; - - if (!prev) - { - report->active_dir_propfinds = cur->next; - } - else - { - prev->next = cur->next; - } - - /* See if this directory (and perhaps even parents of that) - can be closed now. - - NOTE: This could delete cur_dir->pool, from which is - allocated the list item in report->active_dir_propfinds. - */ - SVN_ERR(maybe_close_dir_chain(cur_dir)); - } - } + handler->method = "REPORT"; + handler->path = report_target; + handler->body_delegate = create_update_report_body; + handler->body_delegate_baton = report; + handler->body_type = "text/xml"; + handler->custom_accept_encoding = TRUE; + handler->header_delegate = setup_update_report_headers; + handler->header_delegate_baton = report; - done_list = next_done; - } - report->done_dir_propfinds = NULL; - - /* If the parser is paused, and the number of active requests has - dropped far enough, then resume parsing. */ - if (parser_ctx->paused - && (report->num_active_fetches + report->num_active_propfinds - < REQUEST_COUNT_TO_RESUME)) - parser_ctx->paused = FALSE; - - /* If we have not paused the parser and it looks like data MAY be - present (we can't know for sure because of the private structure), - then go process the pending content. */ - if (!parser_ctx->paused && parser_ctx->pending != NULL) - SVN_ERR(svn_ra_serf__process_pending(parser_ctx, - &report->report_received, - iterpool_inner)); + svn_ra_serf__request_create(handler); - /* Debugging purposes only! */ - for (i = 0; i < sess->num_conns; i++) - { - serf_debug__closed_conn(sess->conns[i]->bkt_alloc); - } - } + err = process_editor_report(report, handler, scratch_pool); - /* If we got a complete report, close the edit. Otherwise, abort it. */ - if (report->report_completed) + if (err) { - /* Ensure that we opened and closed our root dir and that we closed - * all of our children. */ - if (!report->closed_root && report->root_dir != NULL) - { - SVN_ERR(close_all_dirs(report->root_dir)); - } - - err = report->update_editor->close_edit(report->update_baton, iterpool); + err = svn_error_trace(err); + err = svn_error_compose_create( + err, + svn_error_trace( + report->editor->abort_edit(report->editor_baton, + scratch_pool))); } - else - { - /* Tell the editor that something failed */ - err = report->update_editor->abort_edit(report->update_baton, iterpool); - err = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, err, - _("Missing update-report close tag")); - } + svn_pool_destroy(scratch_pool); - svn_pool_destroy(iterpool); return svn_error_trace(err); } @@ -3162,7 +2767,7 @@ make_update_reporter(svn_ra_session_t *ra_session, update_editor, update_baton, depth, has_target, - sess->pool)); + result_pool)); update_editor = filter_editor; update_baton = filter_baton; } @@ -3170,7 +2775,6 @@ make_update_reporter(svn_ra_session_t *ra_session, report = apr_pcalloc(result_pool, sizeof(*report)); report->pool = result_pool; report->sess = sess; - report->conn = report->sess->conns[0]; report->target_rev = revision; report->ignore_ancestry = ignore_ancestry; report->send_copyfrom_args = send_copyfrom_args; @@ -3181,16 +2785,18 @@ make_update_reporter(svn_ra_session_t *ra_session, report->destination = dest_path; report->update_target = update_target; - report->update_editor = update_editor; - report->update_baton = update_baton; + report->editor = update_editor; + report->editor_baton = update_baton; report->done = FALSE; *reporter = &ra_serf_reporter; *report_baton = report; - SVN_ERR(svn_io_open_unique_file3(&report->body_file, NULL, NULL, - svn_io_file_del_on_pool_cleanup, - report->pool, scratch_pool)); + report->body = apr_pcalloc(report->pool, sizeof(*report->body)); + report->body->result_pool = report->pool; + report->body_template = svn_stream_create(report->body, report->pool); + svn_stream_set_write(report->body_template, body_write_fn); + svn_stream_set_close(report->body_template, body_done_fn); if (sess->bulk_updates == svn_tristate_true) { @@ -3231,6 +2837,14 @@ make_update_reporter(svn_ra_session_t *ra_session, supports inlining properties in update editor report. */ if (sess->supports_inline_props) { + /* NOTE: both inlined properties and server->allows_bulk_update + (flag SVN_DAV_ALLOW_BULK_UPDATES) were added in 1.8.0, so + this code is never reached with a released version of + mod_dav_svn. + + Basically by default a 1.8.0 client connecting to a 1.7.x or + older server will always use bulk updates. */ + /* Inline props supported: do not use bulk updates. */ use_bulk_updates = FALSE; } @@ -3248,14 +2862,14 @@ make_update_reporter(svn_ra_session_t *ra_session, svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal, "S:update-report", "xmlns:S", SVN_XML_NAMESPACE, "send-all", "true", - NULL); + SVN_VA_NULL); } else { svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal, "S:update-report", "xmlns:S", SVN_XML_NAMESPACE, - NULL); + SVN_VA_NULL); /* Subversion 1.8+ servers can be told to send properties for newly added items inline even when doing a skelta response. */ make_simple_xml_tag(&buf, "S:include-props", "yes", scratch_pool); @@ -3315,8 +2929,7 @@ make_update_reporter(svn_ra_session_t *ra_session, make_simple_xml_tag(&buf, "S:depth", svn_depth_to_word(depth), scratch_pool); - SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len, - NULL, scratch_pool)); + SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len)); return SVN_NO_ERROR; } @@ -3367,7 +2980,8 @@ svn_ra_serf__do_diff(svn_ra_session_t *ra_session, SVN_ERR(make_update_reporter(ra_session, reporter, report_baton, revision, session->session_url.path, versus_url, diff_target, - depth, ignore_ancestry, text_deltas, FALSE, + depth, ignore_ancestry, text_deltas, + FALSE /* send_copyfrom */, diff_editor, diff_baton, pool, scratch_pool)); svn_pool_destroy(scratch_pool); @@ -3426,195 +3040,3 @@ svn_ra_serf__do_switch(svn_ra_session_t *ra_session, switch_editor, switch_baton, result_pool, scratch_pool); } - -/* Helper svn_ra_serf__get_file(). Attempts to fetch file contents - * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is - * present in PROPS. - * - * Sets *FOUND_P to TRUE if file contents was successfuly fetched. - * - * Performs all temporary allocations in POOL. - */ -static svn_error_t * -try_get_wc_contents(svn_boolean_t *found_p, - svn_ra_serf__session_t *session, - apr_hash_t *props, - svn_stream_t *dst_stream, - apr_pool_t *pool) -{ - apr_hash_t *svn_props; - const char *sha1_checksum_prop; - svn_checksum_t *checksum; - svn_stream_t *wc_stream; - svn_error_t *err; - - /* No contents found by default. */ - *found_p = FALSE; - - if (!session->wc_callbacks->get_wc_contents) - { - /* No callback, nothing to do. */ - return SVN_NO_ERROR; - } - - - svn_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV); - if (!svn_props) - { - /* No properties -- therefore no checksum property -- in response. */ - return SVN_NO_ERROR; - } - - sha1_checksum_prop = svn_prop_get_value(svn_props, "sha1-checksum"); - if (sha1_checksum_prop == NULL) - { - /* No checksum property in response. */ - return SVN_NO_ERROR; - } - - SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, - sha1_checksum_prop, pool)); - - err = session->wc_callbacks->get_wc_contents( - session->wc_callback_baton, &wc_stream, checksum, pool); - - if (err) - { - svn_error_clear(err); - - /* Ignore errors for now. */ - return SVN_NO_ERROR; - } - - if (wc_stream) - { - SVN_ERR(svn_stream_copy3(wc_stream, - svn_stream_disown(dst_stream, pool), - NULL, NULL, pool)); - *found_p = TRUE; - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_ra_serf__get_file(svn_ra_session_t *ra_session, - const char *path, - svn_revnum_t revision, - svn_stream_t *stream, - svn_revnum_t *fetched_rev, - apr_hash_t **props, - apr_pool_t *pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - svn_ra_serf__connection_t *conn; - const char *fetch_url; - apr_hash_t *fetch_props; - svn_node_kind_t res_kind; - const svn_ra_serf__dav_props_t *which_props; - - /* What connection should we go on? */ - conn = session->conns[session->cur_conn]; - - /* Fetch properties. */ - - fetch_url = svn_path_url_add_component2(session->session_url.path, path, pool); - - /* The simple case is if we want HEAD - then a GET on the fetch_url is fine. - * - * Otherwise, we need to get the baseline version for this particular - * revision and then fetch that file. - */ - if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) - { - SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev, - session, conn, - fetch_url, revision, - pool, pool)); - revision = SVN_INVALID_REVNUM; - } - /* REVISION is always SVN_INVALID_REVNUM */ - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); - - if (props) - { - which_props = all_props; - } - else if (stream && session->wc_callbacks->get_wc_contents) - { - which_props = type_and_checksum_props; - } - else - { - which_props = check_path_props; - } - - SVN_ERR(svn_ra_serf__fetch_node_props(&fetch_props, conn, fetch_url, - SVN_INVALID_REVNUM, - which_props, - pool, pool)); - - /* Verify that resource type is not collection. */ - SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props)); - if (res_kind != svn_node_file) - { - return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, - _("Can't get text contents of a directory")); - } - - /* TODO Filter out all of our props into a usable format. */ - if (props) - { - /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props() - ### put them into POOL, so we're okay. */ - SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props, - pool, pool)); - } - - if (stream) - { - svn_boolean_t found; - SVN_ERR(try_get_wc_contents(&found, session, fetch_props, stream, pool)); - - /* No contents found in the WC, let's fetch from server. */ - if (!found) - { - report_fetch_t *stream_ctx; - svn_ra_serf__handler_t *handler; - - /* Create the fetch context. */ - stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx)); - stream_ctx->target_stream = stream; - stream_ctx->sess = session; - stream_ctx->conn = conn; - stream_ctx->info = apr_pcalloc(pool, sizeof(*stream_ctx->info)); - stream_ctx->info->name = fetch_url; - - handler = apr_pcalloc(pool, sizeof(*handler)); - - handler->handler_pool = pool; - handler->method = "GET"; - handler->path = fetch_url; - handler->conn = conn; - handler->session = session; - - handler->custom_accept_encoding = TRUE; - handler->header_delegate = headers_fetch; - handler->header_delegate_baton = stream_ctx; - - handler->response_handler = handle_stream; - handler->response_baton = stream_ctx; - - handler->response_error = cancel_fetch; - handler->response_error_baton = stream_ctx; - - stream_ctx->handler = handler; - - svn_ra_serf__request_create(handler); - - SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool)); - } - } - - return SVN_NO_ERROR; -} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/util.c b/contrib/subversion/subversion/libsvn_ra_serf/util.c index 8f6c1bb5d..5490ddea8 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/util.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/util.c @@ -32,83 +32,22 @@ #include #include -#include - #include "svn_hash.h" #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_private_config.h" #include "svn_string.h" -#include "svn_xml.h" #include "svn_props.h" #include "svn_dirent_uri.h" #include "../libsvn_ra/ra_loader.h" #include "private/svn_dep_compat.h" #include "private/svn_fspath.h" -#include "private/svn_subr_private.h" #include "private/svn_auth_private.h" #include "private/svn_cert.h" #include "ra_serf.h" - -/* Fix for older expat 1.95.x's that do not define - * XML_STATUS_OK/XML_STATUS_ERROR - */ -#ifndef XML_STATUS_OK -#define XML_STATUS_OK 1 -#define XML_STATUS_ERROR 0 -#endif - -#ifndef XML_VERSION_AT_LEAST -#define XML_VERSION_AT_LEAST(major,minor,patch) \ -(((major) < XML_MAJOR_VERSION) \ - || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ - || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ - (patch) <= XML_MICRO_VERSION)) -#endif /* APR_VERSION_AT_LEAST */ - -#if XML_VERSION_AT_LEAST(1, 95, 8) -#define EXPAT_HAS_STOPPARSER -#endif - -/* Read/write chunks of this size into the spillbuf. */ -#define PARSE_CHUNK_SIZE 8000 - -/* We will store one megabyte in memory, before switching to store content - into a temporary file. */ -#define SPILL_SIZE 1000000 - - -/* This structure records pending data for the parser in memory blocks, - and possibly into a temporary file if "too much" content arrives. */ -struct svn_ra_serf__pending_t { - /* The spillbuf where we record the pending data. */ - svn_spillbuf_t *buf; - - /* This flag is set when the network has reached EOF. The PENDING - processing can then properly detect when parsing has completed. */ - svn_boolean_t network_eof; -}; - -#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \ - && svn_spillbuf__get_size((p)->buf) != 0) - - -struct expat_ctx_t { - svn_ra_serf__xml_context_t *xmlctx; - XML_Parser parser; - svn_ra_serf__handler_t *handler; - - svn_error_t *inner_error; - - /* Do not use this pool for allocation. It is merely recorded for running - the cleanup handler. */ - apr_pool_t *cleanup_pool; -}; - - static const apr_uint32_t serf_failure_map[][2] = { { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, @@ -192,6 +131,7 @@ construct_realm(svn_ra_serf__session_t *session, static char * convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) { + const char *cn = svn_hash_gets(org, "CN"); const char *org_unit = svn_hash_gets(org, "OU"); const char *org_name = svn_hash_gets(org, "O"); const char *locality = svn_hash_gets(org, "L"); @@ -200,6 +140,12 @@ convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) const char *email = svn_hash_gets(org, "E"); svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); + if (cn) + { + svn_stringbuf_appendcstr(buf, cn); + svn_stringbuf_appendcstr(buf, ", "); + } + if (org_unit) { svn_stringbuf_appendcstr(buf, org_unit); @@ -285,7 +231,6 @@ ssl_server_cert(void *baton, int failures, ### This should really be handled by serf, which should pass an error for this case, but that has backwards compatibility issues. */ apr_array_header_t *san; - svn_boolean_t found_san_entry = FALSE; svn_boolean_t found_matching_hostname = FALSE; svn_string_t *actual_hostname = svn_string_create(conn->session->session_url.hostname, scratch_pool); @@ -293,11 +238,16 @@ ssl_server_cert(void *baton, int failures, serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); san = svn_hash_gets(serf_cert, "subjectAltName"); - /* Try to find matching server name via subjectAltName first... */ - if (san) + /* Match server certificate CN with the hostname of the server iff + * we didn't find any subjectAltName fields and try to match them. + * Per RFC 2818 they are authoritative if present and CommonName + * should be ignored. NOTE: This isn't 100% correct since serf + * only loads the subjectAltName hash with dNSNames, technically + * we should ignore the CommonName if any subjectAltName entry + * exists even if it is one we don't support. */ + if (san && san->nelts > 0) { int i; - found_san_entry = san->nelts > 0; for (i = 0; i < san->nelts; i++) { const char *s = APR_ARRAY_IDX(san, i, const char*); @@ -310,12 +260,7 @@ ssl_server_cert(void *baton, int failures, } } } - - /* Match server certificate CN with the hostname of the server iff - * we didn't find any subjectAltName fields and try to match them. - * Per RFC 2818 they are authoritative if present and CommonName - * should be ignored. */ - if (!found_matching_hostname && !found_san_entry) + else { const char *hostname = NULL; @@ -368,11 +313,11 @@ ssl_server_cert(void *baton, int failures, { svn_error_t *err; - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, &cert_info); - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_FAILURES, &svn_failures); @@ -382,13 +327,13 @@ ssl_server_cert(void *baton, int failures, err = svn_auth_first_credentials(&creds, &state, SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, realmstring, - conn->session->wc_callbacks->auth_baton, + conn->session->auth_baton, scratch_pool); - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); if (err) @@ -415,11 +360,11 @@ ssl_server_cert(void *baton, int failures, return APR_SUCCESS; } - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_FAILURES, &svn_failures); - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, &cert_info); @@ -428,7 +373,7 @@ ssl_server_cert(void *baton, int failures, SVN_ERR(svn_auth_first_credentials(&creds, &state, SVN_AUTH_CRED_SSL_SERVER_TRUST, realmstring, - conn->session->wc_callbacks->auth_baton, + conn->session->auth_baton, scratch_pool)); if (creds) { @@ -449,7 +394,7 @@ ssl_server_cert(void *baton, int failures, } } - svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, + svn_auth_set_parameter(conn->session->auth_baton, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); /* Are there non accepted failures left? */ @@ -622,6 +567,7 @@ accept_response(serf_request_t *request, void *acceptor_baton, apr_pool_t *pool) { + /* svn_ra_serf__handler_t *handler = acceptor_baton; */ serf_bucket_t *c; serf_bucket_alloc_t *bkt_alloc; @@ -639,6 +585,7 @@ accept_head(serf_request_t *request, void *acceptor_baton, apr_pool_t *pool) { + /* svn_ra_serf__handler_t *handler = acceptor_baton; */ serf_bucket_t *response; response = accept_response(request, stream, acceptor_baton, pool); @@ -656,7 +603,7 @@ connection_closed(svn_ra_serf__connection_t *conn, { if (why) { - return svn_error_wrap_apr(why, NULL); + return svn_ra_serf__wrap_err(why, NULL); } if (conn->session->using_ssl) @@ -701,7 +648,7 @@ handle_client_cert(void *data, &conn->ssl_client_auth_state, SVN_AUTH_CRED_SSL_CLIENT_CERT, realm, - session->wc_callbacks->auth_baton, + session->auth_baton, pool)); } else @@ -753,7 +700,7 @@ handle_client_cert_pw(void *data, &conn->ssl_client_pw_auth_state, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, cert_path, - session->wc_callbacks->auth_baton, + session->auth_baton, pool)); } else @@ -804,6 +751,9 @@ apr_status_t svn_ra_serf__handle_client_cert_pw(void *data, * * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. * + * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers + * to request. + * * REQUEST_POOL should live for the duration of the request. Serf will * construct this and provide it to the request_setup callback, so we * should just use that one. @@ -816,6 +766,7 @@ setup_serf_req(serf_request_t *request, const char *method, const char *url, serf_bucket_t *body_bkt, const char *content_type, const char *accept_encoding, + svn_boolean_t dav_headers, apr_pool_t *request_pool, apr_pool_t *scratch_pool) { @@ -882,12 +833,86 @@ setup_serf_req(serf_request_t *request, serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); } - /* These headers need to be sent with every request; see issue #3255 - ("mod_dav_svn does not pass client capabilities to start-commit - hooks") for why. */ - serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); - serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); - serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); + /* These headers need to be sent with every request that might need + capability processing (e.g. during commit, reports, etc.), see + issue #3255 ("mod_dav_svn does not pass client capabilities to + start-commit hooks") for why. + + Some request types like GET/HEAD/PROPFIND are unaware of capability + handling; and in some cases the responses can even be cached by + proxies, so we don't have to send these hearders there. */ + if (dav_headers) + { + serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); + serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); + serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__context_run(svn_ra_serf__session_t *sess, + apr_interval_time_t *waittime_left, + apr_pool_t *scratch_pool) +{ + apr_status_t status; + svn_error_t *err; + assert(sess->pending_error == SVN_NO_ERROR); + + if (sess->cancel_func) + SVN_ERR(sess->cancel_func(sess->cancel_baton)); + + status = serf_context_run(sess->context, + SVN_RA_SERF__CONTEXT_RUN_DURATION, + scratch_pool); + + err = sess->pending_error; + sess->pending_error = SVN_NO_ERROR; + + /* If the context duration timeout is up, we'll subtract that + duration from the total time alloted for such things. If + there's no time left, we fail with a message indicating that + the connection timed out. */ + if (APR_STATUS_IS_TIMEUP(status)) + { + status = 0; + + if (sess->timeout) + { + if (*waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) + { + *waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; + } + else + { + return + svn_error_compose_create( + err, + svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, + _("Connection timed out"))); + } + } + } + else + { + *waittime_left = sess->timeout; + } + + SVN_ERR(err); + if (status) + { + /* ### This omits SVN_WARNING, and possibly relies on the fact that + ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */ + if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) + { + /* apr can't translate subversion errors to text */ + SVN_ERR_W(svn_error_create(status, NULL, NULL), + _("Error running context")); + } + + return svn_ra_serf__wrap_err(status, _("Error running context")); + } return SVN_NO_ERROR; } @@ -905,63 +930,11 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done, iterpool = svn_pool_create(scratch_pool); while (!*done) { - apr_status_t status; - svn_error_t *err; int i; svn_pool_clear(iterpool); - if (sess->cancel_func) - SVN_ERR((*sess->cancel_func)(sess->cancel_baton)); - - status = serf_context_run(sess->context, - SVN_RA_SERF__CONTEXT_RUN_DURATION, - iterpool); - - err = sess->pending_error; - sess->pending_error = SVN_NO_ERROR; - - /* If the context duration timeout is up, we'll subtract that - duration from the total time alloted for such things. If - there's no time left, we fail with a message indicating that - the connection timed out. */ - if (APR_STATUS_IS_TIMEUP(status)) - { - status = 0; - - if (sess->timeout) - { - if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) - { - waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; - } - else - { - return - svn_error_compose_create( - err, - svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, - _("Connection timed out"))); - } - } - } - else - { - waittime_left = sess->timeout; - } - - SVN_ERR(err); - if (status) - { - if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) - { - /* apr can't translate subversion errors to text */ - SVN_ERR_W(svn_error_create(status, NULL, NULL), - _("Error running context")); - } - - return svn_ra_serf__wrap_err(status, _("Error running context")); - } + SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool)); /* Debugging purposes only! */ for (i = 0; i < sess->num_conns; i++) @@ -974,6 +947,22 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done, return SVN_NO_ERROR; } +/* Ensure that a handler is no longer scheduled on the connection. + + Eventually serf will have a reliable way to cancel existing requests, + but currently it doesn't even have a way to relyable identify a request + after rescheduling, for auth reasons. + + So the only thing we can do today is reset the connection, which + will cancel all outstanding requests and prepare the connection + for re-use. +*/ +static void +svn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler) +{ + serf_connection_reset(handler->conn->conn); + handler->scheduled = FALSE; +} svn_error_t * svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, @@ -988,130 +977,18 @@ svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, err = svn_ra_serf__context_run_wait(&handler->done, handler->session, scratch_pool); - /* A callback invocation has been canceled. In this simple case of - context_run_one, we can keep the ra-session operational by resetting - the connection. - - If we don't do this, the next context run will notice that the connection - is still in the error state and will just return SVN_ERR_CEASE_INVOCATION - (=the last error for the connection) again */ - if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) - { - apr_status_t status = serf_connection_reset(handler->conn->conn); - - if (status) - err = svn_error_compose_create(err, - svn_ra_serf__wrap_err(status, NULL)); - } - - if (handler->server_error) + if (handler->scheduled) { - err = svn_error_compose_create(err, handler->server_error->error); - handler->server_error = NULL; + /* We reset the connection (breaking pipelining, etc.), as + if we didn't the next data would still be handled by this handler, + which is done as far as our caller is concerned. */ + svn_ra_serf__unschedule_handler(handler); } return svn_error_trace(err); } -/* - * Expat callback invoked on a start element tag for an error response. - */ -static svn_error_t * -start_error(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - const char **attrs, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t *ctx = parser->user_data; - - if (!ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "error") == 0) - { - ctx->in_error = TRUE; - } - else if (ctx->in_error && strcmp(name.name, "human-readable") == 0) - { - const char *err_code; - - err_code = svn_xml_get_attr_value("errcode", attrs); - if (err_code) - { - apr_int64_t val; - - SVN_ERR(svn_cstring_atoi64(&val, err_code)); - ctx->error->apr_err = (apr_status_t)val; - } - - /* If there's no error code provided, or if the provided code is - 0 (which can happen sometimes depending on how the error is - constructed on the server-side), just pick a generic error - code to run with. */ - if (! ctx->error->apr_err) - { - ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; - } - - /* Start collecting cdata. */ - svn_stringbuf_setempty(ctx->cdata); - ctx->collect_cdata = TRUE; - } - - return SVN_NO_ERROR; -} - -/* - * Expat callback invoked on an end element tag for a PROPFIND response. - */ -static svn_error_t * -end_error(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t *ctx = parser->user_data; - - if (ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "error") == 0) - { - ctx->in_error = FALSE; - } - if (ctx->in_error && strcmp(name.name, "human-readable") == 0) - { - /* On the server dav_error_response_tag() will add a leading - and trailing newline if DEBUG_CR is defined in mod_dav.h, - so remove any such characters here. */ - svn_stringbuf_strip_whitespace(ctx->cdata); - - ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, - ctx->cdata->len); - ctx->collect_cdata = FALSE; - } - - return SVN_NO_ERROR; -} - -/* - * Expat callback invoked on CDATA elements in an error response. - * - * This callback can be called multiple times. - */ -static svn_error_t * -cdata_error(svn_ra_serf__xml_parser_t *parser, - const char *data, - apr_size_t len, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t *ctx = parser->user_data; - - if (ctx->collect_cdata) - { - svn_stringbuf_appendbytes(ctx->cdata, data, len); - } - - return SVN_NO_ERROR; -} static apr_status_t @@ -1131,28 +1008,7 @@ drain_bucket(serf_bucket_t *bucket) } -static svn_ra_serf__server_error_t * -begin_error_parsing(svn_ra_serf__xml_start_element_t start, - svn_ra_serf__xml_end_element_t end, - svn_ra_serf__xml_cdata_chunk_handler_t cdata, - apr_pool_t *result_pool) -{ - svn_ra_serf__server_error_t *server_err; - - server_err = apr_pcalloc(result_pool, sizeof(*server_err)); - server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL); - server_err->contains_precondition_error = FALSE; - server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool); - server_err->collect_cdata = FALSE; - server_err->parser.pool = server_err->error->pool; - server_err->parser.user_data = server_err; - server_err->parser.start = start; - server_err->parser.end = end; - server_err->parser.cdata = cdata; - server_err->parser.ignore_errors = TRUE; - - return server_err; -} + /* Implements svn_ra_serf__response_handler_t */ svn_error_t * @@ -1241,7 +1097,7 @@ svn_ra_serf__expect_empty_body(serf_request_t *request, const char *val; /* This function is just like handle_multistatus_only() except for the - XML parsing callbacks. We want to look for the human-readable element. */ + XML parsing callbacks. We want to look for the -readable element. */ /* We should see this just once, in order to initialize SERVER_ERROR. At that point, the core error processing will take over. If we choose @@ -1251,21 +1107,22 @@ svn_ra_serf__expect_empty_body(serf_request_t *request, hdrs = serf_bucket_response_get_headers(response); val = serf_bucket_headers_get(hdrs, "Content-Type"); - if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) + if (val + && (handler->sline.code < 200 || handler->sline.code >= 300) + && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) { svn_ra_serf__server_error_t *server_err; - server_err = begin_error_parsing(start_error, end_error, cdata_error, - handler->handler_pool); - - /* Get the parser to set our DONE flag. */ - server_err->parser.done = &handler->done; + SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, + FALSE, + handler->handler_pool, + handler->handler_pool)); handler->server_error = server_err; } else { - /* The body was not text/xml, so we don't know what to do with it. + /* The body was not text/xml, or we got a success code. Toss anything that arrives. */ handler->discard_body = TRUE; } @@ -1277,728 +1134,103 @@ svn_ra_serf__expect_empty_body(serf_request_t *request, } -/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric - status code into *STATUS_CODE_OUT. Ignores leading whitespace. */ -static svn_error_t * -parse_dav_status(int *status_code_out, svn_stringbuf_t *buf, - apr_pool_t *scratch_pool) +apr_status_t +svn_ra_serf__credentials_callback(char **username, char **password, + serf_request_t *request, void *baton, + int code, const char *authn_type, + const char *realm, + apr_pool_t *pool) { + svn_ra_serf__handler_t *handler = baton; + svn_ra_serf__session_t *session = handler->session; + void *creds; + svn_auth_cred_simple_t *simple_creds; svn_error_t *err; - const char *token; - char *tok_status; - svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool); - - svn_stringbuf_strip_whitespace(temp_buf); - token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status); - if (token) - token = apr_strtok(NULL, " \t\r\n", &tok_status); - if (!token) - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Malformed DAV:status CDATA '%s'"), - buf->data); - err = svn_cstring_atoi(status_code_out, token); - if (err) - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err, - _("Malformed DAV:status CDATA '%s'"), - buf->data); - - return SVN_NO_ERROR; -} - -/* - * Expat callback invoked on a start element tag for a 207 response. - */ -static svn_error_t * -start_207(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - const char **attrs, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t *ctx = parser->user_data; - if (!ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "multistatus") == 0) - { - ctx->in_error = TRUE; - } - else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) - { - /* Start collecting cdata. */ - svn_stringbuf_setempty(ctx->cdata); - ctx->collect_cdata = TRUE; - } - else if (ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "status") == 0) + if (code == 401) { - /* Start collecting cdata. */ - svn_stringbuf_setempty(ctx->cdata); - ctx->collect_cdata = TRUE; - } + /* Use svn_auth_first_credentials if this is the first time we ask for + credentials during this session OR if the last time we asked + session->auth_state wasn't set (eg. if the credentials provider was + cancelled by the user). */ + if (!session->auth_state) + { + err = svn_auth_first_credentials(&creds, + &session->auth_state, + SVN_AUTH_CRED_SIMPLE, + realm, + session->auth_baton, + session->pool); + } + else + { + err = svn_auth_next_credentials(&creds, + session->auth_state, + session->pool); + } - return SVN_NO_ERROR; -} + if (err) + { + (void) save_error(session, err); + return err->apr_err; + } -/* - * Expat callback invoked on an end element tag for a 207 response. - */ -static svn_error_t * -end_207(svn_ra_serf__xml_parser_t *parser, - svn_ra_serf__dav_props_t name, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t *ctx = parser->user_data; + session->auth_attempts++; - if (ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "multistatus") == 0) - { - ctx->in_error = FALSE; - } - if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) - { - /* Remove leading newline added by DEBUG_CR on server */ - svn_stringbuf_strip_whitespace(ctx->cdata); + if (!creds || session->auth_attempts > 4) + { + /* No more credentials. */ + (void) save_error(session, + svn_error_create( + SVN_ERR_AUTHN_FAILED, NULL, + _("No more credentials or we tried too many " + "times.\nAuthentication failed"))); + return SVN_ERR_AUTHN_FAILED; + } - ctx->collect_cdata = FALSE; - ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, - ctx->cdata->len); - if (ctx->contains_precondition_error) - ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH; - else - ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; + simple_creds = creds; + *username = apr_pstrdup(pool, simple_creds->username); + *password = apr_pstrdup(pool, simple_creds->password); } - else if (ctx->in_error && - strcmp(name.namespace, "DAV:") == 0 && - strcmp(name.name, "status") == 0) + else { - int status_code; + *username = apr_pstrdup(pool, session->proxy_username); + *password = apr_pstrdup(pool, session->proxy_password); - ctx->collect_cdata = FALSE; + session->proxy_auth_attempts++; - SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool)); - if (status_code == 412) - ctx->contains_precondition_error = TRUE; + if (!session->proxy_username || session->proxy_auth_attempts > 4) + { + /* No more credentials. */ + (void) save_error(session, + svn_error_create( + SVN_ERR_AUTHN_FAILED, NULL, + _("Proxy authentication failed"))); + return SVN_ERR_AUTHN_FAILED; + } } - return SVN_NO_ERROR; + handler->conn->last_status_code = code; + + return APR_SUCCESS; } -/* - * Expat callback invoked on CDATA elements in a 207 response. - * - * This callback can be called multiple times. - */ +/* Wait for HTTP response status and headers, and invoke HANDLER-> + response_handler() to carry out operation-specific processing. + Afterwards, check for connection close. + + SERF_STATUS allows returning errors to serf without creating a + subversion error object. + */ static svn_error_t * -cdata_207(svn_ra_serf__xml_parser_t *parser, - const char *data, - apr_size_t len, - apr_pool_t *scratch_pool) +handle_response(serf_request_t *request, + serf_bucket_t *response, + svn_ra_serf__handler_t *handler, + apr_status_t *serf_status, + apr_pool_t *scratch_pool) { - svn_ra_serf__server_error_t *ctx = parser->user_data; - - if (ctx->collect_cdata) - { - svn_stringbuf_appendbytes(ctx->cdata, data, len); - } - - return SVN_NO_ERROR; -} - -/* Implements svn_ra_serf__response_handler_t */ -svn_error_t * -svn_ra_serf__handle_multistatus_only(serf_request_t *request, - serf_bucket_t *response, - void *baton, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__handler_t *handler = baton; - - /* This function is just like expect_empty_body() except for the - XML parsing callbacks. We are looking for very limited pieces of - the multistatus response. */ - - /* We should see this just once, in order to initialize SERVER_ERROR. - At that point, the core error processing will take over. If we choose - not to parse an error, then we'll never return here (because we - change the response handler). */ - SVN_ERR_ASSERT(handler->server_error == NULL); - - { - serf_bucket_t *hdrs; - const char *val; - - hdrs = serf_bucket_response_get_headers(response); - val = serf_bucket_headers_get(hdrs, "Content-Type"); - if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) - { - svn_ra_serf__server_error_t *server_err; - - server_err = begin_error_parsing(start_207, end_207, cdata_207, - handler->handler_pool); - - /* Get the parser to set our DONE flag. */ - server_err->parser.done = &handler->done; - - handler->server_error = server_err; - } - else - { - /* The body was not text/xml, so we don't know what to do with it. - Toss anything that arrives. */ - handler->discard_body = TRUE; - } - } - - /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it - to call the response handler again. That will start up the XML parsing, - or it will be dropped on the floor (per the decision above). */ - return SVN_NO_ERROR; -} - - -/* Conforms to Expat's XML_StartElementHandler */ -static void -start_xml(void *userData, const char *raw_name, const char **attrs) -{ - svn_ra_serf__xml_parser_t *parser = userData; - svn_ra_serf__dav_props_t name; - apr_pool_t *scratch_pool; - svn_error_t *err; - - if (parser->error) - return; - - if (!parser->state) - svn_ra_serf__xml_push_state(parser, 0); - - /* ### get a real scratch_pool */ - scratch_pool = parser->state->pool; - - svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool); - - svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); - - err = parser->start(parser, name, attrs, scratch_pool); - if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) - err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); - - parser->error = err; -} - - -/* Conforms to Expat's XML_EndElementHandler */ -static void -end_xml(void *userData, const char *raw_name) -{ - svn_ra_serf__xml_parser_t *parser = userData; - svn_ra_serf__dav_props_t name; - svn_error_t *err; - apr_pool_t *scratch_pool; - - if (parser->error) - return; - - /* ### get a real scratch_pool */ - scratch_pool = parser->state->pool; - - svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); - - err = parser->end(parser, name, scratch_pool); - if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) - err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); - - parser->error = err; -} - - -/* Conforms to Expat's XML_CharacterDataHandler */ -static void -cdata_xml(void *userData, const char *data, int len) -{ - svn_ra_serf__xml_parser_t *parser = userData; - svn_error_t *err; - apr_pool_t *scratch_pool; - - if (parser->error) - return; - - if (!parser->state) - svn_ra_serf__xml_push_state(parser, 0); - - /* ### get a real scratch_pool */ - scratch_pool = parser->state->pool; - - err = parser->cdata(parser, data, len, scratch_pool); - if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) - err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); - - parser->error = err; -} - -/* Flip the requisite bits in CTX to indicate that processing of the - response is complete, adding the current "done item" to the list of - completed items. */ -static void -add_done_item(svn_ra_serf__xml_parser_t *ctx) -{ - /* Make sure we don't add to DONE_LIST twice. */ - if (!*ctx->done) - { - *ctx->done = TRUE; - if (ctx->done_list) - { - ctx->done_item->data = ctx->user_data; - ctx->done_item->next = *ctx->done_list; - *ctx->done_list = ctx->done_item; - } - } -} - - -static svn_error_t * -write_to_pending(svn_ra_serf__xml_parser_t *ctx, - const char *data, - apr_size_t len, - apr_pool_t *scratch_pool) -{ - if (ctx->pending == NULL) - { - ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending)); - ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE, - SPILL_SIZE, - ctx->pool); - } - - /* Copy the data into one or more chunks in the spill buffer. */ - return svn_error_trace(svn_spillbuf__write(ctx->pending->buf, - data, len, - scratch_pool)); -} - - -static svn_error_t * -inject_to_parser(svn_ra_serf__xml_parser_t *ctx, - const char *data, - apr_size_t len, - const serf_status_line *sl) -{ - int xml_status; - - xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0); - - if (! ctx->ignore_errors) - { - SVN_ERR(ctx->error); - - if (xml_status != XML_STATUS_OK) - { - if (sl == NULL) - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("XML parsing failed")); - - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("XML parsing failed: (%d %s)"), - sl->code, sl->reason); - } - } - - return SVN_NO_ERROR; -} - -/* Apr pool cleanup handler to release an XML_Parser in success and error - conditions */ -static apr_status_t -xml_parser_cleanup(void *baton) -{ - XML_Parser *xmlp = baton; - - if (*xmlp) - { - (void) XML_ParserFree(*xmlp); - *xmlp = NULL; - } - - return APR_SUCCESS; -} - -/* Limit the amount of pending content to parse at once to < 100KB per - iteration. This number is chosen somewhat arbitrarely. Making it lower - will have a drastical negative impact on performance, whereas increasing it - increases the risk for connection timeouts. - */ -#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5 - -svn_error_t * -svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, - svn_boolean_t *network_eof, - apr_pool_t *scratch_pool) -{ - svn_boolean_t pending_empty = FALSE; - apr_size_t cur_read = 0; - - /* Fast path exit: already paused, nothing to do, or already done. */ - if (parser->paused || parser->pending == NULL || *parser->done) - { - *network_eof = parser->pending ? parser->pending->network_eof : FALSE; - return SVN_NO_ERROR; - } - - /* Parsing the pending conten in the spillbuf will result in many disc i/o - operations. This can be so slow that we don't run the network event - processing loop often enough, resulting in timed out connections. - - So we limit the amounts of bytes parsed per iteration. - */ - while (cur_read < PENDING_TO_PARSE) - { - const char *data; - apr_size_t len; - - /* Get a block of content, stopping the loop when we run out. */ - SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf, - scratch_pool)); - if (data) - { - /* Inject the content into the XML parser. */ - SVN_ERR(inject_to_parser(parser, data, len, NULL)); - - /* If the XML parsing callbacks paused us, then we're done for now. */ - if (parser->paused) - break; - - cur_read += len; - } - else - { - /* The buffer is empty. */ - pending_empty = TRUE; - break; - } - } - - /* If the PENDING structures are empty *and* we consumed all content from - the network, then we're completely done with the parsing. */ - if (pending_empty && - parser->pending->network_eof) - { - int xml_status; - SVN_ERR_ASSERT(parser->xmlp != NULL); - - /* Tell the parser that no more content will be parsed. */ - xml_status = XML_Parse(parser->xmlp, NULL, 0, 1); - - apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup); - parser->xmlp = NULL; - - if (! parser->ignore_errors) - { - SVN_ERR(parser->error); - - if (xml_status != XML_STATUS_OK) - { - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("XML parsing failed")); - } - } - - add_done_item(parser); - } - - *network_eof = parser->pending ? parser->pending->network_eof : FALSE; - - return SVN_NO_ERROR; -} -#undef PENDING_TO_PARSE - - -/* ### this is still broken conceptually. just shifting incrementally... */ -static svn_error_t * -handle_server_error(serf_request_t *request, - serf_bucket_t *response, - apr_pool_t *scratch_pool) -{ - svn_ra_serf__server_error_t server_err = { 0 }; - serf_bucket_t *hdrs; - const char *val; - apr_status_t err; - - hdrs = serf_bucket_response_get_headers(response); - val = serf_bucket_headers_get(hdrs, "Content-Type"); - if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) - { - /* ### we should figure out how to reuse begin_error_parsing */ - - server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL); - server_err.contains_precondition_error = FALSE; - server_err.cdata = svn_stringbuf_create_empty(scratch_pool); - server_err.collect_cdata = FALSE; - server_err.parser.pool = server_err.error->pool; - server_err.parser.user_data = &server_err; - server_err.parser.start = start_error; - server_err.parser.end = end_error; - server_err.parser.cdata = cdata_error; - server_err.parser.done = &server_err.done; - server_err.parser.ignore_errors = TRUE; - - /* We don't care about any errors except for SERVER_ERR.ERROR */ - svn_error_clear(svn_ra_serf__handle_xml_parser(request, - response, - &server_err.parser, - scratch_pool)); - - /* ### checking DONE is silly. the above only parses whatever has - ### been received at the network interface. totally wrong. but - ### it is what we have for now (maintaining historical code), - ### until we fully migrate. */ - if (server_err.done && server_err.error->apr_err == APR_SUCCESS) - { - svn_error_clear(server_err.error); - server_err.error = SVN_NO_ERROR; - } - - return svn_error_trace(server_err.error); - } - - /* The only error that we will return is from the XML response body. - Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to - surface. */ - err = drain_bucket(response); - if (err && !SERF_BUCKET_READ_ERROR(err)) - return svn_ra_serf__wrap_err(err, NULL); - - return SVN_NO_ERROR; -} - - -/* Implements svn_ra_serf__response_handler_t */ -svn_error_t * -svn_ra_serf__handle_xml_parser(serf_request_t *request, - serf_bucket_t *response, - void *baton, - apr_pool_t *pool) -{ - serf_status_line sl; - apr_status_t status; - svn_ra_serf__xml_parser_t *ctx = baton; - svn_error_t *err; - - /* ### get the HANDLER rather than fetching this. */ - status = serf_bucket_response_status(response, &sl); - if (SERF_BUCKET_READ_ERROR(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - - /* Woo-hoo. Nothing here to see. */ - if (sl.code == 404 && !ctx->ignore_errors) - { - err = handle_server_error(request, response, pool); - - if (err && APR_STATUS_IS_EOF(err->apr_err)) - add_done_item(ctx); - - return svn_error_trace(err); - } - - if (!ctx->xmlp) - { - ctx->xmlp = XML_ParserCreate(NULL); - apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup, - apr_pool_cleanup_null); - XML_SetUserData(ctx->xmlp, ctx); - XML_SetElementHandler(ctx->xmlp, start_xml, end_xml); - if (ctx->cdata) - { - XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml); - } - } - - while (1) - { - const char *data; - apr_size_t len; - - status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); - if (SERF_BUCKET_READ_ERROR(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - - /* Note: once the callbacks invoked by inject_to_parser() sets the - PAUSED flag, then it will not be cleared. write_to_pending() will - only save the content. Logic outside of serf_context_run() will - clear that flag, as appropriate, along with processing the - content that we have placed into the PENDING buffer. - - We want to save arriving content into the PENDING structures if - the parser has been paused, or we already have data in there (so - the arriving data is appended, rather than injected out of order) */ - if (ctx->paused || HAS_PENDING_DATA(ctx->pending)) - { - err = write_to_pending(ctx, data, len, pool); - } - else - { - err = inject_to_parser(ctx, data, len, &sl); - if (err) - { - /* Should have no errors if IGNORE_ERRORS is set. */ - SVN_ERR_ASSERT(!ctx->ignore_errors); - } - } - if (err) - { - SVN_ERR_ASSERT(ctx->xmlp != NULL); - - apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); - add_done_item(ctx); - return svn_error_trace(err); - } - - if (APR_STATUS_IS_EAGAIN(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - - if (APR_STATUS_IS_EOF(status)) - { - if (ctx->pending != NULL) - ctx->pending->network_eof = TRUE; - - /* We just hit the end of the network content. If we have nothing - in the PENDING structures, then we're completely done. */ - if (!HAS_PENDING_DATA(ctx->pending)) - { - int xml_status; - SVN_ERR_ASSERT(ctx->xmlp != NULL); - - xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1); - - apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); - - if (! ctx->ignore_errors) - { - SVN_ERR(ctx->error); - - if (xml_status != XML_STATUS_OK) - { - return svn_error_create( - SVN_ERR_XML_MALFORMED, NULL, - _("The XML response contains invalid XML")); - } - } - - add_done_item(ctx); - } - - return svn_ra_serf__wrap_err(status, NULL); - } - - /* feed me! */ - } - /* not reached */ -} - - -apr_status_t -svn_ra_serf__credentials_callback(char **username, char **password, - serf_request_t *request, void *baton, - int code, const char *authn_type, - const char *realm, - apr_pool_t *pool) -{ - svn_ra_serf__handler_t *handler = baton; - svn_ra_serf__session_t *session = handler->session; - void *creds; - svn_auth_cred_simple_t *simple_creds; - svn_error_t *err; - - if (code == 401) - { - /* Use svn_auth_first_credentials if this is the first time we ask for - credentials during this session OR if the last time we asked - session->auth_state wasn't set (eg. if the credentials provider was - cancelled by the user). */ - if (!session->auth_state) - { - err = svn_auth_first_credentials(&creds, - &session->auth_state, - SVN_AUTH_CRED_SIMPLE, - realm, - session->wc_callbacks->auth_baton, - session->pool); - } - else - { - err = svn_auth_next_credentials(&creds, - session->auth_state, - session->pool); - } - - if (err) - { - (void) save_error(session, err); - return err->apr_err; - } - - session->auth_attempts++; - - if (!creds || session->auth_attempts > 4) - { - /* No more credentials. */ - (void) save_error(session, - svn_error_create( - SVN_ERR_AUTHN_FAILED, NULL, - _("No more credentials or we tried too many " - "times.\nAuthentication failed"))); - return SVN_ERR_AUTHN_FAILED; - } - - simple_creds = creds; - *username = apr_pstrdup(pool, simple_creds->username); - *password = apr_pstrdup(pool, simple_creds->password); - } - else - { - *username = apr_pstrdup(pool, session->proxy_username); - *password = apr_pstrdup(pool, session->proxy_password); - - session->proxy_auth_attempts++; - - if (!session->proxy_username || session->proxy_auth_attempts > 4) - { - /* No more credentials. */ - (void) save_error(session, - svn_error_create( - SVN_ERR_AUTHN_FAILED, NULL, - _("Proxy authentication failed"))); - return SVN_ERR_AUTHN_FAILED; - } - } - - handler->conn->last_status_code = code; - - return APR_SUCCESS; -} - -/* Wait for HTTP response status and headers, and invoke HANDLER-> - response_handler() to carry out operation-specific processing. - Afterwards, check for connection close. - - SERF_STATUS allows returning errors to serf without creating a - subversion error object. - */ -static svn_error_t * -handle_response(serf_request_t *request, - serf_bucket_t *response, - svn_ra_serf__handler_t *handler, - apr_status_t *serf_status, - apr_pool_t *scratch_pool) -{ - apr_status_t status; - svn_error_t *err; + apr_status_t status; + svn_error_t *err; /* ### need to verify whether this already gets init'd on every ### successful exit. for an error-exit, it will (properly) be @@ -2008,6 +1240,8 @@ handle_response(serf_request_t *request, if (!response) { /* Uh-oh. Our connection died. */ + handler->scheduled = FALSE; + if (handler->response_error) { /* Give a handler chance to prevent request requeue. */ @@ -2126,10 +1360,7 @@ handle_response(serf_request_t *request, } handler->conn->last_status_code = handler->sline.code; - if (handler->sline.code == 405 - || handler->sline.code == 408 - || handler->sline.code == 409 - || handler->sline.code >= 500) + if (handler->sline.code >= 400) { /* 405 Method Not allowed. 408 Request Timeout @@ -2144,35 +1375,22 @@ handle_response(serf_request_t *request, { svn_ra_serf__server_error_t *server_err; - server_err = begin_error_parsing(start_error, end_error, cdata_error, - handler->handler_pool); - /* Get the parser to set our DONE flag. */ - server_err->parser.done = &handler->done; + SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, + FALSE, + handler->handler_pool, + handler->handler_pool)); handler->server_error = server_err; } else { handler->discard_body = TRUE; - - if (!handler->session->pending_error) - { - apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; - - /* 405 == Method Not Allowed (Occurs when trying to lock a working - copy path which no longer exists at HEAD in the repository. */ - if (handler->sline.code == 405 - && strcmp(handler->method, "LOCK") == 0) - apr_err = SVN_ERR_FS_OUT_OF_DATE; - - handler->session->pending_error = - svn_error_createf(apr_err, NULL, - _("%s request on '%s' failed: %d %s"), - handler->method, handler->path, - handler->sline.code, handler->sline.reason); - } } } + else if (handler->sline.code <= 199) + { + handler->discard_body = TRUE; + } /* Stop processing the above, on every packet arrival. */ handler->reading_body = TRUE; @@ -2184,13 +1402,6 @@ handle_response(serf_request_t *request, { *serf_status = drain_bucket(response); - /* If the handler hasn't set done (which it shouldn't have) and - we now have the EOF, go ahead and set it so that we can stop - our context loops. - */ - if (!handler->done && APR_STATUS_IS_EOF(*serf_status)) - handler->done = TRUE; - return SVN_NO_ERROR; } @@ -2198,50 +1409,12 @@ handle_response(serf_request_t *request, that now. */ if (handler->server_error != NULL) { - err = svn_ra_serf__handle_xml_parser(request, response, - &handler->server_error->parser, - scratch_pool); - - /* If we do not receive an error or it is a non-transient error, return - immediately. - - APR_EOF will be returned when parsing is complete. - - APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through - parsing and the network has no more data right now. If we receive that, - clear the error and return - allowing serf to wait for more data. - */ - if (!err || SERF_BUCKET_READ_ERROR(err->apr_err)) - return svn_error_trace(err); - - if (!APR_STATUS_IS_EOF(err->apr_err)) - { - *serf_status = err->apr_err; - svn_error_clear(err); - return SVN_NO_ERROR; - } - - /* Clear the EOF. We don't need it. */ - svn_error_clear(err); - - /* If the parsing is done, and we did not extract an error, then - simply toss everything, and anything else that might arrive. - The higher-level code will need to investigate HANDLER->SLINE, - as we have no further information for them. */ - if (handler->done - && handler->server_error->error->apr_err == APR_SUCCESS) - { - svn_error_clear(handler->server_error->error); - - /* Stop parsing for a server error. */ - handler->server_error = NULL; - - /* If anything arrives after this, then just discard it. */ - handler->discard_body = TRUE; - } - - *serf_status = APR_EOF; - return SVN_NO_ERROR; + return svn_error_trace( + svn_ra_serf__handle_server_error(handler->server_error, + handler, + request, response, + serf_status, + scratch_pool)); } /* Pass the body along to the registered response handler. */ @@ -2271,12 +1444,13 @@ static apr_status_t handle_response_cb(serf_request_t *request, serf_bucket_t *response, void *baton, - apr_pool_t *scratch_pool) + apr_pool_t *response_pool) { svn_ra_serf__handler_t *handler = baton; svn_error_t *err; apr_status_t inner_status; apr_status_t outer_status; + apr_pool_t *scratch_pool = response_pool; /* Scratch pool needed? */ err = svn_error_trace(handle_response(request, response, handler, &inner_status, @@ -2287,9 +1461,34 @@ handle_response_cb(serf_request_t *request, if (!outer_status) outer_status = inner_status; - /* Make sure the DONE flag is set properly. */ + /* Make sure the DONE flag is set properly and requests are cleaned up. */ if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) - handler->done = TRUE; + { + svn_ra_serf__session_t *sess = handler->session; + handler->done = TRUE; + handler->scheduled = FALSE; + outer_status = APR_EOF; + + /* We use a cached handler->session here to allow handler to free the + memory containing the handler */ + save_error(sess, + handler->done_delegate(request, handler->done_delegate_baton, + scratch_pool)); + } + else if (SERF_BUCKET_READ_ERROR(outer_status) + && handler->session->pending_error) + { + handler->discard_body = TRUE; /* Discard further data */ + handler->done = TRUE; /* Mark as done */ + /* handler->scheduled is still TRUE, as we still expect data. + If we would return an error outer-status the connection + would have to be restarted. With scheduled still TRUE + destroying the handler's pool will still reset the + connection, avoiding the posibility of returning + an error for this handler when a new request is + scheduled. */ + outer_status = APR_EAGAIN; /* Exit context loop */ + } return outer_status; } @@ -2312,9 +1511,8 @@ setup_request(serf_request_t *request, { serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); - /* ### should pass the scratch_pool */ SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, - bkt_alloc, request_pool)); + bkt_alloc, request_pool, scratch_pool)); } else { @@ -2338,17 +1536,17 @@ setup_request(serf_request_t *request, SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, handler->session, handler->method, handler->path, body_bkt, handler->body_type, accept_encoding, - request_pool, scratch_pool)); + !handler->no_dav_headers, request_pool, + scratch_pool)); if (handler->header_delegate) { - /* ### should pass the scratch_pool */ SVN_ERR(handler->header_delegate(headers_bkt, handler->header_delegate_baton, - request_pool)); + request_pool, scratch_pool)); } - return APR_SUCCESS; + return SVN_NO_ERROR; } /* Implements the serf_request_setup_t interface (which sets up both a @@ -2362,51 +1560,58 @@ setup_request_cb(serf_request_t *request, void **acceptor_baton, serf_response_handler_t *s_handler, void **s_handler_baton, - apr_pool_t *pool) + apr_pool_t *request_pool) { svn_ra_serf__handler_t *handler = setup_baton; + apr_pool_t *scratch_pool; svn_error_t *err; - /* ### construct a scratch_pool? serf gives us a pool that will live for - ### the duration of the request. */ - apr_pool_t *scratch_pool = pool; + /* Construct a scratch_pool? serf gives us a pool that will live for + the duration of the request. But requests are retried in some cases */ + scratch_pool = svn_pool_create(request_pool); if (strcmp(handler->method, "HEAD") == 0) *acceptor = accept_head; else *acceptor = accept_response; - *acceptor_baton = handler->session; + *acceptor_baton = handler; *s_handler = handle_response_cb; *s_handler_baton = handler; err = svn_error_trace(setup_request(request, handler, req_bkt, - pool /* request_pool */, scratch_pool)); + request_pool, scratch_pool)); + svn_pool_destroy(scratch_pool); return save_error(handler->session, err); } void svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) { - SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL); + SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL + && !handler->scheduled); - /* In case HANDLER is re-queued, reset the various transient fields. - - ### prior to recent changes, HANDLER was constant. maybe we should - ### break out these processing fields, apart from the request - ### definition. */ + /* In case HANDLER is re-queued, reset the various transient fields. */ handler->done = FALSE; handler->server_error = NULL; handler->sline.version = 0; handler->location = NULL; handler->reading_body = FALSE; handler->discard_body = FALSE; + handler->scheduled = TRUE; + + /* Keeping track of the returned request object would be nice, but doesn't + work the way we would expect in ra_serf.. + + Serf sometimes creates a new request for us (and destroys the old one) + without telling, like when authentication failed (401/407 response. - /* ### do we ever alter the >response_handler? */ + We 'just' trust serf to do the right thing and expect it to tell us + when the state of the request changes. - /* ### do we need to hold onto the returned request object, or just - ### not worry about it (the serf ctx will manage it). */ + ### I fixed a request leak in serf in r2258 on auth failures. + */ (void) serf_connection_request_create(handler->conn->conn, setup_request_cb, handler); } @@ -2415,8 +1620,7 @@ svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) svn_error_t * svn_ra_serf__discover_vcc(const char **vcc_url, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { const char *path; const char *relative_path; @@ -2429,12 +1633,6 @@ svn_ra_serf__discover_vcc(const char **vcc_url, return SVN_NO_ERROR; } - /* If no connection is provided, use the default one. */ - if (! conn) - { - conn = session->conns[0]; - } - path = session->session_url.path; *vcc_url = NULL; uuid = NULL; @@ -2444,9 +1642,10 @@ svn_ra_serf__discover_vcc(const char **vcc_url, apr_hash_t *props; svn_error_t *err; - err = svn_ra_serf__fetch_node_props(&props, conn, + err = svn_ra_serf__fetch_node_props(&props, session, path, SVN_INVALID_REVNUM, - base_props, pool, pool); + base_props, + scratch_pool, scratch_pool); if (! err) { apr_hash_t *ns_props; @@ -2474,12 +1673,7 @@ svn_ra_serf__discover_vcc(const char **vcc_url, svn_error_clear(err); /* Okay, strip off a component from PATH. */ - path = svn_urlpath__dirname(path, pool); - - /* An error occurred on conns. 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(conn->conn); + path = svn_urlpath__dirname(path, scratch_pool); } } } @@ -2505,7 +1699,7 @@ svn_ra_serf__discover_vcc(const char **vcc_url, { svn_stringbuf_t *url_buf; - url_buf = svn_stringbuf_create(path, pool); + url_buf = svn_stringbuf_create(path, scratch_pool); svn_path_remove_components(url_buf, svn_path_component_count(relative_path)); @@ -2533,7 +1727,6 @@ svn_error_t * svn_ra_serf__get_relative_path(const char **rel_path, const char *orig_path, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, apr_pool_t *pool) { const char *decoded_root, *decoded_orig; @@ -2550,7 +1743,6 @@ svn_ra_serf__get_relative_path(const char **rel_path, promises to populate the session's root-url cache, and that's what we really want. */ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, - conn ? conn : session->conns[0], pool)); } @@ -2564,7 +1756,6 @@ svn_ra_serf__get_relative_path(const char **rel_path, svn_error_t * svn_ra_serf__report_resource(const char **report_target, svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, apr_pool_t *pool) { /* If we have HTTP v2 support, we want to report against the 'me' @@ -2574,7 +1765,7 @@ svn_ra_serf__report_resource(const char **report_target, /* Otherwise, we'll use the default VCC. */ else - SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, pool)); return SVN_NO_ERROR; } @@ -2588,13 +1779,14 @@ svn_ra_serf__error_on_status(serf_status_line sline, { case 301: case 302: + case 303: case 307: + case 308: return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, (sline.code == 301) - ? _("Repository moved permanently to '%s';" - " please relocate") - : _("Repository moved temporarily to '%s';" - " please relocate"), location); + ? _("Repository moved permanently to '%s'") + : _("Repository moved temporarily to '%s'"), + location); case 403: return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, _("Access to '%s' forbidden"), path); @@ -2602,6 +1794,16 @@ svn_ra_serf__error_on_status(serf_status_line sline, case 404: return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("'%s' path not found"), path); + case 405: + return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, + _("HTTP method is not allowed on '%s'"), + path); + case 409: + return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, + _("'%s' conflicts"), path); + case 412: + return svn_error_createf(SVN_ERR_RA_DAV_PRECONDITION_FAILED, NULL, + _("Precondition on '%s' failed"), path); case 423: return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, _("'%s': no lock token available"), path); @@ -2612,20 +1814,58 @@ svn_ra_serf__error_on_status(serf_status_line sline, "server or an intermediate proxy does not accept " "chunked encoding. Try setting 'http-chunked-requests' " "to 'auto' or 'no' in your client configuration.")); + case 500: + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("Unexpected server error %d '%s' on '%s'"), + sline.code, sline.reason, path); case 501: return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("The requested feature is not supported by " "'%s'"), path); } - if (sline.code >= 300) + if (sline.code >= 300 || sline.code <= 199) return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("Unexpected HTTP status %d '%s' on '%s'\n"), + _("Unexpected HTTP status %d '%s' on '%s'"), sline.code, sline.reason, path); return SVN_NO_ERROR; } +svn_error_t * +svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler) +{ + /* Is it a standard error status? */ + if (handler->sline.code != 405) + SVN_ERR(svn_ra_serf__error_on_status(handler->sline, + handler->path, + handler->location)); + + switch (handler->sline.code) + { + case 201: + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("Path '%s' unexpectedly created"), + handler->path); + case 204: + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '%s' already exists"), + handler->path); + + case 405: + return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, + _("The HTTP method '%s' is not allowed" + " on '%s'"), + handler->method, handler->path); + default: + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("Unexpected HTTP status %d '%s' on '%s' " + "request to '%s'"), + handler->sline.code, handler->sline.reason, + handler->method, handler->path); + } +} + svn_error_t * svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, svn_delta_shim_callbacks_t *callbacks) @@ -2636,185 +1876,102 @@ svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, return SVN_NO_ERROR; } - -/* Conforms to Expat's XML_StartElementHandler */ -static void -expat_start(void *userData, const char *raw_name, const char **attrs) -{ - struct expat_ctx_t *ectx = userData; - - if (ectx->inner_error != NULL) - return; - - ectx->inner_error = svn_error_trace( - svn_ra_serf__xml_cb_start(ectx->xmlctx, - raw_name, attrs)); - -#ifdef EXPAT_HAS_STOPPARSER - if (ectx->inner_error) - (void) XML_StopParser(ectx->parser, 0 /* resumable */); -#endif -} - - -/* Conforms to Expat's XML_EndElementHandler */ -static void -expat_end(void *userData, const char *raw_name) -{ - struct expat_ctx_t *ectx = userData; - - if (ectx->inner_error != NULL) - return; - - ectx->inner_error = svn_error_trace( - svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name)); - -#ifdef EXPAT_HAS_STOPPARSER - if (ectx->inner_error) - (void) XML_StopParser(ectx->parser, 0 /* resumable */); -#endif -} - - -/* Conforms to Expat's XML_CharacterDataHandler */ -static void -expat_cdata(void *userData, const char *data, int len) +/* Shared/standard done_delegate handler */ +static svn_error_t * +response_done(serf_request_t *request, + void *handler_baton, + apr_pool_t *scratch_pool) { - struct expat_ctx_t *ectx = userData; + svn_ra_serf__handler_t *handler = handler_baton; - if (ectx->inner_error != NULL) - return; - - ectx->inner_error = svn_error_trace( - svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len)); - -#ifdef EXPAT_HAS_STOPPARSER - if (ectx->inner_error) - (void) XML_StopParser(ectx->parser, 0 /* resumable */); -#endif -} + assert(handler->done); + if (handler->no_fail_on_http_failure_status) + return SVN_NO_ERROR; -/* Implements svn_ra_serf__response_handler_t */ -static svn_error_t * -expat_response_handler(serf_request_t *request, - serf_bucket_t *response, - void *baton, - apr_pool_t *scratch_pool) -{ - struct expat_ctx_t *ectx = baton; + if (handler->server_error) + return svn_ra_serf__server_error_create(handler, scratch_pool); - if (!ectx->parser) + if (handler->sline.code >= 400 || handler->sline.code <= 199) { - ectx->parser = XML_ParserCreate(NULL); - apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, - xml_parser_cleanup, apr_pool_cleanup_null); - XML_SetUserData(ectx->parser, ectx); - XML_SetElementHandler(ectx->parser, expat_start, expat_end); - XML_SetCharacterDataHandler(ectx->parser, expat_cdata); + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); } - /* ### TODO: sline.code < 200 should really be handled by the core */ - if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)) + if ((handler->sline.code >= 300 && handler->sline.code < 399) + && !handler->no_fail_on_http_redirect_status) { - /* By deferring to expect_empty_body(), it will make a choice on - how to handle the body. Whatever the decision, the core handler - will take over, and we will not be called again. */ - return svn_error_trace(svn_ra_serf__expect_empty_body( - request, response, ectx->handler, - scratch_pool)); + return svn_error_trace(svn_ra_serf__unexpected_status(handler)); } - while (1) - { - apr_status_t status; - const char *data; - apr_size_t len; - int expat_status; - - status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); - if (SERF_BUCKET_READ_ERROR(status)) - return svn_ra_serf__wrap_err(status, NULL); - -#if 0 - /* ### move restart/skip into the core handler */ - ectx->handler->read_size += len; -#endif - - /* ### move PAUSED behavior to a new response handler that can feed - ### an inner handler, or can pause for a while. */ - - /* ### should we have an IGNORE_ERRORS flag like the v1 parser? */ - - expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */); - - /* We need to check INNER_ERROR first. This is an error from the - callbacks that has been "dropped off" for us to retrieve. On - current Expat parsers, we stop the parser when an error occurs, - so we want to ignore EXPAT_STATUS (which reports the stoppage). - - If an error is not present, THEN we go ahead and look for parsing - errors. */ - if (ectx->inner_error) - { - apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, - xml_parser_cleanup); - return svn_error_trace(ectx->inner_error); - } - if (expat_status == XML_STATUS_ERROR) - return svn_error_createf(SVN_ERR_XML_MALFORMED, - ectx->inner_error, - _("The %s response contains invalid XML" - " (%d %s)"), - ectx->handler->method, - ectx->handler->sline.code, - ectx->handler->sline.reason); - - /* The parsing went fine. What has the bucket told us? */ - - if (APR_STATUS_IS_EOF(status)) - { - /* Tell expat we've reached the end of the content. Ignore the - return status. We just don't care. */ - (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */); + return SVN_NO_ERROR; +} - svn_ra_serf__xml_context_destroy(ectx->xmlctx); - apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, - xml_parser_cleanup); +/* Pool cleanup handler for request handlers. - /* ### should check XMLCTX to see if it has returned to the - ### INITIAL state. we may have ended early... */ - } + If a serf context run stops for some outside error, like when the user + cancels a request via ^C in the context loop, the handler is still + registered in the serf context. With the pool cleanup there would be + handlers registered in no freed memory. - if (status && !SERF_BUCKET_READ_ERROR(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } + This fallback kills the connection for this case, which will make serf + unregister any outstanding requests on it. */ +static apr_status_t +handler_cleanup(void *baton) +{ + svn_ra_serf__handler_t *handler = baton; + if (handler->scheduled) + { + svn_ra_serf__unschedule_handler(handler); } - /* NOTREACHED */ + return APR_SUCCESS; } - svn_ra_serf__handler_t * -svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx, - apr_pool_t *result_pool) +svn_ra_serf__create_handler(svn_ra_serf__session_t *session, + apr_pool_t *result_pool) { svn_ra_serf__handler_t *handler; - struct expat_ctx_t *ectx; - - ectx = apr_pcalloc(result_pool, sizeof(*ectx)); - ectx->xmlctx = xmlctx; - ectx->parser = NULL; - ectx->cleanup_pool = result_pool; - handler = apr_pcalloc(result_pool, sizeof(*handler)); handler->handler_pool = result_pool; - handler->response_handler = expat_response_handler; - handler->response_baton = ectx; - ectx->handler = handler; + apr_pool_cleanup_register(result_pool, handler, handler_cleanup, + apr_pool_cleanup_null); + + handler->session = session; + handler->conn = session->conns[0]; + + /* Setup the default done handler, to handle server errors */ + handler->done_delegate_baton = handler; + handler->done_delegate = response_done; return handler; } + +svn_error_t * +svn_ra_serf__uri_parse(apr_uri_t *uri, + const char *url_str, + apr_pool_t *result_pool) +{ + apr_status_t status; + + status = apr_uri_parse(result_pool, url_str, uri); + if (status) + { + /* Do not use returned error status in error message because currently + apr_uri_parse() returns APR_EGENERAL for all parsing errors. */ + return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, + _("Illegal URL '%s'"), + url_str); + } + + /* Depending the version of apr-util in use, for root paths uri.path + will be NULL or "", where serf requires "/". */ + if (uri->path == NULL || uri->path[0] == '\0') + { + uri->path = apr_pstrdup(result_pool, "/"); + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_ra_serf/util_error.c b/contrib/subversion/subversion/libsvn_ra_serf/util_error.c index da66091a3..bce935ab2 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/util_error.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/util_error.c @@ -88,7 +88,8 @@ svn_ra_serf__wrap_err(apr_status_t status, } if (err_msg) { - err->message = apr_pstrcat(err->pool, msg, ": ", err_msg, NULL); + err->message = apr_pstrcat(err->pool, msg, ": ", err_msg, + SVN_VA_NULL); } else { diff --git a/contrib/subversion/subversion/libsvn_ra_serf/xml.c b/contrib/subversion/subversion/libsvn_ra_serf/xml.c index a95eaccd2..bedcbd302 100644 --- a/contrib/subversion/subversion/libsvn_ra_serf/xml.c +++ b/contrib/subversion/subversion/libsvn_ra_serf/xml.c @@ -24,6 +24,7 @@ #include +#include #include #include "svn_hash.h" @@ -42,13 +43,35 @@ #include "ra_serf.h" +/* Fix for older expat 1.95.x's that do not define + * XML_STATUS_OK/XML_STATUS_ERROR + */ +#ifndef XML_STATUS_OK +#define XML_STATUS_OK 1 +#define XML_STATUS_ERROR 0 +#endif + +#ifndef XML_VERSION_AT_LEAST +#define XML_VERSION_AT_LEAST(major,minor,patch) \ +(((major) < XML_MAJOR_VERSION) \ + || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ + || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ + (patch) <= XML_MICRO_VERSION)) +#endif /* XML_VERSION_AT_LEAST */ + +/* Read/write chunks of this size into the spillbuf. */ +#define PARSE_CHUNK_SIZE 8000 + + struct svn_ra_serf__xml_context_t { /* Current state information. */ svn_ra_serf__xml_estate_t *current; - /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be - closed before looking for transitions from CURRENT->STATE. */ - svn_ra_serf__dav_props_t waiting; + /* If WAITING >= then we are waiting for an element to close before + resuming events. The number stored here is the amount of nested + elements open. The Xml parser will make sure the document is well + formed. */ + int waiting; /* The transition table. */ const svn_ra_serf__xml_transition_t *ttable; @@ -83,6 +106,16 @@ struct svn_ra_serf__xml_context_t { }; +/* Structure which represents an XML namespace. */ +typedef struct svn_ra_serf__ns_t { + /* The assigned name. */ + const char *xmlns; + /* The full URL for this namespace. */ + const char *url; + /* The next namespace in our list. */ + struct svn_ra_serf__ns_t *next; +} svn_ra_serf__ns_t; + struct svn_ra_serf__xml_estate_t { /* The current state value. */ int state; @@ -114,6 +147,19 @@ struct svn_ra_serf__xml_estate_t { }; +struct expat_ctx_t { + svn_ra_serf__xml_context_t *xmlctx; + XML_Parser parser; + svn_ra_serf__handler_t *handler; + const int *expected_status; + + svn_error_t *inner_error; + + /* Do not use this pool for allocation. It is merely recorded for running + the cleanup handler. */ + apr_pool_t *cleanup_pool; +}; + static void define_namespaces(svn_ra_serf__ns_t **ns_list, @@ -140,7 +186,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, /* Have we already defined this ns previously? */ for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next) { - if (strcmp(cur_ns->namespace, prefix) == 0) + if (strcmp(cur_ns->xmlns, prefix) == 0) { found = TRUE; break; @@ -157,7 +203,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, else pool = baton; new_ns = apr_palloc(pool, sizeof(*new_ns)); - new_ns->namespace = apr_pstrdup(pool, prefix); + new_ns->xmlns = apr_pstrdup(pool, prefix); new_ns->url = apr_pstrdup(pool, tmp_attrs[1]); /* Push into the front of NS_LIST. Parent states will point @@ -170,22 +216,15 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, } } - -void -svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list, - const char *const *attrs, - apr_pool_t *result_pool) -{ - define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool); -} - - /* - * Look up NAME in the NS_LIST list for previously declared namespace - * definitions and return a DAV_PROPS_T-tuple that has values. + * Look up @a name in the @a ns_list list for previously declared namespace + * definitions. + * + * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple + * representing the expanded name. */ -void -svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, +static void +expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, const svn_ra_serf__ns_t *ns_list, const char *name) { @@ -198,9 +237,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, for (ns = ns_list; ns; ns = ns->next) { - if (strncmp(ns->namespace, name, colon - name) == 0) + if (strncmp(ns->xmlns, name, colon - name) == 0) { - returned_prop_name->namespace = ns->url; + returned_prop_name->xmlns = ns->url; returned_prop_name->name = colon + 1; return; } @@ -212,9 +251,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, for (ns = ns_list; ns; ns = ns->next) { - if (! ns->namespace[0]) + if (! ns->xmlns[0]) { - returned_prop_name->namespace = ns->url; + returned_prop_name->xmlns = ns->url; returned_prop_name->name = name; return; } @@ -223,7 +262,7 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, /* If the prefix is not found, then the name is NOT within a namespace. */ - returned_prop_name->namespace = ""; + returned_prop_name->xmlns = ""; returned_prop_name->name = name; } @@ -284,6 +323,49 @@ svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket, serf_bucket_aggregate_append(agg_bucket, tmp); } +void +svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket, + serf_bucket_alloc_t *bkt_alloc, + const char *tag, ...) +{ + va_list ap; + const char *key; + serf_bucket_t *tmp; + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + va_start(ap, tag); + while ((key = va_arg(ap, char *)) != NULL) + { + const char *val = va_arg(ap, const char *); + if (val) + { + tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + } + } + va_end(ap); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("/>", 2, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); +} + void svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket, serf_bucket_alloc_t *bkt_alloc, @@ -368,7 +450,7 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag, const char *value, serf_bucket_alloc_t *bkt_alloc) { - svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, NULL); + svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, SVN_VA_NULL); if (value) { @@ -379,54 +461,6 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag, svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag); } -void -svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser, - int state) -{ - svn_ra_serf__xml_state_t *new_state; - - if (!parser->free_state) - { - new_state = apr_palloc(parser->pool, sizeof(*new_state)); - new_state->pool = svn_pool_create(parser->pool); - } - else - { - new_state = parser->free_state; - parser->free_state = parser->free_state->prev; - - svn_pool_clear(new_state->pool); - } - - if (parser->state) - { - new_state->private = parser->state->private; - new_state->ns_list = parser->state->ns_list; - } - else - { - new_state->private = NULL; - new_state->ns_list = NULL; - } - - new_state->current_state = state; - - /* Add it to the state chain. */ - new_state->prev = parser->state; - parser->state = new_state; -} - -void svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser) -{ - svn_ra_serf__xml_state_t *cur_state; - - cur_state = parser->state; - parser->state = cur_state->prev; - cur_state->prev = parser->free_state; - parser->free_state = cur_state; -} - - /* Return a pool for XES to use for self-alloc (and other specifics). */ static apr_pool_t * xes_pool(const svn_ra_serf__xml_estate_t *xes) @@ -458,11 +492,50 @@ lazy_create_pool(void *baton) return xes->state_pool; } -void -svn_ra_serf__xml_context_destroy( - svn_ra_serf__xml_context_t *xmlctx) +svn_error_t * +svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx) { + if (xmlctx->current->prev) + { + /* Probably unreachable as this would be an xml parser error */ + return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("XML stream truncated: closing '%s' missing"), + xmlctx->current->tag.name); + } + else if (! xmlctx->free_states) + { + /* If we have no items on the free_states list, we didn't push anything, + which tells us that we found an empty xml body */ + const svn_ra_serf__xml_transition_t *scan; + const svn_ra_serf__xml_transition_t *document = NULL; + const char *msg; + + for (scan = xmlctx->ttable; scan->ns != NULL; ++scan) + { + if (scan->from_state == XML_STATE_INITIAL) + { + if (document != NULL) + { + document = NULL; /* Multiple document elements defined */ + break; + } + document = scan; + } + } + + if (document) + msg = apr_psprintf(xmlctx->scratch_pool, "'%s' element not found", + document->name); + else + msg = _("document element not found"); + + return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("XML stream truncated: %s"), + msg); + } + svn_pool_destroy(xmlctx->scratch_pool); + return SVN_NO_ERROR; } svn_ra_serf__xml_context_t * @@ -577,10 +650,10 @@ svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes) } -svn_error_t * -svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name, - const char *const *attrs) +static svn_error_t * +xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, + const char *raw_name, + const char *const *attrs) { svn_ra_serf__xml_estate_t *current = xmlctx->current; svn_ra_serf__dav_props_t elemname; @@ -590,14 +663,17 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* If we're waiting for an element to close, then just ignore all other element-opens. */ - if (xmlctx->waiting.namespace != NULL) - return SVN_NO_ERROR; + if (xmlctx->waiting > 0) + { + xmlctx->waiting++; + return SVN_NO_ERROR; + } /* Look for xmlns: attributes. Lazily create the state pool if any were found. */ define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current); - svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name); + expand_ns(&elemname, current->ns_list, raw_name); for (scan = xmlctx->ttable; scan->ns != NULL; ++scan) { @@ -610,21 +686,20 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* Found a specific transition. */ if (strcmp(elemname.name, scan->name) == 0 - && strcmp(elemname.namespace, scan->ns) == 0) + && strcmp(elemname.xmlns, scan->ns) == 0) break; } if (scan->ns == NULL) { - if (current->state == 0) + if (current->state == XML_STATE_INITIAL) { return svn_error_createf( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + SVN_ERR_XML_UNEXPECTED_ELEMENT, NULL, _("XML Parsing failed: Unexpected root element '%s'"), elemname.name); } - xmlctx->waiting = elemname; - /* ### return? */ + xmlctx->waiting++; /* Start waiting for the close tag */ return SVN_NO_ERROR; } @@ -677,10 +752,11 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, name = *saveattr; value = svn_xml_get_attr_value(name, attrs); if (value == NULL) - return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND, - NULL, - _("Missing XML attribute: '%s'"), - name); + return svn_error_createf( + SVN_ERR_XML_ATTRIB_NOT_FOUND, + NULL, + _("Missing XML attribute '%s' on '%s' element"), + name, scan->name); } if (value) @@ -699,7 +775,7 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* Some basic copies to set up the new estate. */ new_xes->state = scan->to_state; new_xes->tag.name = apr_pstrdup(new_pool, elemname.name); - new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace); + new_xes->tag.xmlns = apr_pstrdup(new_pool, elemname.xmlns); new_xes->custom_close = scan->custom_close; /* Start with the parent's namespace set. */ @@ -723,39 +799,18 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, } -svn_error_t * -svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name) +static svn_error_t * +xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, + const char *raw_name) { svn_ra_serf__xml_estate_t *xes = xmlctx->current; - svn_ra_serf__dav_props_t elemname; - - svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name); - if (xmlctx->waiting.namespace != NULL) + if (xmlctx->waiting > 0) { - /* If this element is not the closer, then keep waiting... */ - if (strcmp(elemname.name, xmlctx->waiting.name) != 0 - || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0) - return SVN_NO_ERROR; - - /* Found it. Stop waiting, and go back for more. */ - xmlctx->waiting.namespace = NULL; + xmlctx->waiting--; return SVN_NO_ERROR; } - /* We should be looking at the same tag that opened the current state. - - Unknown elements are simply skipped, so we wouldn't reach this check. - - Known elements push a new state for a given tag. Some other elemname - would imply closing an ancestor tag (where did ours go?) or a spurious - tag closure. */ - if (strcmp(elemname.name, xes->tag.name) != 0 - || strcmp(elemname.namespace, xes->tag.namespace) != 0) - return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, - _("The response contains invalid XML")); - if (xes->custom_close) { const svn_string_t *cdata; @@ -799,14 +854,14 @@ svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, } -svn_error_t * -svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, - const char *data, - apr_size_t len) +static svn_error_t * +xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, + const char *data, + apr_size_t len) { /* If we are waiting for a closing tag, then we are uninterested in the cdata. Just return. */ - if (xmlctx->waiting.namespace != NULL) + if (xmlctx->waiting > 0) return SVN_NO_ERROR; /* If the current state is collecting cdata, then copy the cdata. */ @@ -831,3 +886,226 @@ svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, return SVN_NO_ERROR; } +/* svn_error_t * wrapper around XML_Parse */ +static APR_INLINE svn_error_t * +parse_xml(struct expat_ctx_t *ectx, const char *data, apr_size_t len, svn_boolean_t is_final) +{ + int xml_status = XML_Parse(ectx->parser, data, (int)len, is_final); + const char *msg; + int xml_code; + + if (xml_status == XML_STATUS_OK) + return ectx->inner_error; + + xml_code = XML_GetErrorCode(ectx->parser); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + /* If we called XML_StopParser() expat will return an abort error. If we + have a better error stored we should ignore it as it will not help + the end-user to store it in the error chain. */ + if (xml_code == XML_ERROR_ABORTED && ectx->inner_error) + return ectx->inner_error; +#endif + + msg = XML_ErrorString(xml_code); + + return svn_error_compose_create( + ectx->inner_error, + svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, + svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("Malformed XML: %s"), + msg), + _("The XML response contains invalid XML"))); +} + +/* Apr pool cleanup handler to release an XML_Parser in success and error + conditions */ +static apr_status_t +xml_parser_cleanup(void *baton) +{ + XML_Parser *xmlp = baton; + + if (*xmlp) + { + (void) XML_ParserFree(*xmlp); + *xmlp = NULL; + } + + return APR_SUCCESS; +} + +/* Conforms to Expat's XML_StartElementHandler */ +static void +expat_start(void *userData, const char *raw_name, const char **attrs) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_start(ectx->xmlctx, + raw_name, attrs)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + + +/* Conforms to Expat's XML_EndElementHandler */ +static void +expat_end(void *userData, const char *raw_name) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_end(ectx->xmlctx, raw_name)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + + +/* Conforms to Expat's XML_CharacterDataHandler */ +static void +expat_cdata(void *userData, const char *data, int len) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_cdata(ectx->xmlctx, data, len)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + + +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +expat_response_handler(serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *scratch_pool) +{ + struct expat_ctx_t *ectx = baton; + svn_boolean_t got_expected_status; + + if (ectx->expected_status) + { + const int *status = ectx->expected_status; + got_expected_status = FALSE; + + while (*status && ectx->handler->sline.code != *status) + status++; + + got_expected_status = (*status) != 0; + } + else + got_expected_status = (ectx->handler->sline.code == 200); + + if (!ectx->handler->server_error + && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300) + || ! got_expected_status)) + { + /* By deferring to expect_empty_body(), it will make a choice on + how to handle the body. Whatever the decision, the core handler + will take over, and we will not be called again. */ + + /* ### This handles xml bodies as svn-errors (returned via serf context + ### loop), but ignores non-xml errors. + + Current code depends on this behavior and checks itself while other + continues, and then verifies if work has been performed. + + ### TODO: Make error checking consistent */ + + /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */ + return svn_error_trace(svn_ra_serf__expect_empty_body( + request, response, ectx->handler, + scratch_pool)); + } + + if (!ectx->parser) + { + ectx->parser = XML_ParserCreate(NULL); + apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, + xml_parser_cleanup, apr_pool_cleanup_null); + XML_SetUserData(ectx->parser, ectx); + XML_SetElementHandler(ectx->parser, expat_start, expat_end); + XML_SetCharacterDataHandler(ectx->parser, expat_cdata); + } + + while (1) + { + apr_status_t status; + const char *data; + apr_size_t len; + svn_error_t *err; + svn_boolean_t at_eof = FALSE; + + status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + return svn_ra_serf__wrap_err(status, NULL); + else if (APR_STATUS_IS_EOF(status)) + at_eof = TRUE; + + err = parse_xml(ectx, data, len, at_eof /* isFinal */); + + if (at_eof || err) + { + /* Release xml parser state/tables. */ + apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, + xml_parser_cleanup); + } + + SVN_ERR(err); + + /* The parsing went fine. What has the bucket told us? */ + if (at_eof) + { + /* Make sure we actually got xml and clean up after parsing */ + SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx)); + } + + if (status && !SERF_BUCKET_READ_ERROR(status)) + { + return svn_ra_serf__wrap_err(status, NULL); + } + } + + /* NOTREACHED */ +} + + +svn_ra_serf__handler_t * +svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session, + svn_ra_serf__xml_context_t *xmlctx, + const int *expected_status, + apr_pool_t *result_pool) +{ + svn_ra_serf__handler_t *handler; + struct expat_ctx_t *ectx; + + ectx = apr_pcalloc(result_pool, sizeof(*ectx)); + ectx->xmlctx = xmlctx; + ectx->parser = NULL; + ectx->expected_status = expected_status; + ectx->cleanup_pool = result_pool; + + handler = svn_ra_serf__create_handler(session, result_pool); + handler->response_handler = expat_response_handler; + handler->response_baton = ectx; + + ectx->handler = handler; + + return handler; +} diff --git a/contrib/subversion/subversion/libsvn_ra_svn/client.c b/contrib/subversion/subversion/libsvn_ra_svn/client.c index 335f3215a..9ea59d20e 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/client.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/client.c @@ -233,7 +233,7 @@ svn_error_t *svn_ra_svn__auth_response(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *mech, const char *mech_arg) { - return svn_ra_svn__write_tuple(conn, pool, "w(?c)", mech, mech_arg); + return svn_error_trace(svn_ra_svn__write_tuple(conn, pool, "w(?c)", mech, mech_arg)); } static svn_error_t *handle_auth_request(svn_ra_svn__session_baton_t *sess, @@ -368,15 +368,16 @@ ra_svn_get_reporter(svn_ra_svn__session_baton_t *sess_baton, /* --- RA LAYER IMPLEMENTATION --- */ -/* (Note: *ARGV is an output parameter.) */ +/* (Note: *ARGV_P is an output parameter.) */ static svn_error_t *find_tunnel_agent(const char *tunnel, const char *hostinfo, - const char ***argv, + const char ***argv_p, apr_hash_t *config, apr_pool_t *pool) { svn_config_t *cfg; const char *val, *var, *cmd; char **cmd_argv; + const char **argv; apr_size_t len; apr_status_t status; int n; @@ -430,16 +431,22 @@ static svn_error_t *find_tunnel_agent(const char *tunnel, if (status != APR_SUCCESS) return svn_error_wrap_apr(status, _("Can't tokenize command '%s'"), cmd); - /* Append the fixed arguments to the result. */ + /* Calc number of the fixed arguments. */ for (n = 0; cmd_argv[n] != NULL; n++) ; - *argv = apr_palloc(pool, (n + 4) * sizeof(char *)); - memcpy((void *) *argv, cmd_argv, n * sizeof(char *)); - (*argv)[n++] = svn_path_uri_decode(hostinfo, pool); - (*argv)[n++] = "svnserve"; - (*argv)[n++] = "-t"; - (*argv)[n] = NULL; + argv = apr_palloc(pool, (n + 4) * sizeof(char *)); + + /* Append the fixed arguments to the result. */ + for (n = 0; cmd_argv[n] != NULL; n++) + argv[n] = cmd_argv[n]; + + argv[n++] = svn_path_uri_decode(hostinfo, pool); + argv[n++] = "svnserve"; + argv[n++] = "-t"; + argv[n] = NULL; + + *argv_p = argv; return SVN_NO_ERROR; } @@ -452,13 +459,17 @@ static void handle_child_process_error(apr_pool_t *pool, apr_status_t status, { svn_ra_svn_conn_t *conn; apr_file_t *in_file, *out_file; + svn_stream_t *in_stream, *out_stream; svn_error_t *err; if (apr_file_open_stdin(&in_file, pool) || apr_file_open_stdout(&out_file, pool)) return; - conn = svn_ra_svn_create_conn3(NULL, in_file, out_file, + in_stream = svn_stream_from_aprfile2(in_file, FALSE, pool); + out_stream = svn_stream_from_aprfile2(out_file, FALSE, pool); + + conn = svn_ra_svn_create_conn4(NULL, in_stream, out_stream, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, pool); err = svn_error_wrap_apr(status, _("Error in child process: %s"), desc); @@ -529,7 +540,11 @@ static svn_error_t *make_tunnel(const char **args, svn_ra_svn_conn_t **conn, apr_file_inherit_unset(proc->out); /* Guard against dotfile output to stdout on the server. */ - *conn = svn_ra_svn_create_conn3(NULL, proc->out, proc->in, + *conn = svn_ra_svn_create_conn4(NULL, + svn_stream_from_aprfile2(proc->out, FALSE, + pool), + svn_stream_from_aprfile2(proc->in, FALSE, + pool), SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, pool); err = svn_ra_svn__skip_leading_garbage(*conn, pool); @@ -556,24 +571,54 @@ static svn_error_t *parse_url(const char *url, apr_uri_t *uri, return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Illegal svn repository URL '%s'"), url); - if (! uri->port) - uri->port = SVN_RA_SVN_PORT; - return SVN_NO_ERROR; } +/* This structure is used as a baton for the pool cleanup function to + store tunnel parameters used by the close-tunnel callback. */ +struct tunnel_data_t { + void *tunnel_context; + void *tunnel_baton; + svn_ra_close_tunnel_func_t close_tunnel; + svn_stream_t *request; + svn_stream_t *response; +}; + +/* Pool cleanup function that invokes the close-tunnel callback. */ +static apr_status_t close_tunnel_cleanup(void *baton) +{ + const struct tunnel_data_t *const td = baton; + + if (td->close_tunnel) + td->close_tunnel(td->tunnel_context, td->tunnel_baton); + + svn_error_clear(svn_stream_close(td->request)); + + /* We might have one stream to use for both request and response! */ + if (td->request != td->response) + svn_error_clear(svn_stream_close(td->response)); + + return APR_SUCCESS; /* ignored */ +} + /* Open a session to URL, returning it in *SESS_P, allocating it in POOL. URI is a parsed version of URL. CALLBACKS and CALLBACKS_BATON - are provided by the caller of ra_svn_open. If tunnel_argv is non-null, - it points to a program argument list to use when invoking the tunnel agent. + are provided by the caller of ra_svn_open. If TUNNEL_NAME is not NULL, + it is the name of the tunnel type parsed from the URL scheme. + If TUNNEL_ARGV is not NULL, it points to a program argument list to use + when invoking the tunnel agent. */ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, const char *url, const apr_uri_t *uri, + const char *tunnel_name, const char **tunnel_argv, + apr_hash_t *config, const svn_ra_callbacks2_t *callbacks, void *callbacks_baton, - apr_pool_t *pool) + svn_auth_baton_t *auth_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_svn__session_baton_t *sess; svn_ra_svn_conn_t *conn; @@ -581,26 +626,67 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, apr_uint64_t minver, maxver; apr_array_header_t *mechlist, *server_caplist, *repos_caplist; const char *client_string = NULL; + apr_pool_t *pool = result_pool; sess = apr_palloc(pool, sizeof(*sess)); sess->pool = pool; - sess->is_tunneled = (tunnel_argv != NULL); + sess->is_tunneled = (tunnel_name != NULL); sess->url = apr_pstrdup(pool, url); sess->user = uri->user; sess->hostname = uri->hostname; - sess->realm_prefix = apr_psprintf(pool, "", uri->hostname, - uri->port); + sess->tunnel_name = tunnel_name; sess->tunnel_argv = tunnel_argv; sess->callbacks = callbacks; sess->callbacks_baton = callbacks_baton; sess->bytes_read = sess->bytes_written = 0; + sess->auth_baton = auth_baton; - if (tunnel_argv) - SVN_ERR(make_tunnel(tunnel_argv, &conn, pool)); + if (config) + SVN_ERR(svn_config_copy_config(&sess->config, config, pool)); else + sess->config = NULL; + + if (tunnel_name) { - SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool)); - conn = svn_ra_svn_create_conn3(sock, NULL, NULL, + sess->realm_prefix = apr_psprintf(pool, "", + tunnel_name, + uri->hostname, uri->port); + + if (tunnel_argv) + SVN_ERR(make_tunnel(tunnel_argv, &conn, pool)); + else + { + struct tunnel_data_t *const td = apr_palloc(pool, sizeof(*td)); + + td->tunnel_baton = callbacks->tunnel_baton; + td->close_tunnel = NULL; + + SVN_ERR(callbacks->open_tunnel_func( + &td->request, &td->response, + &td->close_tunnel, &td->tunnel_context, + callbacks->tunnel_baton, tunnel_name, + uri->user, uri->hostname, uri->port, + callbacks->cancel_func, callbacks_baton, + pool)); + + apr_pool_cleanup_register(pool, td, close_tunnel_cleanup, + apr_pool_cleanup_null); + + conn = svn_ra_svn_create_conn4(NULL, td->response, td->request, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, + 0, 0, pool); + SVN_ERR(svn_ra_svn__skip_leading_garbage(conn, pool)); + } + } + else + { + sess->realm_prefix = apr_psprintf(pool, "", uri->hostname, + uri->port ? uri->port : SVN_RA_SVN_PORT); + + SVN_ERR(make_connection(uri->hostname, + uri->port ? uri->port : SVN_RA_SVN_PORT, + &sock, pool)); + conn = svn_ra_svn_create_conn4(sock, NULL, NULL, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, pool); } @@ -618,7 +704,7 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, &client_string, pool)); if (client_string) sess->useragent = apr_pstrcat(pool, SVN_RA_SVN__DEFAULT_USERAGENT " ", - client_string, (char *)NULL); + client_string, SVN_VA_NULL); else sess->useragent = SVN_RA_SVN__DEFAULT_USERAGENT; @@ -722,10 +808,12 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, const char *url, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *sess_pool = svn_pool_create(pool); + apr_pool_t *sess_pool = svn_pool_create(result_pool); svn_ra_svn__session_baton_t *sess; const char *tunnel, **tunnel_argv; apr_uri_t uri; @@ -737,11 +825,18 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, SVN_ERR(parse_url(url, &uri, sess_pool)); - parse_tunnel(url, &tunnel, pool); + parse_tunnel(url, &tunnel, result_pool); - if (tunnel) + /* Use the default tunnel implementation if we got a tunnel name, + but either do not have tunnel handler callbacks installed, or + the handlers don't like the tunnel name. */ + if (tunnel + && (!callbacks->open_tunnel_func + || (callbacks->check_tunnel_func && callbacks->open_tunnel_func + && !callbacks->check_tunnel_func(callbacks->tunnel_baton, + tunnel)))) SVN_ERR(find_tunnel_agent(tunnel, uri.hostinfo, &tunnel_argv, config, - pool)); + result_pool)); else tunnel_argv = NULL; @@ -749,20 +844,37 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS) : NULL; - svn_auth_set_parameter(callbacks->auth_baton, + svn_auth_set_parameter(auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, cfg_client); - svn_auth_set_parameter(callbacks->auth_baton, + svn_auth_set_parameter(auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, cfg); /* We open the session in a subpool so we can get rid of it if we reparent with a server that doesn't support reparenting. */ - SVN_ERR(open_session(&sess, url, &uri, tunnel_argv, - callbacks, callback_baton, sess_pool)); + SVN_ERR(open_session(&sess, url, &uri, tunnel, tunnel_argv, config, + callbacks, callback_baton, + auth_baton, sess_pool, scratch_pool)); session->priv = sess; return SVN_NO_ERROR; } +static svn_error_t *ra_svn_dup_session(svn_ra_session_t *new_session, + svn_ra_session_t *old_session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_svn__session_baton_t *old_sess = old_session->priv; + + SVN_ERR(ra_svn_open(new_session, NULL, new_session_url, + old_sess->callbacks, old_sess->callbacks_baton, + old_sess->auth_baton, old_sess->config, + result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session, const char *url, apr_pool_t *pool) @@ -792,8 +904,9 @@ static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session, sess_pool = svn_pool_create(ra_session->pool); err = parse_url(url, &uri, sess_pool); if (! err) - err = open_session(&new_sess, url, &uri, sess->tunnel_argv, - sess->callbacks, sess->callbacks_baton, sess_pool); + err = open_session(&new_sess, url, &uri, sess->tunnel_name, sess->tunnel_argv, + sess->config, sess->callbacks, sess->callbacks_baton, + sess->auth_baton, sess_pool, sess_pool); /* We destroy the new session pool on error, since it is allocated in the main session pool. */ if (err) @@ -954,6 +1067,9 @@ static svn_error_t *ra_svn_end_commit(void *baton) &(commit_info->author), &(commit_info->post_commit_err))); + commit_info->repos_root = apr_pstrdup(ccb->pool, + ccb->sess_baton->conn->repos_root); + if (ccb->callback) SVN_ERR(ccb->callback(commit_info, ccb->callback_baton, ccb->pool)); @@ -986,11 +1102,11 @@ static svn_error_t *ra_svn_commit(svn_ra_session_t *session, "a log message with pre-1.5 servers; " "consider passing an empty one, or upgrading " "the server")); - } + } else if (log_msg == NULL) /* 1.5+ server. Set LOG_MSG to something, since the 'logmsg' argument to the 'commit' protocol command is non-optional; on the server side, - only REVPROP_TABLE will be used, and LOG_MSG will be ignored. The + only REVPROP_TABLE will be used, and LOG_MSG will be ignored. The "svn:log" member of REVPROP_TABLE table is NULL, therefore the commit will have a NULL log message (not just "", really NULL). @@ -1075,7 +1191,6 @@ parse_iproplist(apr_array_header_t **inherited_props, { int i; - const char *repos_root_url; apr_pool_t *iterpool; if (iproplist == NULL) @@ -1088,8 +1203,6 @@ parse_iproplist(apr_array_header_t **inherited_props, return SVN_NO_ERROR; } - SVN_ERR(ra_svn_get_repos_root(session, &repos_root_url, scratch_pool)); - *inherited_props = apr_array_make( result_pool, iproplist->nelts, sizeof(svn_prop_inherited_item_t *)); @@ -1115,16 +1228,14 @@ parse_iproplist(apr_array_header_t **inherited_props, SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, iterpool, "cl", &parent_rel_path, &iprop_list)); SVN_ERR(svn_ra_svn__parse_proplist(iprop_list, iterpool, &iprops)); - new_iprop->path_or_url = svn_path_url_add_component2(repos_root_url, - parent_rel_path, - result_pool); - new_iprop->prop_hash = apr_hash_make(result_pool); + new_iprop->path_or_url = apr_pstrdup(result_pool, parent_rel_path); + new_iprop->prop_hash = svn_hash__make(result_pool); for (hi = apr_hash_first(iterpool, iprops); 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 *name = apr_hash_this_key(hi); + svn_string_t *value = apr_hash_this_val(hi); svn_hash_sets(new_iprop->prop_hash, apr_pstrdup(result_pool, name), svn_string_dup(value, result_pool)); @@ -1241,7 +1352,10 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_LAST_AUTHOR)); - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); + /* Always send the, nominally optional, want-iprops as "false" to + workaround a bug in svnserve 1.8.0-1.8.8 that causes the server + to see "true" if it is omitted. */ + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b)", FALSE)); SVN_ERR(handle_auth_request(sess_baton, pool)); SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, "rll", &rev, &proplist, @@ -1257,7 +1371,7 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, return SVN_NO_ERROR; /* Interpret the directory list. */ - *dirents = apr_hash_make(pool); + *dirents = svn_hash__make(pool); for (i = 0; i < dirlist->nelts; i++) { const char *name, *kind, *cdate, *cauthor; @@ -1273,7 +1387,14 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cwnbr(?c)(?c)", &name, &kind, &size, &has_props, &crev, &cdate, &cauthor)); - name = svn_relpath_canonicalize(name, pool); + + /* Nothing to sanitize here. Any multi-segment path is simply + illegal in the hash returned by svn_ra_get_dir2. */ + if (strchr(name, '/')) + return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Invalid directory entry name '%s'"), + name); + dirent = svn_dirent_create(pool); dirent->kind = svn_node_kind_from_word(kind); dirent->size = size;/* FIXME: svn_filesize_t */ @@ -1345,7 +1466,7 @@ static svn_error_t *ra_svn_get_mergeinfo(svn_ra_session_t *session, *catalog = NULL; if (mergeinfo_tuple->nelts > 0) { - *catalog = apr_hash_make(pool); + *catalog = svn_hash__make(pool); for (i = 0; i < mergeinfo_tuple->nelts; i++) { svn_mergeinfo_t for_path; @@ -1501,6 +1622,10 @@ perform_ra_svn_log(svn_error_t **outer_error, const char *path; char *name; svn_boolean_t want_custom_revprops; + svn_boolean_t want_author = FALSE; + svn_boolean_t want_message = FALSE; + svn_boolean_t want_date = FALSE; + int nreceived = 0; SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "log")); if (paths) @@ -1523,10 +1648,14 @@ perform_ra_svn_log(svn_error_t **outer_error, { name = APR_ARRAY_IDX(revprops, i, char *); SVN_ERR(svn_ra_svn__write_cstring(conn, pool, name)); - if (!want_custom_revprops - && strcmp(name, SVN_PROP_REVISION_AUTHOR) != 0 - && strcmp(name, SVN_PROP_REVISION_DATE) != 0 - && strcmp(name, SVN_PROP_REVISION_LOG) != 0) + + if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) + want_author = TRUE; + else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) + want_date = TRUE; + else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) + want_message = TRUE; + else want_custom_revprops = TRUE; } SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); @@ -1534,6 +1663,10 @@ perform_ra_svn_log(svn_error_t **outer_error, else { SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!w())", "all-revprops")); + + want_author = TRUE; + want_date = TRUE; + want_message = TRUE; want_custom_revprops = TRUE; } @@ -1554,7 +1687,6 @@ perform_ra_svn_log(svn_error_t **outer_error, svn_ra_svn_item_t *item; apr_hash_t *cphash; svn_revnum_t rev; - int nreceived; svn_pool_clear(iterpool); SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item)); @@ -1597,11 +1729,12 @@ perform_ra_svn_log(svn_error_t **outer_error, if (cplist->nelts > 0) { /* Interpret the changed-paths list. */ - cphash = apr_hash_make(iterpool); + cphash = svn_hash__make(iterpool); for (i = 0; i < cplist->nelts; i++) { svn_log_changed_path2_t *change; - const char *copy_path, *action, *cpath, *kind_str; + svn_string_t *cpath; + const char *copy_path, *action, *kind_str; apr_uint64_t text_mods, prop_mods; svn_revnum_t copy_rev; svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(cplist, i, @@ -1610,14 +1743,19 @@ perform_ra_svn_log(svn_error_t **outer_error, if (elt->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Changed-path entry not a list")); - SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, iterpool, - "cw(?cr)?(?c?BB)", + SVN_ERR(svn_ra_svn__read_data_log_changed_entry(elt->u.list, &cpath, &action, ©_path, ©_rev, &kind_str, &text_mods, &prop_mods)); - cpath = svn_fspath__canonicalize(cpath, iterpool); - if (copy_path) + + if (!svn_fspath__is_canonical(cpath->data)) + { + cpath->data = svn_fspath__canonicalize(cpath->data, iterpool); + cpath->len = strlen(cpath->data); + } + if (copy_path && !svn_fspath__is_canonical(copy_path)) copy_path = svn_fspath__canonicalize(copy_path, iterpool); + change = svn_log_changed_path2_create(iterpool); change->action = *action; change->copyfrom_path = copy_path; @@ -1625,13 +1763,16 @@ perform_ra_svn_log(svn_error_t **outer_error, change->node_kind = svn_node_kind_from_word(kind_str); change->text_modified = optbool_to_tristate(text_mods); change->props_modified = optbool_to_tristate(prop_mods); - svn_hash_sets(cphash, cpath, change); + apr_hash_set(cphash, cpath->data, cpath->len, change); } } else cphash = NULL; - nreceived = 0; + /* Invoke RECEIVER + - Except if the server sends more than a >= 1 limit top level items + - Or when the callback reported a SVN_ERR_CEASE_INVOCATION + in an earlier invocation. */ if (! (limit && (nest_level == 0) && (++nreceived > limit)) && ! *outer_error) { @@ -1647,37 +1788,18 @@ perform_ra_svn_log(svn_error_t **outer_error, SVN_ERR(svn_ra_svn__parse_proplist(rplist, iterpool, &log_entry->revprops)); if (log_entry->revprops == NULL) - log_entry->revprops = apr_hash_make(iterpool); - if (revprops == NULL) - { - /* Caller requested all revprops; set author/date/log. */ - if (author) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR, - author); - if (date) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE, - date); - if (message) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_LOG, - message); - } - else - { - /* Caller requested some; maybe set author/date/log. */ - for (i = 0; i < revprops->nelts; i++) - { - name = APR_ARRAY_IDX(revprops, i, char *); - if (author && strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_AUTHOR, author); - if (date && strcmp(name, SVN_PROP_REVISION_DATE) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_DATE, date); - if (message && strcmp(name, SVN_PROP_REVISION_LOG) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_LOG, message); - } - } + log_entry->revprops = svn_hash__make(iterpool); + + if (author && want_author) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_AUTHOR, author); + if (date && want_date) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_DATE, date); + if (message && want_message) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_LOG, message); + err = receiver(receiver_baton, log_entry, iterpool); if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) { @@ -1863,7 +1985,7 @@ static svn_error_t *ra_svn_get_locations(svn_ra_session_t *session, /* Read the response. This is so the server would have a chance to * report an error. */ - return svn_ra_svn__read_cmd_response(conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "")); } static svn_error_t * @@ -2009,7 +2131,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session, { svn_stream_t *stream; - if (d_handler) + if (d_handler && d_handler != svn_delta_noop_window_handler) stream = svn_txdelta_parse_svndiff(d_handler, d_baton, TRUE, rev_pool); else @@ -2552,7 +2674,7 @@ static svn_error_t *ra_svn_replay(svn_ra_session_t *session, SVN_ERR(svn_ra_svn_drive_editor2(sess->conn, pool, editor, edit_baton, NULL, TRUE)); - return svn_ra_svn__read_cmd_response(sess->conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(sess->conn, pool, "")); } @@ -2621,7 +2743,7 @@ ra_svn_replay_range(svn_ra_session_t *session, } svn_pool_destroy(iterpool); - return svn_ra_svn__read_cmd_response(sess->conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(sess->conn, pool, "")); } @@ -2687,7 +2809,8 @@ ra_svn_get_deleted_rev(svn_ra_session_t *session, SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool), N_("'get-deleted-rev' not implemented"))); - return svn_ra_svn__read_cmd_response(conn, pool, "r", revision_deleted); + return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "r", + revision_deleted)); } static svn_error_t * @@ -2713,6 +2836,16 @@ ra_svn_get_inherited_props(svn_ra_session_t *session, svn_ra_svn__session_baton_t *sess_baton = session->priv; svn_ra_svn_conn_t *conn = sess_baton->conn; apr_array_header_t *iproplist; + svn_boolean_t iprop_capable; + + SVN_ERR(ra_svn_has_capability(session, &iprop_capable, + SVN_RA_CAPABILITY_INHERITED_PROPS, + scratch_pool)); + + /* If we don't support native iprop handling, use the implementation + in libsvn_ra */ + if (!iprop_capable) + return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); SVN_ERR(svn_ra_svn__write_cmd_get_iprops(conn, scratch_pool, path, revision)); @@ -2729,6 +2862,7 @@ static const svn_ra__vtable_t ra_svn_vtable = { ra_svn_get_description, ra_svn_get_schemes, ra_svn_open, + ra_svn_dup_session, ra_svn_reparent, ra_svn_get_session_url, ra_svn_get_latest_rev, diff --git a/contrib/subversion/subversion/libsvn_ra_svn/cram.c b/contrib/subversion/subversion/libsvn_ra_svn/cram.c index 1e54ac812..b92f37b4a 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/cram.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/cram.c @@ -114,7 +114,7 @@ static svn_error_t *fail(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *msg) { SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", msg)); - return svn_ra_svn__flush(conn, pool); + return svn_error_trace(svn_ra_svn__flush(conn, pool)); } /* If we can, make the nonce with random bytes. If we can't... well, diff --git a/contrib/subversion/subversion/libsvn_ra_svn/cyrus_auth.c b/contrib/subversion/subversion/libsvn_ra_svn/cyrus_auth.c index 82e33d364..ac94a485a 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/cyrus_auth.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/cyrus_auth.c @@ -488,7 +488,7 @@ static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess, pmech - mechstring); const char *tail = pmech + strlen(mech); - mechstring = apr_pstrcat(pool, head, tail, (char *)NULL); + mechstring = apr_pstrcat(pool, head, tail, SVN_VA_NULL); again = TRUE; } } @@ -704,11 +704,13 @@ static void sasl_timeout_cb(void *baton, apr_interval_time_t interval) svn_ra_svn__stream_timeout(sasl_baton->stream, interval); } -/* Implements ra_svn_pending_fn_t. */ -static svn_boolean_t sasl_pending_cb(void *baton) +/* Implements svn_stream_data_available_fn_t. */ +static svn_error_t * +sasl_data_available_cb(void *baton, svn_boolean_t *data_available) { sasl_baton_t *sasl_baton = baton; - return svn_ra_svn__stream_pending(sasl_baton->stream); + return svn_error_trace(svn_ra_svn__stream_data_available(sasl_baton->stream, + data_available)); } svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn, @@ -766,10 +768,19 @@ svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn, /* Wrap the existing stream. */ sasl_baton->stream = conn->stream; - conn->stream = svn_ra_svn__stream_create(sasl_baton, sasl_read_cb, - sasl_write_cb, - sasl_timeout_cb, - sasl_pending_cb, conn->pool); + { + svn_stream_t *sasl_in = svn_stream_create(sasl_baton, conn->pool); + svn_stream_t *sasl_out = svn_stream_create(sasl_baton, conn->pool); + + svn_stream_set_read2(sasl_in, sasl_read_cb, NULL /* use default */); + svn_stream_set_data_available(sasl_in, sasl_data_available_cb); + svn_stream_set_write(sasl_out, sasl_write_cb); + + conn->stream = svn_ra_svn__stream_create(sasl_in, sasl_out, + sasl_baton, + sasl_timeout_cb, + conn->pool); + } /* Yay, we have a security layer! */ conn->encrypted = TRUE; } @@ -807,10 +818,10 @@ svn_error_t *svn_ra_svn__get_addresses(const char **local_addrport, /* Format the IP address and port number like this: a.b.c.d;port */ *local_addrport = apr_pstrcat(pool, local_addr, ";", apr_itoa(pool, (int)local_sa->port), - (char *)NULL); + SVN_VA_NULL); *remote_addrport = apr_pstrcat(pool, remote_addr, ";", apr_itoa(pool, (int)remote_sa->port), - (char *)NULL); + SVN_VA_NULL); } return SVN_NO_ERROR; } @@ -849,14 +860,14 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess, mechstring = apr_pstrcat(pool, mechstring, i == 0 ? "" : " ", - elt->u.word, (char *)NULL); + elt->u.word, SVN_VA_NULL); } } realmstring = apr_psprintf(pool, "%s %s", sess->realm_prefix, realm); /* Initialize the credential baton. */ - cred_baton.auth_baton = sess->callbacks->auth_baton; + cred_baton.auth_baton = sess->auth_baton; cred_baton.realmstring = realmstring; cred_baton.pool = pool; @@ -935,8 +946,8 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess, the CRAM-MD5 or ANONYMOUS plugins, in which case we can simply use the built-in implementation. In all other cases this call will be useless, but hey, at least we'll get consistent error messages. */ - return svn_ra_svn__do_internal_auth(sess, mechlist, - realm, pool); + return svn_error_trace(svn_ra_svn__do_internal_auth(sess, mechlist, + realm, pool)); } return err; } diff --git a/contrib/subversion/subversion/libsvn_ra_svn/deprecated.c b/contrib/subversion/subversion/libsvn_ra_svn/deprecated.c index 8182a4d5a..7f0c8fde2 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/deprecated.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/deprecated.c @@ -21,6 +21,10 @@ * ==================================================================== */ +/* We define this here to remove any further warnings about the usage of + deprecated functions in this file. */ +#define SVN_DEPRECATED + #include "svn_ra_svn.h" #include "private/svn_ra_svn_private.h" @@ -232,3 +236,49 @@ svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn, { return svn_error_trace(svn_ra_svn__write_cmd_failure(conn, pool, err)); } + +/* From marshal.c */ +svn_ra_svn_conn_t * +svn_ra_svn_create_conn3(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + int compression_level, + apr_size_t zero_copy_limit, + apr_size_t error_check_interval, + apr_pool_t *pool) +{ + svn_stream_t *in_stream = NULL; + svn_stream_t *out_stream = NULL; + + if (in_file) + in_stream = svn_stream_from_aprfile2(in_file, FALSE, pool); + if (out_file) + out_stream = svn_stream_from_aprfile2(out_file, FALSE, pool); + + return svn_ra_svn_create_conn4(sock, in_stream, out_stream, + compression_level, zero_copy_limit, + error_check_interval, pool); +} + +svn_ra_svn_conn_t * +svn_ra_svn_create_conn2(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + int compression_level, + apr_pool_t *pool) +{ + return svn_ra_svn_create_conn3(sock, in_file, out_file, + compression_level, 0, 0, pool); +} + +/* backward-compatible implementation using the default compression level */ +svn_ra_svn_conn_t * +svn_ra_svn_create_conn(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + apr_pool_t *pool) +{ + return svn_ra_svn_create_conn3(sock, in_file, out_file, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, + pool); +} diff --git a/contrib/subversion/subversion/libsvn_ra_svn/editorp.c b/contrib/subversion/subversion/libsvn_ra_svn/editorp.c index cc1d8ab2f..88af89859 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/editorp.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/editorp.c @@ -91,7 +91,7 @@ typedef struct ra_svn_driver_state_t { different purpose instead: at apply-textdelta time, we set it to a subpool of the file pool, which is destroyed in textdelta-end. */ typedef struct ra_svn_token_entry_t { - const char *token; + svn_string_t *token; void *baton; svn_boolean_t is_file; svn_stream_t *dstream; /* svndiff stream for apply_textdelta */ @@ -126,6 +126,7 @@ static ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn, static svn_error_t * check_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool) { + svn_boolean_t available; SVN_ERR_ASSERT(!eb->got_status); /* reset TX counter */ @@ -135,7 +136,8 @@ check_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool) eb->conn->may_check_for_error = eb->conn->error_check_interval == 0; /* any incoming data? */ - if (svn_ra_svn__input_waiting(eb->conn, pool)) + SVN_ERR(svn_ra_svn__data_available(eb->conn, &available)); + if (available) { eb->got_status = TRUE; SVN_ERR(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); @@ -393,11 +395,13 @@ static svn_error_t *ra_svn_close_edit(void *edit_baton, apr_pool_t *pool) SVN_ERR_ASSERT(!eb->got_status); eb->got_status = TRUE; SVN_ERR(svn_ra_svn__write_cmd_close_edit(eb->conn, pool)); - err = svn_ra_svn__read_cmd_response(eb->conn, pool, ""); + err = svn_error_trace(svn_ra_svn__read_cmd_response(eb->conn, pool, "")); if (err) { - svn_error_clear(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); - return err; + return svn_error_compose_create( + err, + svn_error_trace( + svn_ra_svn__write_cmd_abort_edit(eb->conn, pool))); } if (eb->callback) SVN_ERR(eb->callback(eb->callback_baton)); @@ -461,27 +465,31 @@ void svn_ra_svn_get_editor(const svn_delta_editor_t **editor, /* Store a token entry. The token string will be copied into pool. */ static ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds, - void *baton, const char *token, + void *baton, + svn_string_t *token, svn_boolean_t is_file, apr_pool_t *pool) { ra_svn_token_entry_t *entry; entry = apr_palloc(pool, sizeof(*entry)); - entry->token = apr_pstrdup(pool, token); + entry->token = svn_string_dup(token, pool); entry->baton = baton; entry->is_file = is_file; entry->dstream = NULL; entry->pool = pool; - svn_hash_sets(ds->tokens, entry->token, entry); + + apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry); + return entry; } -static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, const char *token, +static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, + svn_string_t *token, svn_boolean_t is_file, ra_svn_token_entry_t **entry) { - *entry = svn_hash_gets(ds->tokens, token); + *entry = apr_hash_get(ds->tokens, token->data, token->len); if (!*entry || (*entry)->is_file != is_file) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Invalid file or dir token during edit")); @@ -507,10 +515,10 @@ static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, { svn_revnum_t rev; apr_pool_t *subpool; - const char *token; + svn_string_t *token; void *root_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)c", &rev, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)s", &rev, &token)); subpool = svn_pool_create(ds->pool); SVN_CMD_ERR(ds->editor->open_root(ds->edit_baton, rev, subpool, &root_baton)); @@ -523,11 +531,12 @@ static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token; + const char *path; + svn_string_t *token; svn_revnum_t rev; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)c", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)s", &path, &rev, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); path = svn_relpath_canonicalize(path, pool); @@ -540,13 +549,14 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *child_token, *copy_path; + const char *path, *copy_path; + svn_string_t *token, *child_token; svn_revnum_t copy_rev; ra_svn_token_entry_t *entry; apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, &child_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -573,13 +583,14 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *child_token; + const char *path; + svn_string_t *token, *child_token; svn_revnum_t rev; ra_svn_token_entry_t *entry; apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, &child_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -595,11 +606,12 @@ static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token, *name; + svn_string_t *token; + const char *name; svn_string_t *value; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, &value)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); SVN_CMD_ERR(ds->editor->change_dir_prop(entry->baton, name, value, @@ -612,16 +624,16 @@ static svn_error_t *ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse and look up the directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Close the directory and destroy the baton. */ SVN_CMD_ERR(ds->editor->close_directory(entry->baton, pool)); - svn_hash_sets(ds->tokens, token, NULL); + apr_hash_set(ds->tokens, token->data, token->len, NULL); svn_pool_destroy(entry->pool); return SVN_NO_ERROR; } @@ -632,11 +644,11 @@ static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, ra_svn_driver_state_t *ds) { const char *path; - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse parameters and look up the directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cc", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -649,15 +661,19 @@ static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *file_token, *copy_path; + const char *path, *copy_path; + svn_string_t *token, *file_token; svn_revnum_t copy_rev; ra_svn_token_entry_t *entry, *file_entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, &file_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; - path = svn_relpath_canonicalize(path, pool); + + /* The PATH should be canonical .. but never trust incoming data. */ + if (!svn_relpath_is_canonical(path)) + path = svn_relpath_canonicalize(path, pool); /* Some operations pass COPY_PATH as a full URL (commits, etc.). Others (replay, e.g.) deliver an fspath. That's ... annoying. */ @@ -680,15 +696,20 @@ static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *file_token; + const char *path; + svn_string_t *token, *file_token; svn_revnum_t rev; ra_svn_token_entry_t *entry, *file_entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, &file_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; - path = svn_relpath_canonicalize(path, pool); + + /* The PATH should be canonical .. but never trust incoming data. */ + if (!svn_relpath_is_canonical(path)) + path = svn_relpath_canonicalize(path, pool); + file_entry = store_token(ds, NULL, file_token, TRUE, ds->file_pool); SVN_CMD_ERR(ds->editor->open_file(path, entry->baton, rev, ds->file_pool, &file_entry->baton)); @@ -700,14 +721,14 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; svn_txdelta_window_handler_t wh; void *wh_baton; char *base_checksum; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", &token, &base_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (entry->dstream) @@ -725,12 +746,12 @@ static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; svn_string_t *str; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &token, &str)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ss", &token, &str)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (!entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -744,11 +765,11 @@ static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (!entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -764,11 +785,11 @@ static svn_error_t *ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token, *name; - svn_string_t *value; + const char *name; + svn_string_t *token, *value; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, &value)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); SVN_CMD_ERR(ds->editor->change_file_prop(entry->baton, name, value, pool)); @@ -780,18 +801,18 @@ static svn_error_t *ra_svn_handle_close_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; const char *text_checksum; /* Parse arguments and look up the file token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", &token, &text_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); /* Close the file and destroy the baton. */ SVN_CMD_ERR(ds->editor->close_file(entry->baton, text_checksum, pool)); - svn_hash_sets(ds->tokens, token, NULL); + apr_hash_set(ds->tokens, token->data, token->len, NULL); if (--ds->file_refs == 0) svn_pool_clear(ds->file_pool); return SVN_NO_ERROR; @@ -803,11 +824,11 @@ static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, ra_svn_driver_state_t *ds) { const char *path; - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse parameters and look up the parent directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cc", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -978,7 +999,12 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, { /* Abort the edit and use non-blocking I/O to write the error. */ if (editor) - svn_error_clear(editor->abort_edit(edit_baton, subpool)); + { + err = svn_error_compose_create( + err, + svn_error_trace(editor->abort_edit(edit_baton, + subpool))); + } svn_ra_svn__set_block_handler(conn, blocked_write, &state); } write_err = svn_ra_svn__write_cmd_failure( @@ -987,7 +1013,7 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, if (!write_err) write_err = svn_ra_svn__flush(conn, subpool); svn_ra_svn__set_block_handler(conn, NULL, NULL); - svn_error_clear(err); + svn_error_clear(err); /* We just sent this error */ SVN_ERR(write_err); break; } diff --git a/contrib/subversion/subversion/libsvn_ra_svn/internal_auth.c b/contrib/subversion/subversion/libsvn_ra_svn/internal_auth.c index eac2ccdec..8e63ab5b5 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/internal_auth.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/internal_auth.c @@ -95,7 +95,7 @@ svn_ra_svn__do_internal_auth(svn_ra_svn__session_baton_t *sess, { SVN_ERR(svn_auth_first_credentials(&creds, &iterstate, SVN_AUTH_CRED_SIMPLE, realmstring, - sess->callbacks->auth_baton, pool)); + sess->auth_baton, pool)); if (!creds) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Can't get password")); diff --git a/contrib/subversion/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in b/contrib/subversion/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in new file mode 100644 index 000000000..4d6768986 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_ra_svn +Description: Subversion SVN Protocol Repository Access Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_ra_svn @SVN_SASL_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_ra_svn/marshal.c b/contrib/subversion/subversion/libsvn_ra_svn/marshal.c index 7cf483f50..0778269fa 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/marshal.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/marshal.c @@ -40,6 +40,7 @@ #include "svn_ra_svn.h" #include "svn_private_config.h" #include "svn_ctype.h" +#include "svn_sorts.h" #include "svn_time.h" #include "ra_svn.h" @@ -47,6 +48,7 @@ #include "private/svn_string_private.h" #include "private/svn_dep_compat.h" #include "private/svn_error_private.h" +#include "private/svn_subr_private.h" #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n') @@ -56,6 +58,19 @@ #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000) +/* We don't use "words" longer than this in our protocol. The longest word + * we are currently using is only about 16 chars long but we leave room for + * longer future capability and command names. + */ +#define MAX_WORD_LENGTH 31 + +/* The generic parsers will use the following value to limit the recursion + * depth to some reasonable value. The current protocol implementation + * actually uses only maximum item nesting level of around 5. So, there is + * plenty of headroom here. + */ +#define ITEM_NESTING_LIMIT 64 + /* Return the APR socket timeout to be used for the connection depending * on whether there is a blockage handler or zero copy has been activated. */ static apr_interval_time_t @@ -66,19 +81,20 @@ get_timeout(svn_ra_svn_conn_t *conn) /* --- CONNECTION INITIALIZATION --- */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, +svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock, + svn_stream_t *in_stream, + svn_stream_t *out_stream, int compression_level, apr_size_t zero_copy_limit, apr_size_t error_check_interval, - apr_pool_t *pool) + apr_pool_t *result_pool) { svn_ra_svn_conn_t *conn; - void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); + void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE); - assert((sock && !in_file && !out_file) || (!sock && in_file && out_file)); + assert((sock && !in_stream && !out_stream) + || (!sock && in_stream && out_stream)); #ifdef SVN_HAVE_SASL conn->sock = sock; conn->encrypted = FALSE; @@ -92,15 +108,15 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, conn->may_check_for_error = error_check_interval == 0; conn->block_handler = NULL; conn->block_baton = NULL; - conn->capabilities = apr_hash_make(pool); + conn->capabilities = apr_hash_make(result_pool); conn->compression_level = compression_level; conn->zero_copy_limit = zero_copy_limit; - conn->pool = pool; + conn->pool = result_pool; if (sock != NULL) { apr_sockaddr_t *sa; - conn->stream = svn_ra_svn__stream_from_sock(sock, pool); + conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool); if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS)) conn->remote_ip = NULL; @@ -108,34 +124,14 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, } else { - conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool); + conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream, + result_pool); conn->remote_ip = NULL; } return conn; } -svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - int compression_level, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - compression_level, 0, 0, pool); -} - -/* backward-compatible implementation using the default compression level */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, - pool); -} - svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, const apr_array_header_t *list) { @@ -155,6 +151,12 @@ svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } +apr_pool_t * +svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) +{ + return conn->pool; +} + svn_error_t * svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn, svn_delta_shim_callbacks_t *shim_callbacks) @@ -196,10 +198,10 @@ svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn)); } -svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, - apr_pool_t *pool) +svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, + svn_boolean_t *data_available) { - return svn_ra_svn__stream_pending(conn->stream); + return svn_ra_svn__stream_data_available(conn->stream, data_available); } /* --- WRITE BUFFER MANAGEMENT --- */ @@ -285,20 +287,13 @@ static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t * -writebuf_write_short_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - const char *data, apr_size_t len) -{ - apr_size_t left = sizeof(conn->write_buf) - conn->write_pos; - if (len <= left) - { - memcpy(conn->write_buf + conn->write_pos, data, len); - conn->write_pos += len; - return SVN_NO_ERROR; - } - else - return writebuf_write(conn, pool, data, len); -} +/* Write STRING_LITERAL, which is a string literal argument. + + Note: The purpose of the empty string "" in the macro definition is to + assert that STRING_LITERAL is in fact a string literal. Otherwise, the + string concatenation attempt should produce a compile-time error. */ +#define writebuf_write_literal(conn, pool, string_literal) \ + writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1) static APR_INLINE svn_error_t * writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data) @@ -389,7 +384,9 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) apr_size_t len; SVN_ERR_ASSERT(conn->read_ptr == conn->read_end); - SVN_ERR(writebuf_flush(conn, pool)); + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + len = sizeof(conn->read_buf); SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool)); conn->read_ptr = conn->read_buf; @@ -397,7 +394,11 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) return SVN_NO_ERROR; } -static APR_INLINE svn_error_t * +/* This is a hot function calling a cold function. GCC and others tend to + * inline the cold sub-function instead of this hot one. Therefore, be + * very insistent on lining this one. It is not a correctness issue, though. + */ +static SVN__FORCE_INLINE svn_error_t * readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result) { if (conn->read_ptr == conn->read_end) @@ -511,21 +512,32 @@ svn_ra_svn__write_number(svn_ra_svn_conn_t *conn, return write_number(conn, pool, number, ' '); } -svn_error_t * -svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const svn_string_t *str) +static svn_error_t * +svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *s, + apr_size_t len) { - if (str->len < 10) + if (len < 10) { - SVN_ERR(writebuf_writechar(conn, pool, (char)(str->len + '0'))); + SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); SVN_ERR(writebuf_writechar(conn, pool, ':')); } else - SVN_ERR(write_number(conn, pool, str->len, ':')); + SVN_ERR(write_number(conn, pool, len, ':')); - SVN_ERR(writebuf_write(conn, pool, str->data, str->len)); + SVN_ERR(writebuf_write(conn, pool, s, len)); SVN_ERR(writebuf_writechar(conn, pool, ' ')); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_string_t *str) +{ + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len)); return SVN_NO_ERROR; } @@ -534,19 +546,7 @@ svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *s) { - apr_size_t len = strlen(s); - - if (len < 10) - { - SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); - SVN_ERR(writebuf_writechar(conn, pool, ':')); - } - else - SVN_ERR(write_number(conn, pool, len, ':')); - - SVN_ERR(writebuf_write(conn, pool, s, len)); - SVN_ERR(writebuf_writechar(conn, pool, ' ')); - + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s))); return SVN_NO_ERROR; } @@ -555,38 +555,52 @@ svn_ra_svn__write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *word) { - SVN_ERR(writebuf_write_short_string(conn, pool, word, strlen(word))); + SVN_ERR(writebuf_write(conn, pool, word, strlen(word))); SVN_ERR(writebuf_writechar(conn, pool, ' ')); return SVN_NO_ERROR; } +svn_error_t * +svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_boolean_t value) +{ + if (value) + SVN_ERR(writebuf_write_literal(conn, pool, "true ")); + else + SVN_ERR(writebuf_write_literal(conn, pool, "false ")); + + return SVN_NO_ERROR; +} + svn_error_t * svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_t *props) { - apr_pool_t *iterpool; apr_hash_index_t *hi; - const void *key; - void *val; const char *propname; svn_string_t *propval; + apr_size_t len; + /* One might use an iterpool here but that would only be used when the + send buffer gets flushed and only by the CONN's progress callback. + That should happen at most once for typical prop lists and even then + use only a few bytes at best. + */ if (props) - { - iterpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) - { - svn_pool_clear(iterpool); - apr_hash_this(hi, &key, NULL, &val); - propname = key; - propval = val; - SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", - propname, propval)); - } - svn_pool_destroy(iterpool); - } + for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) + { + apr_hash_this(hi, (const void **)&propname, + (apr_ssize_t *)&len, + (void **)&propval); + + SVN_ERR(svn_ra_svn__start_list(conn, pool)); + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len)); + SVN_ERR(svn_ra_svn__write_string(conn, pool, propval)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); + } return SVN_NO_ERROR; } @@ -704,8 +718,7 @@ vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) static svn_error_t * vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) { - const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t)); } static svn_error_t * @@ -780,8 +793,7 @@ write_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t value) { - const char *cstr = value ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, value); } static svn_error_t * @@ -929,10 +941,10 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn, static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_ra_svn_item_t *item, apr_uint64_t len64) { - svn_stringbuf_t *stringbuf; apr_size_t len = (apr_size_t)len64; apr_size_t readbuf_len; char *dest; + apr_size_t buflen; /* We can't store strings longer than the maximum size of apr_size_t, * so check for wrapping */ @@ -940,58 +952,61 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("String length larger than maximum")); - /* Read the string in chunks. The chunk size is large enough to avoid - * re-allocation in typical cases, and small enough to ensure we do not - * pre-allocate an unreasonable amount of memory if (perhaps due to - * network data corruption or a DOS attack), we receive a bogus claim that - * a very long string is going to follow. In that case, we start small - * and wait for all that data to actually show up. This does not fully - * prevent DOS attacks but makes them harder (you have to actually send - * gigabytes of data). */ - readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool); - dest = stringbuf->data; - - /* Read remaining string data directly into the string structure. - * Do it iteratively, if necessary. */ - while (readbuf_len) + buflen = conn->read_end - conn->read_ptr; + /* Shorter strings can be copied directly from the read buffer. */ + if (len <= buflen) { - SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - - stringbuf->len += readbuf_len; - len -= readbuf_len; + item->kind = SVN_RA_SVN_STRING; + item->u.string = svn_string_ncreate(conn->read_ptr, len, pool); + conn->read_ptr += len; + } + else + { + /* Read the string in chunks. The chunk size is large enough to avoid + * re-allocation in typical cases, and small enough to ensure we do + * not pre-allocate an unreasonable amount of memory if (perhaps due + * to network data corruption or a DOS attack), we receive a bogus + * claim that a very long string is going to follow. In that case, we + * start small and wait for all that data to actually show up. This + * does not fully prevent DOS attacks but makes them harder (you have + * to actually send gigabytes of data). */ + svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool); + + /* Read string data directly into the string structure. + * Do it iteratively. */ + do + { + /* Determine length of chunk to read and re-alloc the buffer. */ + readbuf_len + = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD + ? len + : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - /* Early exit. In most cases, strings can be read in the first - * iteration. */ - if (len == 0) - break; + svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); + dest = stringbuf->data + stringbuf->len; - /* Prepare next iteration: determine length of chunk to read - * and re-alloc the string buffer. */ - readbuf_len - = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; + /* read data & update length info */ + SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); - dest = stringbuf->data + stringbuf->len; - } + stringbuf->len += readbuf_len; + len -= readbuf_len; + } + while (len); - /* zero-terminate the string */ - stringbuf->data[stringbuf->len] = '\0'; + /* zero-terminate the string */ + stringbuf->data[stringbuf->len] = '\0'; - /* Return the string properly wrapped into an RA_SVN item. */ - item->kind = SVN_RA_SVN_STRING; - item->u.string = svn_stringbuf__morph_into_string(stringbuf); + /* Return the string properly wrapped into an RA_SVN item. */ + item->kind = SVN_RA_SVN_STRING; + item->u.string = svn_stringbuf__morph_into_string(stringbuf); + } return SVN_NO_ERROR; } /* Given the first non-whitespace character FIRST_CHAR, read an item * into the already allocated structure ITEM. LEVEL should be set - * to 0 for the first call and is used to enforce a recurssion limit + * to 0 for the first call and is used to enforce a recursion limit * on the parser. */ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_ra_svn_item_t *item, char first_char, @@ -999,12 +1014,11 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { char c = first_char; apr_uint64_t val; - svn_stringbuf_t *str; svn_ra_svn_item_t *listitem; - if (++level >= 64) + if (++level >= ITEM_NESTING_LIMIT) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - _("Too many nested items")); + _("Items are nested too deeply")); /* Determine the item type and read it in. Make sure that c is the @@ -1022,7 +1036,8 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, break; val = val * 10 + (c - '0'); /* val wrapped past maximum value? */ - if (prev_val >= (APR_UINT64_MAX / 10) && (val / 10) != prev_val) + if ((prev_val >= (APR_UINT64_MAX / 10)) + && (val < APR_UINT64_MAX - 10)) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Number is larger than maximum")); } @@ -1041,18 +1056,28 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else if (svn_ctype_isalpha(c)) { - /* It's a word. */ - str = svn_stringbuf_create_ensure(16, pool); - svn_stringbuf_appendbyte(str, c); + /* It's a word. Read it into a buffer of limited size. */ + char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1); + char *end = buffer + MAX_WORD_LENGTH; + char *p = buffer + 1; + + buffer[0] = c; while (1) { - SVN_ERR(readbuf_getchar(conn, pool, &c)); - if (!svn_ctype_isalnum(c) && c != '-') + SVN_ERR(readbuf_getchar(conn, pool, p)); + if (!svn_ctype_isalnum(*p) && *p != '-') break; - svn_stringbuf_appendbyte(str, c); + + if (++p == end) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Word is too long")); } + + c = *p; + *p = '\0'; + item->kind = SVN_RA_SVN_WORD; - item->u.word = str->data; + item->u.word = buffer; } else if (c == '(') { @@ -1179,6 +1204,36 @@ svn_ra_svn__read_item(svn_ra_svn_conn_t *conn, return read_item(conn, pool, *item, c, 0); } +/* Drain existing whitespace from the receive buffer of CONN until either + there is no data in the underlying receive socket anymore or we found + a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case. + */ +static svn_error_t * +svn_ra_svn__has_item(svn_boolean_t *has_item, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + do + { + if (conn->read_ptr == conn->read_end) + { + svn_boolean_t available; + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + + SVN_ERR(svn_ra_svn__data_available(conn, &available)); + if (!available) + break; + + SVN_ERR(readbuf_fill(conn, pool)); + } + } + while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr); + + *has_item = conn->read_ptr != conn->read_end; + return SVN_NO_ERROR; +} + svn_error_t * svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, apr_pool_t *pool) @@ -1202,14 +1257,15 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po if (**fmt == '?') (*fmt)++; elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t); - if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, apr_uint64_t *) = elt->u.number; - else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; - else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) - *va_arg(*ap, svn_string_t **) = elt->u.string; + if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + { + (*fmt)++; + SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + } else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING) *va_arg(*ap, const char **) = elt->u.string->data; + else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) + *va_arg(*ap, svn_string_t **) = elt->u.string; else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD) *va_arg(*ap, const char **) = elt->u.word; else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD) @@ -1221,6 +1277,10 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } + else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) + *va_arg(*ap, apr_uint64_t *) = elt->u.number; + else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) + *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD) { if (strcmp(elt->u.word, "true") == 0) @@ -1230,13 +1290,17 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } - else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) - *va_arg(*ap, apr_array_header_t **) = elt->u.list; - else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD) { - (*fmt)++; - SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + if (strcmp(elt->u.word, "true") == 0) + *va_arg(*ap, svn_tristate_t *) = svn_tristate_true; + else if (strcmp(elt->u.word, "false") == 0) + *va_arg(*ap, svn_tristate_t *) = svn_tristate_false; + else + break; } + else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) + *va_arg(*ap, apr_array_header_t **) = elt->u.list; else if (**fmt == ')') return SVN_NO_ERROR; else @@ -1268,6 +1332,9 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po case 'n': *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER; break; + case '3': + *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown; + break; case '(': nesting_level++; break; @@ -1337,21 +1404,21 @@ svn_ra_svn__parse_proplist(const apr_array_header_t *list, apr_pool_t *pool, apr_hash_t **props) { - char *name; + svn_string_t *name; svn_string_t *value; svn_ra_svn_item_t *elt; int i; - *props = apr_hash_make(pool); + *props = svn_hash__make(pool); for (i = 0; i < list->nelts; i++) { elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); if (elt->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Proplist element not a list")); - SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cs", + SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss", &name, &value)); - svn_hash_sets(*props, name, value); + apr_hash_set(*props, name->data, name->len, value); } return SVN_NO_ERROR; @@ -1447,7 +1514,7 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, } else if (strcmp(status, "failure") == 0) { - return svn_ra_svn__handle_failure_status(params, pool); + return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool)); } return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -1455,6 +1522,76 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, status); } +svn_error_t * +svn_ra_svn__has_command(svn_boolean_t *has_command, + svn_boolean_t *terminated, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool); + if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + *terminated = TRUE; + svn_error_clear(err); + return SVN_NO_ERROR; + } + + *terminated = FALSE; + return svn_error_trace(err); +} + +svn_error_t * +svn_ra_svn__handle_command(svn_boolean_t *terminate, + apr_hash_t *cmd_hash, + void *baton, + svn_ra_svn_conn_t *conn, + svn_boolean_t error_on_disconnect, + apr_pool_t *pool) +{ + const char *cmdname; + svn_error_t *err, *write_err; + apr_array_header_t *params; + const svn_ra_svn_cmd_entry_t *command; + + *terminate = FALSE; + err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms); + if (err) + { + if (!error_on_disconnect + && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + svn_error_clear(err); + *terminate = TRUE; + return SVN_NO_ERROR; + } + return err; + } + + command = svn_hash_gets(cmd_hash, cmdname); + if (command) + { + err = (*command->handler)(conn, pool, params, baton); + *terminate = command->terminate; + } + else + { + err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, + _("Unknown editor command '%s'"), cmdname); + err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); + } + + if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + { + write_err = svn_ra_svn__write_cmd_failure( + conn, pool, + svn_ra_svn__locate_real_error_child(err)); + svn_error_clear(err); + return write_err ? write_err : SVN_NO_ERROR; + } + + return err; +} + svn_error_t * svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -1464,10 +1601,7 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, { apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *iterpool = svn_pool_create(subpool); - const char *cmdname; const svn_ra_svn_cmd_entry_t *command; - svn_error_t *err, *write_err; - apr_array_header_t *params; apr_hash_t *cmd_hash = apr_hash_make(subpool); for (command = commands; command->cmdname; command++) @@ -1475,43 +1609,18 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, while (1) { + svn_boolean_t terminate; + svn_error_t *err; svn_pool_clear(iterpool); - err = svn_ra_svn__read_tuple(conn, iterpool, "wl", &cmdname, ¶ms); - if (err) - { - if (!error_on_disconnect - && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) - { - svn_error_clear(err); - svn_pool_destroy(subpool); - return SVN_NO_ERROR; - } - return err; - } - command = svn_hash_gets(cmd_hash, cmdname); - if (command) - err = (*command->handler)(conn, iterpool, params, baton); - else - { - err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, - _("Unknown editor command '%s'"), cmdname); - err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); - } - - if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn, + error_on_disconnect, iterpool); + if (err) { - write_err = svn_ra_svn__write_cmd_failure( - conn, iterpool, - svn_ra_svn__locate_real_error_child(err)); - svn_error_clear(err); - if (write_err) - return write_err; + svn_pool_destroy(subpool); + return svn_error_trace(err); } - else if (err) - return err; - - if (command && command->terminate) + if (terminate) break; } svn_pool_destroy(iterpool); @@ -1524,9 +1633,9 @@ svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( target-rev ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1537,12 +1646,12 @@ svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-root ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1554,13 +1663,13 @@ svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-entry ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1574,10 +1683,10 @@ svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-dir ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1590,9 +1699,9 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-dir ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1604,9 +1713,9 @@ svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-dir-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1616,9 +1725,9 @@ svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-dir ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1629,9 +1738,9 @@ svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-dir ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1645,10 +1754,10 @@ svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1661,9 +1770,9 @@ svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-file ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1675,9 +1784,9 @@ svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-file-prop ( ", 21)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1688,12 +1797,12 @@ svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn, const char *token, const char *text_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-file ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1704,9 +1813,9 @@ svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-file ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1717,10 +1826,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, const char *token, const svn_string_t *chunk) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-chunk ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_string(conn, pool, chunk)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1730,9 +1839,9 @@ svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-end ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1743,12 +1852,12 @@ svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, const char *token, const char *base_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( apply-textdelta ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1757,14 +1866,14 @@ svn_error_t * svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( close-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( close-edit ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) "); } svn_error_t * @@ -1776,7 +1885,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( set-path ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); @@ -1784,7 +1893,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1794,9 +1903,9 @@ svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-path ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1811,7 +1920,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( link-path ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_cstring(conn, pool, url)); SVN_ERR(write_tuple_revision(conn, pool, rev)); @@ -1820,7 +1929,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1829,14 +1938,14 @@ svn_error_t * svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-report ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-report ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-report ( ) ) ", 21); + return writebuf_write_literal(conn, pool, "( abort-report ( ) ) "); } svn_error_t * @@ -1844,9 +1953,9 @@ svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *url) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( reparent ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( ")); SVN_ERR(write_tuple_cstring(conn, pool, url)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1855,7 +1964,7 @@ svn_error_t * svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( get-latest-rev ( ) ) ", 23); + return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) "); } svn_error_t * @@ -1863,9 +1972,9 @@ svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_time_t tm) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-dated-rev ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool))); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1879,7 +1988,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, svn_boolean_t dont_care, const svn_string_t *old_value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop2 ( ", 21)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); SVN_ERR(write_tuple_start_list(conn, pool)); @@ -1889,7 +1998,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_boolean(conn, pool, dont_care)); SVN_ERR(write_tuple_string_opt(conn, pool, old_value)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1901,11 +2010,11 @@ svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); SVN_ERR(write_tuple_string_opt(conn, pool, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1915,9 +2024,9 @@ svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-proplist ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1928,10 +2037,10 @@ svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *name) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-prop ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1944,14 +2053,18 @@ svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn, svn_boolean_t props, svn_boolean_t stream) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, props)); SVN_ERR(write_tuple_boolean(conn, pool, stream)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + + /* Always send the, nominally optional, want-iprops as "false" to + workaround a bug in svnserve 1.8.0-1.8.8 that causes the server + to see "true" if it is omitted. */ + SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) ")); return SVN_NO_ERROR; } @@ -1966,7 +2079,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( update ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( update ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -1975,7 +2088,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1991,7 +2104,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( switch ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -2001,7 +2114,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2014,14 +2127,14 @@ svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn, svn_revnum_t rev, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( status ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( status ( ")); SVN_ERR(write_tuple_cstring(conn, pool, target)); SVN_ERR(write_tuple_boolean(conn, pool, recurse)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2037,7 +2150,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, svn_boolean_t text_deltas, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( diff ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -2047,7 +2160,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring(conn, pool, versus_url)); SVN_ERR(write_tuple_boolean(conn, pool, text_deltas)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2058,12 +2171,12 @@ svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( check-path ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2074,12 +2187,12 @@ svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( stat ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2092,7 +2205,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, svn_revnum_t end, svn_boolean_t include_merged_revisions) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file-revs ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, start)); @@ -2101,7 +2214,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_revision_opt(conn, pool, end)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2114,7 +2227,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, svn_boolean_t steal_lock, svn_revnum_t revnum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( lock ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, comment)); @@ -2123,7 +2236,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revnum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2135,13 +2248,13 @@ svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn, const char *token, svn_boolean_t break_lock) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( unlock ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, break_lock)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2151,9 +2264,9 @@ svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-lock ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2164,12 +2277,12 @@ svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn, const char *path, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-locks ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2181,11 +2294,11 @@ svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2198,12 +2311,12 @@ svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay-range ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( ")); SVN_ERR(write_tuple_revision(conn, pool, start_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2215,11 +2328,11 @@ svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, svn_revnum_t peg_revision, svn_revnum_t end_revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-deleted-rev ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, peg_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2230,12 +2343,12 @@ svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-iprops ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revision)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2244,7 +2357,7 @@ svn_error_t * svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-replay ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) "); } svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, @@ -2254,7 +2367,7 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, va_list ap; svn_error_t *err; - SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10)); + SVN_ERR(writebuf_write_literal(conn, pool, "( success ")); va_start(ap, fmt); err = vwrite_tuple(conn, pool, fmt, &ap); va_end(ap); @@ -2262,10 +2375,11 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, } svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, svn_error_t *err) + apr_pool_t *pool, + const svn_error_t *err) { char buffer[128]; - SVN_ERR(writebuf_write_short_string(conn, pool, "( failure ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( ")); for (; err; err = err->child) { const char *msg; @@ -2285,5 +2399,217 @@ svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, err->file ? err->file : "", (apr_uint64_t) err->line)); } - return writebuf_write_short_string(conn, pool, ") ) ", 4); + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *path, + char action, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev, + svn_node_kind_t node_kind, + svn_boolean_t text_modified, + svn_boolean_t props_modified) +{ + SVN_ERR(write_tuple_start_list(conn, pool)); + + SVN_ERR(write_tuple_cstring(conn, pool, path)); + SVN_ERR(writebuf_writechar(conn, pool, action)); + SVN_ERR(writebuf_writechar(conn, pool, ' ')); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path)); + SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind))); + SVN_ERR(write_tuple_boolean(conn, pool, text_modified)); + SVN_ERR(write_tuple_boolean(conn, pool, props_modified)); + + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_revnum_t revision, + const svn_string_t *author, + const svn_string_t *date, + const svn_string_t *message, + svn_boolean_t has_children, + svn_boolean_t invalid_revnum, + unsigned revprop_count) +{ + SVN_ERR(write_tuple_revision(conn, pool, revision)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, author)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, date)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, message)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_boolean(conn, pool, has_children)); + SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum)); + SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count)); + + return SVN_NO_ERROR; +} + +/* If condition COND is not met, return a "malformed network data" error. + */ +#define CHECK_PROTOCOL_COND(cond)\ + if (!(cond)) \ + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \ + _("Malformed network data")); + +/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_string(const apr_array_header_t *items, + int idx, + svn_string_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the C-style string at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_cstring(const apr_array_header_t *items, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string->data; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the word at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_word(const apr_array_header_t *items, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + *result = elt->u.word; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the revision at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_revision(const apr_array_header_t *items, + int idx, + svn_revnum_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER); + *result = (svn_revnum_t)elt->u.number; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the boolean at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_boolean(const apr_array_header_t *items, + int idx, + apr_uint64_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0) + *result = TRUE; + else if (strcmp(elt->u.word, "false") == 0) + *result = FALSE; + else + CHECK_PROTOCOL_COND(FALSE); + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the tuple at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_list(const apr_array_header_t *items, + int idx, + const apr_array_header_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST); + + *result = elt->u.list; + return SVN_NO_ERROR; +} + +/* Verify the tuple ITEMS contains at least MIN and at most MAX elements. + */ +static svn_error_t * +svn_ra_svn__read_check_array_size(const apr_array_header_t *items, + int min, + int max) +{ + CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, + svn_string_t **cpath, + const char **action, + const char **copy_path, + svn_revnum_t *copy_rev, + const char **kind_str, + apr_uint64_t *text_mods, + apr_uint64_t *prop_mods) +{ + const apr_array_header_t *sub_items; + + /* initialize optional values */ + *copy_path = NULL; + *copy_rev = SVN_INVALID_REVNUM; + *kind_str = NULL; + *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + + /* top-level elements (mandatory) */ + SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX)); + SVN_ERR(svn_ra_svn__read_string(items, 0, cpath)); + SVN_ERR(svn_ra_svn__read_word(items, 1, action)); + + /* first sub-structure (mandatory) */ + SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items)); + if (sub_items->nelts) + { + SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2)); + SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path)); + SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev)); + } + + /* second sub-structure (optional) */ + if (items->nelts >= 4) + { + SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items)); + switch (MIN(3, sub_items->nelts)) + { + case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods)); + case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods)); + case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str)); + default: break; + } + } + + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_ra_svn/protocol b/contrib/subversion/subversion/libsvn_ra_svn/protocol index 4d98b2bb1..dfc1f3dc1 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/protocol +++ b/contrib/subversion/subversion/libsvn_ra_svn/protocol @@ -303,18 +303,20 @@ second place for auth-request point as noted below. get-file params: ( path:string [ rev:number ] want-props:bool want-contents:bool - [ want-iprops:bool ] ) + ? want-iprops:bool ) response: ( [ checksum:string ] rev:number props:proplist [ inherited-props:iproplist ] ) If want-contents is specified, then after sending response, server sends file contents as a series of strings, terminated by the empty string, followed by a second empty command response to indicate whether an error occurred during the sending of the file. - NOTE: the standard client never sends want-iprops, it uses get-iprops. + NOTE: the standard client doesn't send want-iprops as true, it uses + get-iprops, but does send want-iprops as false to workaround a server + bug in 1.8.0-1.8.8. get-dir params: ( path:string [ rev:number ] want-props:bool want-contents:bool - ? ( field:dirent-field ... ) [ want-iprops:bool ] ) + ? ( field:dirent-field ... ) ? want-iprops:bool ) response: ( rev:number props:proplist ( entry:dirent ... ) [ inherited-props:iproplist ] )] dirent: ( name:string kind:node-kind size:number has-props:bool @@ -322,7 +324,9 @@ second place for auth-request point as noted below. [ last-author:string ] ) dirent-field: kind | size | has-props | created-rev | time | last-author | word - NOTE: the standard client never sends want-iprops, it uses get-iprops. + NOTE: the standard client doesn't send want-iprops as true, it uses + get-iprops, but does send want-iprops as false to workaround a server + bug in 1.8.0-1.8.8. check-path params: ( path:string [ rev:number ] ) @@ -339,7 +343,7 @@ second place for auth-request point as noted below. get-mergeinfo params: ( ( path:string ... ) [ rev:number ] inherit:word - descendents:bool) + descendants:bool) response: ( ( ( path:string merge-info:string ) ... ) ) New in svn 1.5. If no paths are specified, an empty response is returned. If rev is not specified, the youngest revision is used. @@ -597,7 +601,13 @@ desirability: * The protocol version may be bumped. Clients and servers can then choose to any range of protocol versions. -4.1. Extending existing commands +4.1. Limitations + +The current implementation limits the length of a word to 31 characters. +Longer words, such as capability names, will be cause an error on the +receiver side. + +4.2. Extending existing commands Extending an existing command is normally done by indicating that its tuple is allowed to end where it currently ends, for backwards diff --git a/contrib/subversion/subversion/libsvn_ra_svn/ra_svn.h b/contrib/subversion/subversion/libsvn_ra_svn/ra_svn.h index dc70eb72f..d9fe1b275 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/ra_svn.h +++ b/contrib/subversion/subversion/libsvn_ra_svn/ra_svn.h @@ -123,13 +123,16 @@ struct svn_ra_svn__session_baton_t { apr_pool_t *pool; svn_ra_svn_conn_t *conn; svn_boolean_t is_tunneled; + svn_auth_baton_t *auth_baton; const char *url; const char *user; const char *hostname; /* The remote hostname. */ const char *realm_prefix; + const char *tunnel_name; const char **tunnel_argv; const svn_ra_callbacks2_t *callbacks; void *callbacks_baton; + apr_hash_t *config; apr_off_t bytes_read, bytes_written; /* apr_off_t's because that's what the callback interface uses */ const char *useragent; @@ -145,8 +148,8 @@ void svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, void *baton); /* Return true if there is input waiting on conn. */ -svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, - apr_pool_t *pool); +svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, + svn_boolean_t *data_available); /* CRAM-MD5 client implementation. */ svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -169,20 +172,20 @@ svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params, svn_ra_svn__stream_t *svn_ra_svn__stream_from_sock(apr_socket_t *sock, apr_pool_t *pool); -/* Returns a stream that reads from IN_FILE and writes to OUT_FILE. */ -svn_ra_svn__stream_t *svn_ra_svn__stream_from_files(apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool); +/* Returns a stream that reads from IN_STREAM and writes to OUT_STREAM, + creating a timeout callback for OUT_STREAM if possible */ +svn_ra_svn__stream_t *svn_ra_svn__stream_from_streams(svn_stream_t *in_stream, + svn_stream_t *out_stream, + apr_pool_t *pool); /* Create an svn_ra_svn__stream_t using READ_CB, WRITE_CB, TIMEOUT_CB, * PENDING_CB, and BATON. */ -svn_ra_svn__stream_t *svn_ra_svn__stream_create(void *baton, - svn_read_fn_t read_cb, - svn_write_fn_t write_cb, +svn_ra_svn__stream_t *svn_ra_svn__stream_create(svn_stream_t *in_stream, + svn_stream_t *out_stream, + void *timeout_baton, ra_svn_timeout_fn_t timeout_cb, - ra_svn_pending_fn_t pending_cb, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Write *LEN bytes from DATA to STREAM, returning the number of bytes * written in *LEN. @@ -208,7 +211,9 @@ void svn_ra_svn__stream_timeout(svn_ra_svn__stream_t *stream, apr_interval_time_t interval); /* Return whether or not there is data pending on STREAM. */ -svn_boolean_t svn_ra_svn__stream_pending(svn_ra_svn__stream_t *stream); +svn_error_t * +svn_ra_svn__stream_data_available(svn_ra_svn__stream_t *stream, + svn_boolean_t *data_available); /* Respond to an auth request and perform authentication. Use the Cyrus * SASL library for mechanism negotiation and for creating authentication diff --git a/contrib/subversion/subversion/libsvn_ra_svn/streams.c b/contrib/subversion/subversion/libsvn_ra_svn/streams.c index 4ae93d575..3ad792bcd 100644 --- a/contrib/subversion/subversion/libsvn_ra_svn/streams.c +++ b/contrib/subversion/subversion/libsvn_ra_svn/streams.c @@ -33,12 +33,14 @@ #include "svn_io.h" #include "svn_private_config.h" +#include "private/svn_io_private.h" + #include "ra_svn.h" struct svn_ra_svn__stream_st { - svn_stream_t *stream; - void *baton; - ra_svn_pending_fn_t pending_fn; + svn_stream_t *in_stream; + svn_stream_t *out_stream; + void *timeout_baton; ra_svn_timeout_fn_t timeout_fn; }; @@ -47,11 +49,6 @@ typedef struct sock_baton_t { apr_pool_t *pool; } sock_baton_t; -typedef struct file_baton_t { - apr_file_t *in_file; - apr_file_t *out_file; - apr_pool_t *pool; -} file_baton_t; /* Returns TRUE if PFD has pending data, FALSE otherwise. */ static svn_boolean_t pending(apr_pollfd_t *pfd, apr_pool_t *pool) @@ -67,65 +64,34 @@ static svn_boolean_t pending(apr_pollfd_t *pfd, apr_pool_t *pool) /* Functions to implement a file backed svn_ra_svn__stream_t. */ -/* Implements svn_read_fn_t */ -static svn_error_t * -file_read_cb(void *baton, char *buffer, apr_size_t *len) -{ - file_baton_t *b = baton; - apr_status_t status = apr_file_read(b->in_file, buffer, len); - - if (status && !APR_STATUS_IS_EOF(status)) - return svn_error_wrap_apr(status, _("Can't read from connection")); - if (*len == 0) - return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); - return SVN_NO_ERROR; -} - -/* Implements svn_write_fn_t */ -static svn_error_t * -file_write_cb(void *baton, const char *buffer, apr_size_t *len) -{ - file_baton_t *b = baton; - apr_status_t status = apr_file_write(b->out_file, buffer, len); - if (status) - return svn_error_wrap_apr(status, _("Can't write to connection")); - return SVN_NO_ERROR; -} - /* Implements ra_svn_timeout_fn_t */ static void file_timeout_cb(void *baton, apr_interval_time_t interval) { - file_baton_t *b = baton; - apr_file_pipe_timeout_set(b->out_file, interval); -} - -/* Implements ra_svn_pending_fn_t */ -static svn_boolean_t -file_pending_cb(void *baton) -{ - file_baton_t *b = baton; - apr_pollfd_t pfd; - - pfd.desc_type = APR_POLL_FILE; - pfd.desc.f = b->in_file; + apr_file_t *f = baton; - return pending(&pfd, b->pool); + if (f) + apr_file_pipe_timeout_set(f, interval); } svn_ra_svn__stream_t * -svn_ra_svn__stream_from_files(apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool) +svn_ra_svn__stream_from_streams(svn_stream_t *in_stream, + svn_stream_t *out_stream, + apr_pool_t *pool) { - file_baton_t *b = apr_palloc(pool, sizeof(*b)); + apr_file_t *file; + + /* If out_stream is backed by an apr_file (e.g. an PIPE) we + provide a working callback, otherwise the callback ignores + the timeout. - b->in_file = in_file; - b->out_file = out_file; - b->pool = pool; + The callback is used to make the write non-blocking on + some error scenarios. ### This (legacy) usage + breaks the stream promise */ + file = svn_stream__aprfile(out_stream); - return svn_ra_svn__stream_create(b, file_read_cb, file_write_cb, - file_timeout_cb, file_pending_cb, + return svn_ra_svn__stream_create(in_stream, out_stream, + file, file_timeout_cb, pool); } @@ -155,8 +121,6 @@ sock_read_cb(void *baton, char *buffer, apr_size_t *len) if (status && !APR_STATUS_IS_EOF(status)) return svn_error_wrap_apr(status, _("Can't read from connection")); - if (*len == 0) - return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); return SVN_NO_ERROR; } @@ -179,9 +143,10 @@ sock_timeout_cb(void *baton, apr_interval_time_t interval) apr_socket_timeout_set(b->sock, interval); } -/* Implements ra_svn_pending_fn_t */ -static svn_boolean_t -sock_pending_cb(void *baton) +/* Implements svn_stream_data_available_fn_t */ +static svn_error_t * +sock_pending_cb(void *baton, + svn_boolean_t *data_available) { sock_baton_t *b = baton; apr_pollfd_t pfd; @@ -189,41 +154,45 @@ sock_pending_cb(void *baton) pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = b->sock; - return pending(&pfd, b->pool); + *data_available = pending(&pfd, b->pool); + + svn_pool_clear(b->pool); + + return SVN_NO_ERROR; } svn_ra_svn__stream_t * svn_ra_svn__stream_from_sock(apr_socket_t *sock, - apr_pool_t *pool) + apr_pool_t *result_pool) { - sock_baton_t *b = apr_palloc(pool, sizeof(*b)); + sock_baton_t *b = apr_palloc(result_pool, sizeof(*b)); + svn_stream_t *sock_stream; b->sock = sock; - b->pool = pool; + b->pool = svn_pool_create(result_pool); - return svn_ra_svn__stream_create(b, sock_read_cb, sock_write_cb, - sock_timeout_cb, sock_pending_cb, - pool); + sock_stream = svn_stream_create(b, result_pool); + + svn_stream_set_read2(sock_stream, sock_read_cb, NULL /* use default */); + svn_stream_set_write(sock_stream, sock_write_cb); + svn_stream_set_data_available(sock_stream, sock_pending_cb); + + return svn_ra_svn__stream_create(sock_stream, sock_stream, + b, sock_timeout_cb, result_pool); } svn_ra_svn__stream_t * -svn_ra_svn__stream_create(void *baton, - svn_read_fn_t read_cb, - svn_write_fn_t write_cb, +svn_ra_svn__stream_create(svn_stream_t *in_stream, + svn_stream_t *out_stream, + void *timeout_baton, ra_svn_timeout_fn_t timeout_cb, - ra_svn_pending_fn_t pending_cb, apr_pool_t *pool) { svn_ra_svn__stream_t *s = apr_palloc(pool, sizeof(*s)); - s->stream = svn_stream_empty(pool); - svn_stream_set_baton(s->stream, baton); - if (read_cb) - svn_stream_set_read(s->stream, read_cb); - if (write_cb) - svn_stream_set_write(s->stream, write_cb); - s->baton = baton; + s->in_stream = in_stream; + s->out_stream = out_stream; + s->timeout_baton = timeout_baton; s->timeout_fn = timeout_cb; - s->pending_fn = pending_cb; return s; } @@ -231,25 +200,33 @@ svn_error_t * svn_ra_svn__stream_write(svn_ra_svn__stream_t *stream, const char *data, apr_size_t *len) { - return svn_stream_write(stream->stream, data, len); + return svn_error_trace(svn_stream_write(stream->out_stream, data, len)); } svn_error_t * svn_ra_svn__stream_read(svn_ra_svn__stream_t *stream, char *data, apr_size_t *len) { - return svn_stream_read(stream->stream, data, len); + SVN_ERR(svn_stream_read2(stream->in_stream, data, len)); + + if (*len == 0) + return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); + + return SVN_NO_ERROR; } void svn_ra_svn__stream_timeout(svn_ra_svn__stream_t *stream, apr_interval_time_t interval) { - stream->timeout_fn(stream->baton, interval); + stream->timeout_fn(stream->timeout_baton, interval); } -svn_boolean_t -svn_ra_svn__stream_pending(svn_ra_svn__stream_t *stream) +svn_error_t * +svn_ra_svn__stream_data_available(svn_ra_svn__stream_t *stream, + svn_boolean_t *data_available) { - return stream->pending_fn(stream->baton); + return svn_error_trace( + svn_stream_data_available(stream->in_stream, + data_available)); } diff --git a/contrib/subversion/subversion/libsvn_repos/authz.c b/contrib/subversion/subversion/libsvn_repos/authz.c index af4a1f255..20f9231dc 100644 --- a/contrib/subversion/subversion/libsvn_repos/authz.c +++ b/contrib/subversion/subversion/libsvn_repos/authz.c @@ -35,6 +35,7 @@ #include "svn_config.h" #include "svn_ctype.h" #include "private/svn_fspath.h" +#include "private/svn_repos_private.h" #include "repos.h" @@ -76,8 +77,8 @@ struct authz_validate_baton { enumerator, if any. */ }; -/* Currently this structure is just a wrapper around a - svn_config_t. */ +/* Currently this structure is just a wrapper around a svn_config_t. + Please update authz_pool if you modify this structure. */ struct svn_authz_t { svn_config_t *cfg; @@ -351,7 +352,7 @@ authz_get_path_access(svn_config_t *cfg, const char *repos_name, baton.user = user; /* Try to locate a repository-specific block first. */ - qualified_path = apr_pstrcat(pool, repos_name, ":", path, (char *)NULL); + qualified_path = apr_pstrcat(pool, repos_name, ":", path, SVN_VA_NULL); svn_config_enumerate2(cfg, qualified_path, authz_parse_line, &baton, pool); @@ -394,7 +395,7 @@ authz_get_tree_access(svn_config_t *cfg, const char *repos_name, baton.required_access = required_access; baton.repos_path = path; baton.qualified_repos_path = apr_pstrcat(pool, repos_name, - ":", path, (char *)NULL); + ":", path, SVN_VA_NULL); /* Default to access granted if no rules say otherwise. */ baton.access = TRUE; @@ -453,7 +454,7 @@ authz_get_any_access(svn_config_t *cfg, const char *repos_name, baton.access = FALSE; /* Deny access by default. */ baton.repos_path = "/"; baton.qualified_repos_path = apr_pstrcat(pool, repos_name, - ":/", (char *)NULL); + ":/", SVN_VA_NULL); /* We could have used svn_config_enumerate2 for "repos_name:/". * However, this requires access for root explicitly (which the user @@ -748,9 +749,8 @@ static svn_boolean_t authz_validate_section(const char *name, } -/* Walk the configuration in AUTHZ looking for any errors. */ -static svn_error_t * -authz_validate(svn_authz_t *authz, apr_pool_t *pool) +svn_error_t * +svn_repos__authz_validate(svn_authz_t *authz, apr_pool_t *pool) { struct authz_validate_baton baton = { 0 }; @@ -771,13 +771,17 @@ authz_validate(svn_authz_t *authz, apr_pool_t *pool) * * If DIRENT cannot be parsed as a config file then an error is returned. The * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing - * authz file is also an error. + * authz file is also an error. The CASE_SENSITIVE controls the lookup + * behavior for section and option names alike. * * SCRATCH_POOL will be used for temporary allocations. */ static svn_error_t * -authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent, - svn_boolean_t must_exist, - apr_pool_t *result_pool, apr_pool_t *scratch_pool) +authz_retrieve_config_repo(svn_config_t **cfg_p, + const char *dirent, + svn_boolean_t must_exist, + svn_boolean_t case_sensitive, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err; svn_repos_t *repos; @@ -796,7 +800,8 @@ authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent, "Unable to find repository at '%s'", dirent); /* Attempt to open a repository at repos_root_dirent. */ - SVN_ERR(svn_repos_open2(&repos, repos_root_dirent, NULL, scratch_pool)); + SVN_ERR(svn_repos_open3(&repos, repos_root_dirent, NULL, scratch_pool, + scratch_pool)); fs_path = &dirent[strlen(repos_root_dirent)]; @@ -824,7 +829,8 @@ authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent, { if (!must_exist) { - SVN_ERR(svn_config_create2(cfg_p, TRUE, TRUE, result_pool)); + SVN_ERR(svn_config_create2(cfg_p, case_sensitive, case_sensitive, + result_pool)); return SVN_NO_ERROR; } else @@ -842,7 +848,8 @@ authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent, } SVN_ERR(svn_fs_file_contents(&contents, root, fs_path, scratch_pool)); - err = svn_config_parse(cfg_p, contents, TRUE, TRUE, result_pool); + err = svn_config_parse(cfg_p, contents, case_sensitive, case_sensitive, + result_pool); /* Add the URL to the error stack since the parser doesn't have it. */ if (err != SVN_NO_ERROR) @@ -853,23 +860,12 @@ authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent, return SVN_NO_ERROR; } -/* Given a PATH which might be a relative repo URL (^/), an absolute - * local repo URL (file://), an absolute path outside of the repo - * or a location in the Windows registry. - * - * Retrieve the configuration data that PATH points at and parse it into - * CFG_P allocated in POOL. - * - * If PATH cannot be parsed as a config file then an error is returned. The - * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing - * authz file is also an error. - * - * REPOS_ROOT points at the root of the repos you are - * going to apply the authz against, can be NULL if you are sure that you - * don't have a repos relative URL in PATH. */ -static svn_error_t * -authz_retrieve_config(svn_config_t **cfg_p, const char *path, - svn_boolean_t must_exist, apr_pool_t *pool) +svn_error_t * +svn_repos__retrieve_config(svn_config_t **cfg_p, + const char *path, + svn_boolean_t must_exist, + svn_boolean_t case_sensitive, + apr_pool_t *pool) { if (svn_path_is_url(path)) { @@ -880,8 +876,8 @@ authz_retrieve_config(svn_config_t **cfg_p, const char *path, err = svn_uri_get_dirent_from_file_url(&dirent, path, scratch_pool); if (err == SVN_NO_ERROR) - err = authz_retrieve_config_repo(cfg_p, dirent, must_exist, pool, - scratch_pool); + err = authz_retrieve_config_repo(cfg_p, dirent, must_exist, + case_sensitive, pool, scratch_pool); /* Close the repos and streams we opened. */ svn_pool_destroy(scratch_pool); @@ -891,7 +887,8 @@ authz_retrieve_config(svn_config_t **cfg_p, const char *path, else { /* Outside of repo file or Windows registry*/ - SVN_ERR(svn_config_read3(cfg_p, path, must_exist, TRUE, TRUE, pool)); + SVN_ERR(svn_config_read3(cfg_p, path, must_exist, case_sensitive, + case_sensitive, pool)); } return SVN_NO_ERROR; @@ -942,9 +939,11 @@ svn_repos__authz_read(svn_authz_t **authz_p, const char *path, /* Load the authz file */ if (accept_urls) - SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, pool)); + SVN_ERR(svn_repos__retrieve_config(&authz->cfg, path, must_exist, TRUE, + pool)); else - SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE, pool)); + SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE, + pool)); if (groups_path) { @@ -953,8 +952,8 @@ svn_repos__authz_read(svn_authz_t **authz_p, const char *path, /* Load the groups file */ if (accept_urls) - SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist, - pool)); + SVN_ERR(svn_repos__retrieve_config(&groups_cfg, groups_path, + must_exist, TRUE, pool)); else SVN_ERR(svn_config_read3(&groups_cfg, groups_path, must_exist, TRUE, TRUE, pool)); @@ -971,7 +970,7 @@ svn_repos__authz_read(svn_authz_t **authz_p, const char *path, } /* Make sure there are no errors in the configuration. */ - SVN_ERR(authz_validate(authz, pool)); + SVN_ERR(svn_repos__authz_validate(authz, pool)); *authz_p = authz; return SVN_NO_ERROR; @@ -1011,7 +1010,7 @@ svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream, } /* Make sure there are no errors in the configuration. */ - SVN_ERR(authz_validate(authz, pool)); + SVN_ERR(svn_repos__authz_validate(authz, pool)); *authz_p = authz; return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_repos/authz_pool.c b/contrib/subversion/subversion/libsvn_repos/authz_pool.c new file mode 100644 index 000000000..f8ac52835 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_repos/authz_pool.c @@ -0,0 +1,226 @@ +/* + * authz_pool.c : pool of authorization objects + * + * ==================================================================== + * 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_checksum.h" +#include "svn_config.h" +#include "svn_error.h" +#include "svn_pools.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_mutex.h" +#include "private/svn_object_pool.h" +#include "private/svn_subr_private.h" +#include "private/svn_repos_private.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" + +#include "repos.h" + +/* Currently this structure is just a wrapper around a svn_config_t. + */ +struct svn_authz_t +{ + svn_config_t *cfg; +}; + +/* The wrapper object structure that we store in the object pool. It + * combines the authz with the underlying config structures and their + * identifying keys. + */ +typedef struct authz_object_t +{ + /* key = concatenation of AUTHZ_KEY and GROUPS_KEY */ + svn_membuf_t *key; + + /* keys used to identify AUTHZ_CFG and GROUPS_CFG */ + svn_membuf_t *authz_key; + svn_membuf_t *groups_key; + + /* r/o references to configurations from the configuration pool. + GROUPS_CFG may be NULL. */ + svn_config_t *authz_cfg; + svn_config_t *groups_cfg; + + /* Case-sensitive config. */ + svn_authz_t *authz; +} authz_object_t; + +/* Root data structure simply adding the config_pool to the basic object pool. + */ +struct svn_repos__authz_pool_t +{ + /* authz_object_t object storage */ + svn_object_pool__t *object_pool; + + /* factory and storage of (shared) configuration objects */ + svn_repos__config_pool_t *config_pool; +}; + +/* Return a combination of AUTHZ_KEY and GROUPS_KEY, allocated in POOL. + * GROUPS_KEY may be NULL. + */ +static svn_membuf_t * +construct_key(svn_membuf_t *authz_key, + svn_membuf_t *groups_key, + apr_pool_t *pool) +{ + svn_membuf_t *result = apr_pcalloc(pool, sizeof(*result)); + apr_size_t size; + if (groups_key) + { + size = authz_key->size + groups_key->size; + svn_membuf__create(result,size, pool); + memcpy(result->data, authz_key->data, authz_key->size); + memcpy((char *)result->data + authz_key->size, + groups_key->data, groups_key->size); + } + else + { + size = authz_key->size; + svn_membuf__create(result, size, pool); + memcpy(result->data, authz_key->data, authz_key->size); + } + + result->size = size; + return result; +} + +/* Implement svn_object_pool__getter_t on authz_object_t structures. + */ +static void * +getter(void *object, + void *baton, + apr_pool_t *pool) +{ + return ((authz_object_t *)object)->authz; +} + +/* API implementation */ + +svn_error_t * +svn_repos__authz_pool_create(svn_repos__authz_pool_t **authz_pool, + svn_repos__config_pool_t *config_pool, + svn_boolean_t thread_safe, + apr_pool_t *pool) +{ + svn_repos__authz_pool_t *result; + svn_object_pool__t *object_pool; + + /* there is no setter as we don't need to update existing authz */ + SVN_ERR(svn_object_pool__create(&object_pool, getter, NULL, thread_safe, + pool)); + + result = apr_pcalloc(pool, sizeof(*result)); + result->object_pool = object_pool; + result->config_pool = config_pool; + + *authz_pool = result; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos__authz_pool_get(svn_authz_t **authz_p, + svn_repos__authz_pool_t *authz_pool, + const char *path, + const char *groups_path, + svn_boolean_t must_exist, + svn_repos_t *preferred_repos, + apr_pool_t *pool) +{ + apr_pool_t *authz_ref_pool + = svn_object_pool__new_wrapper_pool(authz_pool->object_pool); + authz_object_t *authz_ref + = apr_pcalloc(authz_ref_pool, sizeof(*authz_ref)); + svn_boolean_t have_all_keys; + + /* read the configurations */ + SVN_ERR(svn_repos__config_pool_get(&authz_ref->authz_cfg, + &authz_ref->authz_key, + authz_pool->config_pool, + path, must_exist, TRUE, + preferred_repos, authz_ref_pool)); + have_all_keys = authz_ref->authz_key != NULL; + + if (groups_path) + { + SVN_ERR(svn_repos__config_pool_get(&authz_ref->groups_cfg, + &authz_ref->groups_key, + authz_pool->config_pool, + groups_path, must_exist, TRUE, + preferred_repos, authz_ref_pool)); + have_all_keys &= authz_ref->groups_key != NULL; + } + + /* fall back to standard implementation in case we don't have all the + * facts (i.e. keys). */ + if (!have_all_keys) + return svn_error_trace(svn_repos_authz_read2(authz_p, path, groups_path, + must_exist, pool)); + + /* all keys are known and lookup is unambigious. */ + authz_ref->key = construct_key(authz_ref->authz_key, + authz_ref->groups_key, + authz_ref_pool); + + SVN_ERR(svn_object_pool__lookup((void **)authz_p, authz_pool->object_pool, + authz_ref->key, NULL, pool)); + if (*authz_p) + { + svn_pool_destroy(authz_ref_pool); + return SVN_NO_ERROR; + } + + authz_ref->authz = apr_palloc(authz_ref_pool, sizeof(*authz_ref->authz)); + authz_ref->authz->cfg = authz_ref->authz_cfg; + + if (groups_path) + { + /* Easy out: we prohibit local groups in the authz file when global + groups are being used. */ + if (svn_config_has_section(authz_ref->authz->cfg, + SVN_CONFIG_SECTION_GROUPS)) + return svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, + "Error reading authz file '%s' with " + "groups file '%s':" + "Authz file cannot contain any groups " + "when global groups are being used.", + path, groups_path); + + /* We simply need to add the [Groups] section to the authz config. + */ + svn_config__shallow_replace_section(authz_ref->authz->cfg, + authz_ref->groups_cfg, + SVN_CONFIG_SECTION_GROUPS); + } + + /* Make sure there are no errors in the configuration. */ + SVN_ERR(svn_repos__authz_validate(authz_ref->authz, authz_ref_pool)); + + SVN_ERR(svn_object_pool__insert((void **)authz_p, authz_pool->object_pool, + authz_ref->key, authz_ref, NULL, + authz_ref_pool, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_repos/commit.c b/contrib/subversion/subversion/libsvn_repos/commit.c index 22cf873b3..1190acc29 100644 --- a/contrib/subversion/subversion/libsvn_repos/commit.c +++ b/contrib/subversion/subversion/libsvn_repos/commit.c @@ -73,7 +73,7 @@ struct edit_baton svn_repos_t *repos; /* URL to the root of the open repository. */ - const char *repos_url; + const char *repos_url_decoded; /* The name of the repository (here for convenience). */ const char *repos_name; @@ -201,6 +201,7 @@ invoke_commit_cb(svn_commit_callback2_t commit_cb, commit_info->date = date ? date->data : NULL; commit_info->author = author ? author->data : NULL; commit_info->post_commit_err = post_commit_errstr; + /* commit_info->repos_root is not set by the repos layer, only by RA layers */ return svn_error_trace(commit_cb(commit_info, commit_baton, scratch_pool)); } @@ -262,7 +263,9 @@ make_dir_baton(struct edit_baton *edit_baton, /* 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. */ + and FALSE when adding a file. + + COPY_PATH must be a full URL, not a relative path. */ static svn_error_t * add_file_or_directory(const char *path, void *parent_baton, @@ -317,8 +320,8 @@ add_file_or_directory(const char *path, /* 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) + repos_url_len = strlen(eb->repos_url_decoded); + if (strncmp(copy_path, eb->repos_url_decoded, repos_url_len) != 0) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("Source url '%s' is from different repository"), copy_path); @@ -394,6 +397,11 @@ open_root(void *edit_baton, dateness checks. */ SVN_ERR(svn_fs_youngest_rev(&youngest, eb->fs, eb->pool)); + if (base_revision > youngest) + return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld (HEAD is %ld)"), + base_revision, youngest); + /* Unless we've been instructed to use a specific transaction, we'll make our own. */ if (eb->txn_owner) @@ -939,7 +947,7 @@ 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 *repos_url_decoded, const char *base_path, apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, @@ -953,6 +961,7 @@ svn_repos_get_commit_editor5(const svn_delta_editor_t **editor, struct edit_baton *eb; svn_delta_shim_callbacks_t *shim_callbacks = svn_delta_shim_callbacks_default(pool); + const char *repos_url = svn_path_uri_encode(repos_url_decoded, pool); /* Do a global authz access lookup. Users with no write access whatsoever to the repository don't get a commit editor. */ @@ -994,7 +1003,7 @@ svn_repos_get_commit_editor5(const svn_delta_editor_t **editor, eb->authz_baton = authz_baton; eb->base_path = svn_fspath__canonicalize(base_path, subpool); eb->repos = repos; - eb->repos_url = repos_url; + eb->repos_url_decoded = repos_url_decoded; eb->repos_name = svn_dirent_basename(svn_repos_path(repos, subpool), subpool); eb->fs = svn_repos_fs(repos); @@ -1010,7 +1019,7 @@ svn_repos_get_commit_editor5(const svn_delta_editor_t **editor, shim_callbacks->fetch_baton = eb; SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, - eb->repos_url, eb->base_path, + repos_url, eb->base_path, shim_callbacks, pool, pool)); return SVN_NO_ERROR; @@ -1031,7 +1040,7 @@ ev2_check_authz(const struct ev2_baton *eb, return SVN_NO_ERROR; if (relpath) - fspath = apr_pstrcat(scratch_pool, "/", relpath, NULL); + fspath = apr_pstrcat(scratch_pool, "/", relpath, SVN_VA_NULL); else fspath = NULL; @@ -1138,15 +1147,15 @@ 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_hash_t *props, apr_pool_t *scratch_pool) { struct ev2_baton *eb = baton; - SVN_ERR(svn_editor_alter_file(eb->inner, relpath, revision, props, - checksum, contents)); + SVN_ERR(svn_editor_alter_file(eb->inner, relpath, revision, + checksum, contents, props)); return SVN_NO_ERROR; } @@ -1156,14 +1165,14 @@ static svn_error_t * alter_symlink_cb(void *baton, const char *relpath, svn_revnum_t revision, - apr_hash_t *props, const char *target, + apr_hash_t *props, apr_pool_t *scratch_pool) { struct ev2_baton *eb = baton; - SVN_ERR(svn_editor_alter_symlink(eb->inner, relpath, revision, props, - target)); + SVN_ERR(svn_editor_alter_symlink(eb->inner, relpath, revision, + target, props)); return SVN_NO_ERROR; } @@ -1216,20 +1225,6 @@ move_cb(void *baton, } -/* 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, @@ -1351,7 +1346,6 @@ svn_repos__get_commit_ev2(svn_editor_t **editor, delete_cb, copy_cb, move_cb, - rotate_cb, complete_cb, abort_cb }; diff --git a/contrib/subversion/subversion/libsvn_repos/config_pool.c b/contrib/subversion/subversion/libsvn_repos/config_pool.c new file mode 100644 index 000000000..164bd983c --- /dev/null +++ b/contrib/subversion/subversion/libsvn_repos/config_pool.c @@ -0,0 +1,531 @@ +/* + * config_pool.c : pool of configuration objects + * + * ==================================================================== + * 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_checksum.h" +#include "svn_config.h" +#include "svn_error.h" +#include "svn_hash.h" +#include "svn_path.h" +#include "svn_pools.h" +#include "svn_repos.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_mutex.h" +#include "private/svn_subr_private.h" +#include "private/svn_repos_private.h" +#include "private/svn_object_pool.h" + +#include "svn_private_config.h" + + +/* Our wrapper structure for parsed svn_config_t* instances. All data in + * CS_CFG and CI_CFG is expanded (to make it thread-safe) and considered + * read-only. + */ +typedef struct config_object_t +{ + /* UUID of the configuration contents. + * This is a SHA1 checksum of the parsed textual representation of CFG. */ + svn_checksum_t *key; + + /* Parsed and expanded configuration. At least one of the following + * must not be NULL. */ + + /* Case-sensitive config. May be NULL */ + svn_config_t *cs_cfg; + + /* Case-insensitive config. May be NULL */ + svn_config_t *ci_cfg; +} config_object_t; + + +/* Data structure used to short-circuit the repository access for configs + * read via URL. After reading such a config successfully, we store key + * repository information here and will validate it without actually opening + * the repository. + * + * As this is only an optimization and may create many entries in + * svn_repos__config_pool_t's IN_REPO_HASH_POOL index, we clean them up + * once in a while. + */ +typedef struct in_repo_config_t +{ + /* URL used to open the configuration */ + const char *url; + + /* Path of the repository that contained URL */ + const char *repo_root; + + /* Head revision of that repository when last read */ + svn_revnum_t revision; + + /* Contents checksum of the file stored under URL@REVISION */ + svn_checksum_t *key; +} in_repo_config_t; + + +/* Core data structure extending the encapsulated OBJECT_POOL. All access + * to it must be serialized using the OBJECT_POOL->MUTEX. + * + * To speed up URL@HEAD lookups, we maintain IN_REPO_CONFIGS as a secondary + * hash index. It maps URLs as provided by the caller onto in_repo_config_t + * instances. If that is still up-to-date, a further lookup into CONFIG + * may yield the desired configuration without the need to actually open + * the respective repository. + * + * Unused configurations that are kept in the IN_REPO_CONFIGS hash and may + * be cleaned up when the hash is about to grow. + */ +struct svn_repos__config_pool_t +{ + svn_object_pool__t *object_pool; + + /* URL -> in_repo_config_t* mapping. + * This is only a partial index and will get cleared regularly. */ + apr_hash_t *in_repo_configs; + + /* allocate the IN_REPO_CONFIGS index and in_repo_config_t here */ + apr_pool_t *in_repo_hash_pool; +}; + + +/* Return an automatic reference to the CFG member in CONFIG that will be + * released when POOL gets cleaned up. The case sensitivity flag in *BATON + * selects the desired option and section name matching mode. + */ +static void * +getter(void *object, + void *baton, + apr_pool_t *pool) +{ + config_object_t *wrapper = object; + svn_boolean_t *case_sensitive = baton; + svn_config_t *config = *case_sensitive ? wrapper->cs_cfg : wrapper->ci_cfg; + + /* we need to duplicate the root structure as it contains temp. buffers */ + return config ? svn_config__shallow_copy(config, pool) : NULL; +} + +/* Return a memory buffer structure allocated in POOL and containing the + * data from CHECKSUM. + */ +static svn_membuf_t * +checksum_as_key(svn_checksum_t *checksum, + apr_pool_t *pool) +{ + svn_membuf_t *result = apr_pcalloc(pool, sizeof(*result)); + apr_size_t size = svn_checksum_size(checksum); + + svn_membuf__create(result, size, pool); + result->size = size; /* exact length is required! */ + memcpy(result->data, checksum->digest, size); + + return result; +} + +/* Copy the configuration from the wrapper in SOURCE to the wrapper in + * *TARGET with the case sensitivity flag in *BATON selecting the config + * to copy. This is usually done to add the missing case-(in)-sensitive + * variant. Since we must hold all data in *TARGET from the same POOL, + * a deep copy is required. + */ +static svn_error_t * +setter(void **target, + void *source, + void *baton, + apr_pool_t *pool) +{ + svn_boolean_t *case_sensitive = baton; + config_object_t *target_cfg = *(config_object_t **)target; + config_object_t *source_cfg = source; + + /* Maybe, we created a variant with different case sensitivity? */ + if (*case_sensitive && target_cfg->cs_cfg == NULL) + { + SVN_ERR(svn_config_dup(&target_cfg->cs_cfg, source_cfg->cs_cfg, pool)); + svn_config__set_read_only(target_cfg->cs_cfg, pool); + } + else if (!*case_sensitive && target_cfg->ci_cfg == NULL) + { + SVN_ERR(svn_config_dup(&target_cfg->ci_cfg, source_cfg->ci_cfg, pool)); + svn_config__set_read_only(target_cfg->ci_cfg, pool); + } + + return SVN_NO_ERROR; +} + +/* Set *CFG to the configuration passed in as text in CONTENTS and *KEY to + * the corresponding object pool key. If no such configuration exists in + * CONFIG_POOL, yet, parse CONTENTS and cache the result. CASE_SENSITIVE + * controls option and section name matching. + * + * RESULT_POOL determines the lifetime of the returned reference and + * SCRATCH_POOL is being used for temporary allocations. + */ +static svn_error_t * +auto_parse(svn_config_t **cfg, + svn_membuf_t **key, + svn_repos__config_pool_t *config_pool, + svn_stringbuf_t *contents, + svn_boolean_t case_sensitive, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_checksum_t *checksum; + config_object_t *config_object; + apr_pool_t *cfg_pool; + + /* calculate SHA1 over the whole file contents */ + SVN_ERR(svn_stream_close + (svn_stream_checksummed2 + (svn_stream_from_stringbuf(contents, scratch_pool), + &checksum, NULL, svn_checksum_sha1, TRUE, scratch_pool))); + + /* return reference to suitable config object if that already exists */ + *key = checksum_as_key(checksum, result_pool); + SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool, + *key, &case_sensitive, result_pool)); + if (*cfg) + return SVN_NO_ERROR; + + /* create a pool for the new config object and parse the data into it */ + cfg_pool = svn_object_pool__new_wrapper_pool(config_pool->object_pool); + + config_object = apr_pcalloc(cfg_pool, sizeof(*config_object)); + + SVN_ERR(svn_config_parse(case_sensitive ? &config_object->cs_cfg + : &config_object->ci_cfg, + svn_stream_from_stringbuf(contents, scratch_pool), + case_sensitive, case_sensitive, cfg_pool)); + + /* switch config data to r/o mode to guarantee thread-safe access */ + svn_config__set_read_only(case_sensitive ? config_object->cs_cfg + : config_object->ci_cfg, + cfg_pool); + + /* add config in pool, handle loads races and return the right config */ + SVN_ERR(svn_object_pool__insert((void **)cfg, config_pool->object_pool, + *key, config_object, &case_sensitive, + cfg_pool, result_pool)); + + return SVN_NO_ERROR; +} + +/* Store a URL@REVISION to CHECKSUM, REPOS_ROOT in CONFIG_POOL. + */ +static svn_error_t * +add_checksum(svn_repos__config_pool_t *config_pool, + const char *url, + const char *repos_root, + svn_revnum_t revision, + svn_checksum_t *checksum) +{ + apr_size_t path_len = strlen(url); + apr_pool_t *pool = config_pool->in_repo_hash_pool; + in_repo_config_t *config = apr_hash_get(config_pool->in_repo_configs, + url, path_len); + if (config) + { + /* update the existing entry */ + memcpy((void *)config->key->digest, checksum->digest, + svn_checksum_size(checksum)); + config->revision = revision; + + /* duplicate the string only if necessary */ + if (strcmp(config->repo_root, repos_root)) + config->repo_root = apr_pstrdup(pool, repos_root); + } + else + { + /* insert a new entry. + * Limit memory consumption by cyclically clearing pool and hash. */ + if (2 * svn_object_pool__count(config_pool->object_pool) + < apr_hash_count(config_pool->in_repo_configs)) + { + svn_pool_clear(pool); + config_pool->in_repo_configs = svn_hash__make(pool); + } + + /* construct the new entry */ + config = apr_pcalloc(pool, sizeof(*config)); + config->key = svn_checksum_dup(checksum, pool); + config->url = apr_pstrmemdup(pool, url, path_len); + config->repo_root = apr_pstrdup(pool, repos_root); + config->revision = revision; + + /* add to index */ + apr_hash_set(config_pool->in_repo_configs, url, path_len, config); + } + + return SVN_NO_ERROR; +} + +/* Set *CFG to the configuration stored in URL@HEAD and cache it in + * CONFIG_POOL. CASE_SENSITIVE controls + * option and section name matching. If PREFERRED_REPOS is given, + * use that if it also matches URL. + * + * RESULT_POOL determines the lifetime of the returned reference and + * SCRATCH_POOL is being used for temporary allocations. + */ +static svn_error_t * +find_repos_config(svn_config_t **cfg, + svn_membuf_t **key, + svn_repos__config_pool_t *config_pool, + const char *url, + svn_boolean_t case_sensitive, + svn_repos_t *preferred_repos, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_repos_t *repos = NULL; + svn_fs_t *fs; + svn_fs_root_t *root; + svn_revnum_t youngest_rev; + svn_node_kind_t node_kind; + const char *dirent; + svn_stream_t *stream; + const char *fs_path; + const char *repos_root_dirent; + svn_checksum_t *checksum; + svn_stringbuf_t *contents; + + *cfg = NULL; + SVN_ERR(svn_uri_get_dirent_from_file_url(&dirent, url, scratch_pool)); + + /* maybe we can use the preferred repos instance instead of creating a + * new one */ + if (preferred_repos) + { + repos_root_dirent = svn_repos_path(preferred_repos, scratch_pool); + if (!svn_dirent_is_absolute(repos_root_dirent)) + SVN_ERR(svn_dirent_get_absolute(&repos_root_dirent, + repos_root_dirent, + scratch_pool)); + + if (svn_dirent_is_ancestor(repos_root_dirent, dirent)) + repos = preferred_repos; + } + + /* open repos if no suitable preferred repos was provided. */ + if (!repos) + { + /* Search for a repository in the full path. */ + repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool); + + /* Attempt to open a repository at repos_root_dirent. */ + SVN_ERR(svn_repos_open3(&repos, repos_root_dirent, NULL, + scratch_pool, scratch_pool)); + } + + fs_path = &dirent[strlen(repos_root_dirent)]; + + /* Get the filesystem. */ + fs = svn_repos_fs(repos); + + /* Find HEAD and the revision root */ + SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool)); + SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, scratch_pool)); + + /* Fetch checksum and see whether we already have a matching config */ + SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, root, fs_path, + FALSE, scratch_pool)); + if (checksum) + { + *key = checksum_as_key(checksum, scratch_pool); + SVN_ERR(svn_object_pool__lookup((void **)cfg, config_pool->object_pool, + *key, &case_sensitive, result_pool)); + } + + /* not parsed, yet? */ + if (!*cfg) + { + svn_filesize_t length; + + /* fetch the file contents */ + SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool)); + if (node_kind != svn_node_file) + return SVN_NO_ERROR; + + SVN_ERR(svn_fs_file_length(&length, root, fs_path, scratch_pool)); + SVN_ERR(svn_fs_file_contents(&stream, root, fs_path, scratch_pool)); + SVN_ERR(svn_stringbuf_from_stream(&contents, stream, + (apr_size_t)length, scratch_pool)); + + /* handle it like ordinary file contents and cache it */ + SVN_ERR(auto_parse(cfg, key, config_pool, contents, case_sensitive, + result_pool, scratch_pool)); + } + + /* store the (path,rev) -> checksum mapping as well */ + if (*cfg && checksum) + SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool), + add_checksum(config_pool, url, repos_root_dirent, + youngest_rev, checksum)); + + return SVN_NO_ERROR; +} + +/* Given the URL, search the CONFIG_POOL for an entry that maps it URL to + * a content checksum and is still up-to-date. If this could be found, + * return the object's *KEY. Use POOL for allocations. + * + * Requires external serialization on CONFIG_POOL. + * + * Note that this is only the URL(+rev) -> Checksum lookup and does not + * guarantee that there is actually a config object available for *KEY. + */ +static svn_error_t * +key_by_url(svn_membuf_t **key, + svn_repos__config_pool_t *config_pool, + const char *url, + apr_pool_t *pool) +{ + svn_error_t *err; + svn_stringbuf_t *contents; + apr_int64_t current; + + /* hash lookup url -> sha1 -> config */ + in_repo_config_t *config = svn_hash_gets(config_pool->in_repo_configs, url); + *key = NULL; + if (!config) + return SVN_NO_ERROR; + + /* found *some* reference to a configuration. + * Verify that it is still current. Will fail for BDB repos. */ + err = svn_stringbuf_from_file2(&contents, + svn_dirent_join(config->repo_root, + "db/current", pool), + pool); + if (!err) + err = svn_cstring_atoi64(¤t, contents->data); + + if (err) + svn_error_clear(err); + else if (current == config->revision) + *key = checksum_as_key(config->key, pool); + + return SVN_NO_ERROR; +} + +/* API implementation */ + +svn_error_t * +svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool, + svn_boolean_t thread_safe, + apr_pool_t *pool) +{ + svn_repos__config_pool_t *result; + svn_object_pool__t *object_pool; + + SVN_ERR(svn_object_pool__create(&object_pool, getter, setter, + thread_safe, pool)); + + /* construct the config pool in our private ROOT_POOL to survive POOL + * cleanup and to prevent threading issues with the allocator */ + result = apr_pcalloc(pool, sizeof(*result)); + + result->object_pool = object_pool; + result->in_repo_hash_pool = svn_pool_create(pool); + result->in_repo_configs = svn_hash__make(result->in_repo_hash_pool); + + *config_pool = result; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos__config_pool_get(svn_config_t **cfg, + svn_membuf_t **key, + svn_repos__config_pool_t *config_pool, + const char *path, + svn_boolean_t must_exist, + svn_boolean_t case_sensitive, + svn_repos_t *preferred_repos, + apr_pool_t *pool) +{ + svn_error_t *err = SVN_NO_ERROR; + apr_pool_t *scratch_pool = svn_pool_create(pool); + + /* make sure we always have a *KEY object */ + svn_membuf_t *local_key = NULL; + if (key == NULL) + key = &local_key; + else + *key = NULL; + + if (svn_path_is_url(path)) + { + /* Read config file from repository. + * Attempt a quick lookup first. */ + SVN_MUTEX__WITH_LOCK(svn_object_pool__mutex(config_pool->object_pool), + key_by_url(key, config_pool, path, pool)); + if (*key) + { + SVN_ERR(svn_object_pool__lookup((void **)cfg, + config_pool->object_pool, + *key, &case_sensitive, pool)); + if (*cfg) + { + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; + } + } + + /* Read and cache the configuration. This may fail. */ + err = find_repos_config(cfg, key, config_pool, path, case_sensitive, + preferred_repos, pool, scratch_pool); + if (err || !*cfg) + { + /* let the standard implementation handle all the difficult cases */ + svn_error_clear(err); + err = svn_repos__retrieve_config(cfg, path, must_exist, + case_sensitive, pool); + } + } + else + { + /* Outside of repo file. Read it. */ + svn_stringbuf_t *contents; + err = svn_stringbuf_from_file2(&contents, path, scratch_pool); + if (err) + { + /* let the standard implementation handle all the difficult cases */ + svn_error_clear(err); + err = svn_config_read3(cfg, path, must_exist, case_sensitive, + case_sensitive, pool); + } + else + { + /* parsing and caching will always succeed */ + err = auto_parse(cfg, key, config_pool, contents, case_sensitive, + pool, scratch_pool); + } + } + + svn_pool_destroy(scratch_pool); + + return err; +} diff --git a/contrib/subversion/subversion/libsvn_repos/delta.c b/contrib/subversion/subversion/libsvn_repos/delta.c index 51cfda7bf..3e28c70b8 100644 --- a/contrib/subversion/subversion/libsvn_repos/delta.c +++ b/contrib/subversion/subversion/libsvn_repos/delta.c @@ -196,17 +196,6 @@ authz_root_check(svn_fs_root_t *root, } -static svn_error_t * -not_a_dir_error(const char *role, - const char *path) -{ - return svn_error_createf - (SVN_ERR_FS_NOT_DIRECTORY, 0, - "Invalid %s directory '%s'", - role, path ? path : "(null)"); -} - - /* Public interface to computing directory deltas. */ svn_error_t * svn_repos_dir_delta2(svn_fs_root_t *src_root, @@ -227,17 +216,17 @@ svn_repos_dir_delta2(svn_fs_root_t *src_root, void *root_baton = NULL; struct context c; const char *src_fullpath; - const svn_fs_id_t *src_id, *tgt_id; svn_node_kind_t src_kind, tgt_kind; svn_revnum_t rootrev; - int distance; + svn_fs_node_relation_t relation; const char *authz_root_path; /* SRC_PARENT_DIR must be valid. */ if (src_parent_dir) src_parent_dir = svn_relpath_canonicalize(src_parent_dir, pool); else - return not_a_dir_error("source parent", src_parent_dir); + return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, 0, + "Invalid source parent directory '(null)'"); /* TGT_FULLPATH must be valid. */ if (tgt_fullpath) @@ -329,11 +318,10 @@ svn_repos_dir_delta2(svn_fs_root_t *src_root, } /* Get and compare the node IDs for the source and target. */ - SVN_ERR(svn_fs_node_id(&tgt_id, tgt_root, tgt_fullpath, pool)); - SVN_ERR(svn_fs_node_id(&src_id, src_root, src_fullpath, pool)); - distance = svn_fs_compare_ids(src_id, tgt_id); + SVN_ERR(svn_fs_node_relation(&relation, tgt_root, tgt_fullpath, + src_root, src_fullpath, pool)); - if (distance == 0) + if (relation == svn_fs_node_unchanged) { /* They are the same node! No-op (you gotta love those). */ goto cleanup; @@ -344,7 +332,7 @@ svn_repos_dir_delta2(svn_fs_root_t *src_root, add the other. Also, if they are completely unrelated and our caller is interested in relatedness, we do the same thing. */ if ((src_kind != tgt_kind) - || ((distance == -1) && (! ignore_ancestry))) + || ((relation == svn_fs_node_unrelated) && (! ignore_ancestry))) { SVN_ERR(authz_root_check(tgt_root, authz_root_path, authz_read_func, authz_read_baton, pool)); @@ -535,8 +523,8 @@ delta_proplists(struct context *c, svn_boolean_t changed; /* Is this deltification worth our time? */ - SVN_ERR(svn_fs_props_changed(&changed, c->target_root, target_path, - c->source_root, source_path, subpool)); + SVN_ERR(svn_fs_props_different(&changed, c->target_root, target_path, + c->source_root, source_path, subpool)); if (! changed) goto cleanup; @@ -616,62 +604,8 @@ svn_repos__compare_files(svn_boolean_t *changed_p, const char *path2, apr_pool_t *pool) { - svn_filesize_t size1, size2; - svn_checksum_t *checksum1, *checksum2; - svn_stream_t *stream1, *stream2; - svn_boolean_t same; - - /* If the filesystem claims the things haven't changed, then they - haven't changed. */ - SVN_ERR(svn_fs_contents_changed(changed_p, root1, path1, - root2, path2, pool)); - if (!*changed_p) - return SVN_NO_ERROR; - - /* If the SHA1 checksums match for these things, we'll claim they - have the same contents. (We don't give quite as much weight to - MD5 checksums.) */ - SVN_ERR(svn_fs_file_checksum(&checksum1, svn_checksum_sha1, - root1, path1, FALSE, pool)); - SVN_ERR(svn_fs_file_checksum(&checksum2, svn_checksum_sha1, - root2, path2, FALSE, pool)); - if (checksum1 && checksum2) - { - *changed_p = !svn_checksum_match(checksum1, checksum2); - return SVN_NO_ERROR; - } - - /* From this point on, our default answer is "Nothing's changed". */ - *changed_p = FALSE; - - /* Different filesizes means the contents are different. */ - SVN_ERR(svn_fs_file_length(&size1, root1, path1, pool)); - SVN_ERR(svn_fs_file_length(&size2, root2, path2, pool)); - if (size1 != size2) - { - *changed_p = TRUE; - return SVN_NO_ERROR; - } - - /* Different MD5 checksums means the contents are different. */ - SVN_ERR(svn_fs_file_checksum(&checksum1, svn_checksum_md5, root1, path1, - FALSE, pool)); - SVN_ERR(svn_fs_file_checksum(&checksum2, svn_checksum_md5, root2, path2, - FALSE, pool)); - if (! svn_checksum_match(checksum1, checksum2)) - { - *changed_p = TRUE; - return SVN_NO_ERROR; - } - - /* And finally, different contents means the ... uh ... contents are - different. */ - SVN_ERR(svn_fs_file_contents(&stream1, root1, path1, pool)); - SVN_ERR(svn_fs_file_contents(&stream2, root2, path2, pool)); - SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, pool)); - *changed_p = !same; - - return SVN_NO_ERROR; + return svn_error_trace(svn_fs_contents_different(changed_p, root1, path1, + root2, path2, pool)); } @@ -698,19 +632,7 @@ delta_files(struct context *c, if (source_path) { - /* Is this delta calculation worth our time? If we are ignoring - ancestry, then our editor implementor isn't concerned by the - theoretical differences between "has contents which have not - changed with respect to" and "has the same actual contents - as". We'll do everything we can to avoid transmitting even - an empty text-delta in that case. */ - if (c->ignore_ancestry) - SVN_ERR(svn_repos__compare_files(&changed, - c->target_root, target_path, - c->source_root, source_path, - subpool)); - else - SVN_ERR(svn_fs_contents_changed(&changed, + SVN_ERR(svn_fs_contents_different(&changed, c->target_root, target_path, c->source_root, source_path, subpool)); @@ -953,10 +875,10 @@ delta_dirs(struct context *c, from the target tree. */ for (hi = apr_hash_first(pool, t_entries); hi; hi = apr_hash_next(hi)) { - const svn_fs_dirent_t *s_entry, *t_entry; - const void *key; - void *val; - apr_ssize_t klen; + const void *key = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(hi); + const svn_fs_dirent_t *t_entry = apr_hash_this_val(hi); + const svn_fs_dirent_t *s_entry; const char *t_fullpath; const char *e_fullpath; const char *s_fullpath; @@ -965,9 +887,6 @@ delta_dirs(struct context *c, /* Clear out our subpool for the next iteration... */ svn_pool_clear(subpool); - /* KEY is the entry name in target, VAL the dirent */ - apr_hash_this(hi, &key, &klen, &val); - t_entry = val; tgt_kind = t_entry->kind; t_fullpath = svn_relpath_join(target_path, t_entry->name, subpool); e_fullpath = svn_relpath_join(edit_path, t_entry->name, subpool); @@ -1042,17 +961,13 @@ delta_dirs(struct context *c, { for (hi = apr_hash_first(pool, s_entries); hi; hi = apr_hash_next(hi)) { - const svn_fs_dirent_t *s_entry; - void *val; + const svn_fs_dirent_t *s_entry = apr_hash_this_val(hi); const char *e_fullpath; svn_node_kind_t src_kind; /* Clear out our subpool for the next iteration... */ svn_pool_clear(subpool); - /* KEY is the entry name in source, VAL the dirent */ - apr_hash_this(hi, NULL, NULL, &val); - s_entry = val; src_kind = s_entry->kind; e_fullpath = svn_relpath_join(edit_path, s_entry->name, subpool); diff --git a/contrib/subversion/subversion/libsvn_repos/deprecated.c b/contrib/subversion/subversion/libsvn_repos/deprecated.c index 7208ba673..fe9d1d227 100644 --- a/contrib/subversion/subversion/libsvn_repos/deprecated.c +++ b/contrib/subversion/subversion/libsvn_repos/deprecated.c @@ -137,6 +137,15 @@ svn_repos_get_commit_editor(const svn_delta_editor_t **editor, callback_baton, pool); } +svn_error_t * +svn_repos_open2(svn_repos_t **repos_p, + const char *path, + apr_hash_t *fs_config, + apr_pool_t *pool) +{ + return svn_repos_open3(repos_p, path, fs_config, pool, pool); +} + svn_error_t * svn_repos_open(svn_repos_t **repos_p, const char *path, @@ -217,6 +226,30 @@ svn_repos_upgrade(const char *path, return svn_repos_upgrade2(path, nonblocking, recovery_started, &rb, pool); } +svn_error_t * +svn_repos_hotcopy2(const char *src_path, + const char *dst_path, + svn_boolean_t clean_logs, + svn_boolean_t incremental, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + return svn_error_trace(svn_repos_hotcopy3(src_path, dst_path, clean_logs, + incremental, NULL, NULL, + cancel_func, cancel_baton, pool)); +} + +svn_error_t * +svn_repos_hotcopy(const char *src_path, + const char *dst_path, + svn_boolean_t clean_logs, + apr_pool_t *pool) +{ + return svn_error_trace(svn_repos_hotcopy2(src_path, dst_path, clean_logs, + FALSE, NULL, NULL, pool)); +} + /*** From reporter.c ***/ svn_error_t * svn_repos_begin_report(void **report_baton, @@ -726,6 +759,29 @@ svn_repos_dump_fs2(svn_repos_t *repos, pool)); } +svn_error_t * +svn_repos_verify_fs2(svn_repos_t *repos, + svn_revnum_t start_rev, + svn_revnum_t end_rev, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + return svn_error_trace(svn_repos_verify_fs3(repos, + start_rev, + end_rev, + FALSE, + FALSE, + notify_func, + notify_baton, + NULL, NULL, + cancel_func, + cancel_baton, + pool)); +} + svn_error_t * svn_repos_verify_fs(svn_repos_t *repos, svn_stream_t *feedback_stream, @@ -749,6 +805,30 @@ svn_repos_verify_fs(svn_repos_t *repos, /*** From load.c ***/ +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) +{ + return svn_repos_load_fs5(repos, dumpstream, start_rev, end_rev, + uuid_action, parent_dir, + use_post_commit_hook, use_post_commit_hook, + validate_props, FALSE, + notify_func, notify_baton, + cancel_func, cancel_baton, pool); +} + svn_error_t * svn_repos_load_fs3(svn_repos_t *repos, svn_stream_t *dumpstream, @@ -916,6 +996,35 @@ svn_repos_load_fs(svn_repos_t *repos, cancel_func, cancel_baton, pool); } +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_ERR(svn_repos_get_fs_build_parser5(callbacks, parse_baton, + repos, + start_rev, end_rev, + use_history, + validate_props, + uuid_action, + parent_dir, + FALSE, FALSE, /*hooks */ + FALSE /*ignore_dates*/, + notify_func, + notify_baton, + pool)); + return SVN_NO_ERROR; +} + svn_error_t * svn_repos_get_fs_build_parser3(const svn_repos_parse_fns2_t **callbacks, void **parse_baton, diff --git a/contrib/subversion/subversion/libsvn_repos/dump.c b/contrib/subversion/subversion/libsvn_repos/dump.c index a64b1809c..78cd78beb 100644 --- a/contrib/subversion/subversion/libsvn_repos/dump.c +++ b/contrib/subversion/subversion/libsvn_repos/dump.c @@ -21,6 +21,8 @@ */ +#include + #include "svn_private_config.h" #include "svn_pools.h" #include "svn_error.h" @@ -36,14 +38,281 @@ #include "svn_props.h" #include "svn_sorts.h" +#include "private/svn_repos_private.h" #include "private/svn_mergeinfo_private.h" #include "private/svn_fs_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_utf_private.h" +#include "private/svn_cache.h" #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) /*----------------------------------------------------------------------*/ +/* To be able to check whether a path exists in the current revision + (as changes come in), we need to track the relevant tree changes. + + In particular, we remember deletions, additions and copies including + their copy-from info. Since the dump performs a pre-order tree walk, + we only need to store the data for the stack of parent folders. + + The problem that we are trying to solve is that the dump receives + transforming operations whose validity depends on previous operations + in the same revision but cannot be checked against the final state + as stored in the repository as that is the state *after* we applied + the respective tree changes. + + Note that the tracker functions don't perform any sanity or validity + checks. Those higher-level tests have to be done in the calling code. + However, there is no way to corrupt the data structure using the + provided functions. + */ + +/* Single entry in the path tracker. Not all levels along the path + hierarchy do need to have an instance of this struct but only those + that got changed by a tree modification. + + Please note that the path info in this struct is stored in re-usable + stringbuf objects such that we don't need to allocate more memory than + the longest path we encounter. + */ +typedef struct path_tracker_entry_t +{ + /* path in the current tree */ + svn_stringbuf_t *path; + + /* copy-from path (must be empty if COPYFROM_REV is SVN_INVALID_REVNUM) */ + svn_stringbuf_t *copyfrom_path; + + /* copy-from revision (SVN_INVALID_REVNUM for additions / replacements + that don't copy history, i.e. with no sub-tree) */ + svn_revnum_t copyfrom_rev; + + /* if FALSE, PATH has been deleted */ + svn_boolean_t exists; +} path_tracker_entry_t; + +/* Tracks all tree modifications above the current path. + */ +typedef struct path_tracker_t +{ + /* Container for all relevant tree changes in depth order. + May contain more entries than DEPTH to allow for reusing memory. + Only entries 0 .. DEPTH-1 are valid. + */ + apr_array_header_t *stack; + + /* Number of relevant entries in STACK. May be 0 */ + int depth; + + /* Revision that we current track. If DEPTH is 0, paths are exist in + REVISION exactly when they exist in REVISION-1. This applies only + to the current state of our tree walk. + */ + svn_revnum_t revision; + + /* Allocate container entries here. */ + apr_pool_t *pool; +} path_tracker_t; + +/* Return a new path tracker object for REVISION, allocated in POOL. + */ +static path_tracker_t * +tracker_create(svn_revnum_t revision, + apr_pool_t *pool) +{ + path_tracker_t *result = apr_pcalloc(pool, sizeof(*result)); + result->stack = apr_array_make(pool, 16, sizeof(path_tracker_entry_t)); + result->revision = revision; + result->pool = pool; + + return result; +} + +/* Remove all entries from TRACKER that are not relevant to PATH anymore. + * If ALLOW_EXACT_MATCH is FALSE, keep only entries that pertain to + * parent folders but not to PATH itself. + * + * This internal function implicitly updates the tracker state during the + * tree by removing "past" entries. Other functions will add entries when + * we encounter a new tree change. + */ +static void +tracker_trim(path_tracker_t *tracker, + const char *path, + svn_boolean_t allow_exact_match) +{ + /* remove everything that is unrelated to PATH. + Note that TRACKER->STACK is depth-ordered, + i.e. stack[N] is a (maybe indirect) parent of stack[N+1] + for N+1 < DEPTH. + */ + for (; tracker->depth; --tracker->depth) + { + path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack, + tracker->depth - 1, + path_tracker_entry_t); + const char *rel_path + = svn_dirent_skip_ancestor(parent->path->data, path); + + /* always keep parents. Keep exact matches when allowed. */ + if (rel_path && (allow_exact_match || *rel_path != '\0')) + break; + } +} + +/* Using TRACKER, check what path at what revision in the repository must + be checked to decide that whether PATH exists. Return the info in + *ORIG_PATH and *ORIG_REV, respectively. + + If the path is known to not exist, *ORIG_PATH will be NULL and *ORIG_REV + will be SVN_INVALID_REVNUM. If *ORIG_REV is SVN_INVALID_REVNUM, PATH + has just been added in the revision currently being tracked. + + Use POOL for allocations. Note that *ORIG_PATH may be allocated in POOL, + a reference to internal data with the same lifetime as TRACKER or just + PATH. + */ +static void +tracker_lookup(const char **orig_path, + svn_revnum_t *orig_rev, + path_tracker_t *tracker, + const char *path, + apr_pool_t *pool) +{ + tracker_trim(tracker, path, TRUE); + if (tracker->depth == 0) + { + /* no tree changes -> paths are the same as in the previous rev. */ + *orig_path = path; + *orig_rev = tracker->revision - 1; + } + else + { + path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack, + tracker->depth - 1, + path_tracker_entry_t); + if (parent->exists) + { + const char *rel_path + = svn_dirent_skip_ancestor(parent->path->data, path); + + if (parent->copyfrom_rev != SVN_INVALID_REVNUM) + { + /* parent is a copy with history. Translate path. */ + *orig_path = svn_dirent_join(parent->copyfrom_path->data, + rel_path, pool); + *orig_rev = parent->copyfrom_rev; + } + else if (*rel_path == '\0') + { + /* added in this revision with no history */ + *orig_path = path; + *orig_rev = tracker->revision; + } + else + { + /* parent got added but not this path */ + *orig_path = NULL; + *orig_rev = SVN_INVALID_REVNUM; + } + } + else + { + /* (maybe parent) path has been deleted */ + *orig_path = NULL; + *orig_rev = SVN_INVALID_REVNUM; + } + } +} + +/* Return a reference to the stack entry in TRACKER for PATH. If no + suitable entry exists, add one. Implicitly updates the tracked tree + location. + + Only the PATH member of the result is being updated. All other members + will have undefined values. + */ +static path_tracker_entry_t * +tracker_add_entry(path_tracker_t *tracker, + const char *path) +{ + path_tracker_entry_t *entry; + tracker_trim(tracker, path, FALSE); + + if (tracker->depth == tracker->stack->nelts) + { + entry = apr_array_push(tracker->stack); + entry->path = svn_stringbuf_create_empty(tracker->pool); + entry->copyfrom_path = svn_stringbuf_create_empty(tracker->pool); + } + else + { + entry = &APR_ARRAY_IDX(tracker->stack, tracker->depth, + path_tracker_entry_t); + } + + svn_stringbuf_set(entry->path, path); + ++tracker->depth; + + return entry; +} + +/* Update the TRACKER with a copy from COPYFROM_PATH@COPYFROM_REV to + PATH in the tracked revision. + */ +static void +tracker_path_copy(path_tracker_t *tracker, + const char *path, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev) +{ + path_tracker_entry_t *entry = tracker_add_entry(tracker, path); + + svn_stringbuf_set(entry->copyfrom_path, copyfrom_path); + entry->copyfrom_rev = copyfrom_rev; + entry->exists = TRUE; +} + +/* Update the TRACKER with a plain addition of PATH (without history). + */ +static void +tracker_path_add(path_tracker_t *tracker, + const char *path) +{ + path_tracker_entry_t *entry = tracker_add_entry(tracker, path); + + svn_stringbuf_setempty(entry->copyfrom_path); + entry->copyfrom_rev = SVN_INVALID_REVNUM; + entry->exists = TRUE; +} + +/* Update the TRACKER with a replacement of PATH with a plain addition + (without history). + */ +static void +tracker_path_replace(path_tracker_t *tracker, + const char *path) +{ + /* this will implicitly purge all previous sub-tree info from STACK. + Thus, no need to tack the deletion explicitly. */ + tracker_path_add(tracker, path); +} + +/* Update the TRACKER with a deletion of PATH. + */ +static void +tracker_path_delete(path_tracker_t *tracker, + const char *path) +{ + path_tracker_entry_t *entry = tracker_add_entry(tracker, path); + + svn_stringbuf_setempty(entry->copyfrom_path); + entry->copyfrom_rev = SVN_INVALID_REVNUM; + entry->exists = FALSE; +} + /* Compute the delta between OLDROOT/OLDPATH and NEWROOT/NEWPATH and store it into a new temporary file *TEMPFILE. OLDROOT may be NULL, @@ -84,6 +353,265 @@ store_delta(apr_file_t **tempfile, svn_filesize_t *len, } +/* Send a notification of type #svn_repos_notify_warning, subtype WARNING, + with message WARNING_FMT formatted with the remaining variable arguments. + Send it by calling NOTIFY_FUNC (if not null) with NOTIFY_BATON. + */ +__attribute__((format(printf, 5, 6))) +static void +notify_warning(apr_pool_t *scratch_pool, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_repos_notify_warning_t warning, + const char *warning_fmt, + ...) +{ + va_list va; + svn_repos_notify_t *notify; + + if (notify_func == NULL) + return; + + notify = svn_repos_notify_create(svn_repos_notify_warning, scratch_pool); + notify->warning = warning; + va_start(va, warning_fmt); + notify->warning_str = apr_pvsprintf(scratch_pool, warning_fmt, va); + va_end(va); + + notify_func(notify_baton, notify, scratch_pool); +} + + +/*----------------------------------------------------------------------*/ + +/* Write to STREAM the header in HEADERS named KEY, if present. + */ +static svn_error_t * +write_header(svn_stream_t *stream, + apr_hash_t *headers, + const char *key, + apr_pool_t *scratch_pool) +{ + const char *val = svn_hash_gets(headers, key); + + if (val) + { + SVN_ERR(svn_stream_printf(stream, scratch_pool, + "%s: %s\n", key, val)); + } + return SVN_NO_ERROR; +} + +/* Write headers, in arbitrary order. + * ### TODO: use a stable order + * ### Modifies HEADERS. + */ +static svn_error_t * +write_revision_headers(svn_stream_t *stream, + apr_hash_t *headers, + apr_pool_t *scratch_pool) +{ + const char **h; + apr_hash_index_t *hi; + + static const char *revision_headers_order[] = + { + SVN_REPOS_DUMPFILE_REVISION_NUMBER, /* must be first */ + NULL + }; + + /* Write some headers in a given order */ + for (h = revision_headers_order; *h; h++) + { + SVN_ERR(write_header(stream, headers, *h, scratch_pool)); + svn_hash_sets(headers, *h, NULL); + } + + /* Write any and all remaining headers except Content-length. + * ### TODO: use a stable order + */ + for (hi = apr_hash_first(scratch_pool, headers); hi; hi = apr_hash_next(hi)) + { + const char *key = apr_hash_this_key(hi); + + if (strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH) != 0) + SVN_ERR(write_header(stream, headers, key, scratch_pool)); + } + + /* Content-length must be last */ + SVN_ERR(write_header(stream, headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/* A header entry: the element type of the apr_array_header_t which is + * the real type of svn_repos__dumpfile_headers_t. + */ +typedef struct svn_repos__dumpfile_header_entry_t { + const char *key, *val; +} svn_repos__dumpfile_header_entry_t; + +svn_repos__dumpfile_headers_t * +svn_repos__dumpfile_headers_create(apr_pool_t *pool) +{ + svn_repos__dumpfile_headers_t *headers + = apr_array_make(pool, 5, sizeof(svn_repos__dumpfile_header_entry_t)); + + return headers; +} + +void +svn_repos__dumpfile_header_push(svn_repos__dumpfile_headers_t *headers, + const char *key, + const char *val) +{ + svn_repos__dumpfile_header_entry_t *h + = &APR_ARRAY_PUSH(headers, svn_repos__dumpfile_header_entry_t); + + h->key = apr_pstrdup(headers->pool, key); + h->val = apr_pstrdup(headers->pool, val); +} + +void +svn_repos__dumpfile_header_pushf(svn_repos__dumpfile_headers_t *headers, + const char *key, + const char *val_fmt, + ...) +{ + va_list ap; + svn_repos__dumpfile_header_entry_t *h + = &APR_ARRAY_PUSH(headers, svn_repos__dumpfile_header_entry_t); + + h->key = apr_pstrdup(headers->pool, key); + va_start(ap, val_fmt); + h->val = apr_pvsprintf(headers->pool, val_fmt, ap); + va_end(ap); +} + +svn_error_t * +svn_repos__dump_headers(svn_stream_t *stream, + svn_repos__dumpfile_headers_t *headers, + apr_pool_t *scratch_pool) +{ + int i; + + for (i = 0; i < headers->nelts; i++) + { + svn_repos__dumpfile_header_entry_t *h + = &APR_ARRAY_IDX(headers, i, svn_repos__dumpfile_header_entry_t); + + SVN_ERR(svn_stream_printf(stream, scratch_pool, + "%s: %s\n", h->key, h->val)); + } + + /* End of headers */ + SVN_ERR(svn_stream_puts(stream, "\n")); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos__dump_revision_record(svn_stream_t *dump_stream, + svn_revnum_t revision, + apr_hash_t *extra_headers, + apr_hash_t *revprops, + svn_boolean_t props_section_always, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *propstring = NULL; + apr_hash_t *headers; + + if (extra_headers) + headers = apr_hash_copy(scratch_pool, extra_headers); + else + headers = apr_hash_make(scratch_pool); + + /* ### someday write a revision-content-checksum */ + + svn_hash_sets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER, + apr_psprintf(scratch_pool, "%ld", revision)); + + if (apr_hash_count(revprops) || props_section_always) + { + svn_stream_t *propstream; + + propstring = svn_stringbuf_create_empty(scratch_pool); + propstream = svn_stream_from_stringbuf(propstring, scratch_pool); + SVN_ERR(svn_hash_write2(revprops, propstream, "PROPS-END", scratch_pool)); + SVN_ERR(svn_stream_close(propstream)); + + svn_hash_sets(headers, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH, + apr_psprintf(scratch_pool, + "%" APR_SIZE_T_FMT, propstring->len)); + } + + /* Write out a regular Content-length header for the benefit of + non-Subversion RFC-822 parsers. */ + svn_hash_sets(headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + apr_psprintf(scratch_pool, + "%" APR_SIZE_T_FMT, propstring->len)); + SVN_ERR(write_revision_headers(dump_stream, headers, scratch_pool)); + + /* End of headers */ + SVN_ERR(svn_stream_puts(dump_stream, "\n")); + + /* Property data. */ + if (propstring) + { + SVN_ERR(svn_stream_write(dump_stream, propstring->data, &propstring->len)); + } + + /* put an end to revision */ + SVN_ERR(svn_stream_puts(dump_stream, "\n")); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos__dump_node_record(svn_stream_t *dump_stream, + svn_repos__dumpfile_headers_t *headers, + svn_stringbuf_t *props_str, + svn_boolean_t has_text, + svn_filesize_t text_content_length, + svn_boolean_t content_length_always, + apr_pool_t *scratch_pool) +{ + svn_filesize_t content_length = 0; + + /* add content-length headers */ + if (props_str) + { + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH, + "%" APR_SIZE_T_FMT, props_str->len); + content_length += props_str->len; + } + if (has_text) + { + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH, + "%" SVN_FILESIZE_T_FMT, text_content_length); + content_length += text_content_length; + } + if (content_length_always || props_str || has_text) + { + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH, + "%" SVN_FILESIZE_T_FMT, content_length); + } + + /* write the headers */ + SVN_ERR(svn_repos__dump_headers(dump_stream, headers, scratch_pool)); + + /* write the props */ + if (props_str) + { + SVN_ERR(svn_stream_write(dump_stream, props_str->data, &props_str->len)); + } + return SVN_NO_ERROR; +} + /*----------------------------------------------------------------------*/ /** An editor which dumps node-data in 'dumpfile format' to a file. **/ @@ -116,6 +644,9 @@ struct edit_baton /* True if this "dump" is in fact a verify. */ svn_boolean_t verify; + /* True if checking UCS normalization during a verify. */ + svn_boolean_t check_normalization; + /* The first revision dumped in this dumpstream. */ svn_revnum_t oldest_dumped_rev; @@ -127,18 +658,14 @@ struct edit_baton revisions older than OLDEST_DUMPED_REV. */ svn_boolean_t *found_old_mergeinfo; - /* reusable buffer for writing file contents */ - char buffer[SVN__STREAM_CHUNK_SIZE]; - apr_size_t bufsize; + /* Structure allows us to verify the paths currently being dumped. + If NULL, validity checks are being skipped. */ + path_tracker_t *path_tracker; }; struct dir_baton { struct edit_baton *edit_baton; - struct dir_baton *parent_dir_baton; - - /* is this directory a new addition to this revision? */ - svn_boolean_t added; /* has this directory been written to the output stream? */ svn_boolean_t written_out; @@ -159,6 +686,12 @@ struct dir_baton really, they're all within this directory.) */ apr_hash_t *deleted_entries; + /* A flag indicating that new entries have been added to this + directory in this revision. Used to optimize detection of UCS + representation collisions; we will only check for that in + revisions where new names appear in the directory. */ + svn_boolean_t check_name_collision; + /* pool to be used for deleting the hash items */ apr_pool_t *pool; }; @@ -172,21 +705,19 @@ struct dir_baton path, SVN_INVALID_REVNUM for the rev), just compare this directory PATH against itself in the previous revision. - PARENT_DIR_BATON is the directory baton of this directory's parent, - or NULL if this is the top-level directory of the edit. ADDED - indicated if this directory is newly added in this revision. + PB is the directory baton of this directory's parent, + or NULL if this is the top-level directory of the edit. + Perform all allocations in POOL. */ static struct dir_baton * make_dir_baton(const char *path, const char *cmp_path, svn_revnum_t cmp_rev, void *edit_baton, - void *parent_dir_baton, - svn_boolean_t added, + struct dir_baton *pb, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; - struct dir_baton *pb = parent_dir_baton; struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db)); const char *full_path; @@ -204,18 +735,106 @@ make_dir_baton(const char *path, cmp_path = svn_relpath_canonicalize(cmp_path, pool); new_db->edit_baton = eb; - new_db->parent_dir_baton = pb; new_db->path = full_path; new_db->cmp_path = cmp_path; new_db->cmp_rev = cmp_rev; - new_db->added = added; new_db->written_out = FALSE; new_db->deleted_entries = apr_hash_make(pool); + new_db->check_name_collision = FALSE; new_db->pool = pool; return new_db; } +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); + +/* Return an error when PATH in REVISION does not exist or is of a + different kind than EXPECTED_KIND. If the latter is svn_node_unknown, + skip that check. Use EB for context information. If REVISION is the + current revision, use EB's path tracker to follow renames, deletions, + etc. + + Use SCRATCH_POOL for temporary allocations. + No-op if EB's path tracker has not been initialized. + */ +static svn_error_t * +node_must_exist(struct edit_baton *eb, + const char *path, + svn_revnum_t revision, + svn_node_kind_t expected_kind, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind = svn_node_none; + + /* in case the caller is trying something stupid ... */ + if (eb->path_tracker == NULL) + return SVN_NO_ERROR; + + /* paths pertaining to the revision currently being processed must + be translated / checked using our path tracker. */ + if (revision == eb->path_tracker->revision) + tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool); + + /* determine the node type (default: no such node) */ + if (path) + SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool)); + + /* check results */ + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("Path '%s' not found in r%ld."), + path, revision); + + if (expected_kind != kind && expected_kind != svn_node_unknown) + return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("Unexpected node kind %d for '%s' at r%ld. " + "Expected kind was %d."), + kind, path, revision, expected_kind); + + return SVN_NO_ERROR; +} + +/* Return an error when PATH exists in REVISION. Use EB for context + information. If REVISION is the current revision, use EB's path + tracker to follow renames, deletions, etc. + + Use SCRATCH_POOL for temporary allocations. + No-op if EB's path tracker has not been initialized. + */ +static svn_error_t * +node_must_not_exist(struct edit_baton *eb, + const char *path, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind = svn_node_none; + + /* in case the caller is trying something stupid ... */ + if (eb->path_tracker == NULL) + return SVN_NO_ERROR; + + /* paths pertaining to the revision currently being processed must + be translated / checked using our path tracker. */ + if (revision == eb->path_tracker->revision) + tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool); + + /* determine the node type (default: no such node) */ + if (path) + SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool)); + + /* check results */ + if (kind != svn_node_none) + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '%s' exists in r%ld."), + path, revision); + + return SVN_NO_ERROR; +} /* If the mergeinfo in MERGEINFO_STR refers to any revisions older than * OLDEST_DUMPED_REV, issue a warning and set *FOUND_OLD_MERGEINFO to TRUE, @@ -239,33 +858,222 @@ verify_mergeinfo_revisions(svn_boolean_t *found_old_mergeinfo, if (apr_hash_count(old_mergeinfo)) { - svn_repos_notify_t *notify = - svn_repos_notify_create(svn_repos_notify_warning, pool); - - notify->warning = svn_repos_notify_warning_found_old_mergeinfo; - notify->warning_str = apr_psprintf( - pool, - _("Mergeinfo referencing revision(s) prior " - "to the oldest dumped revision (r%ld). " - "Loading this dump may result in invalid " - "mergeinfo."), - oldest_dumped_rev); + notify_warning(pool, notify_func, notify_baton, + svn_repos_notify_warning_found_old_mergeinfo, + _("Mergeinfo referencing revision(s) prior " + "to the oldest dumped revision (r%ld). " + "Loading this dump may result in invalid " + "mergeinfo."), + oldest_dumped_rev); if (found_old_mergeinfo) *found_old_mergeinfo = TRUE; - notify_func(notify_baton, notify, pool); } return SVN_NO_ERROR; } +/* Unique string pointers used by verify_mergeinfo_normalization() + and check_name_collision() */ +static const char normalized_unique[] = "normalized_unique"; +static const char normalized_collision[] = "normalized_collision"; + + +/* Baton for extract_mergeinfo_paths */ +struct extract_mergeinfo_paths_baton +{ + apr_hash_t *result; + svn_boolean_t normalize; + svn_membuf_t buffer; +}; + +/* Hash iterator that uniquifies all keys into a single hash table, + optionally normalizing them first. */ +static svn_error_t * +extract_mergeinfo_paths(void *baton, const void *key, apr_ssize_t klen, + void *val, apr_pool_t *iterpool) +{ + struct extract_mergeinfo_paths_baton *const xb = baton; + if (xb->normalize) + { + const char *normkey; + SVN_ERR(svn_utf__normalize(&normkey, key, klen, &xb->buffer)); + svn_hash_sets(xb->result, + apr_pstrdup(xb->buffer.pool, normkey), + normalized_unique); + } + else + apr_hash_set(xb->result, + apr_pmemdup(xb->buffer.pool, key, klen + 1), klen, + normalized_unique); + return SVN_NO_ERROR; +} + +/* Baton for filter_mergeinfo_paths */ +struct filter_mergeinfo_paths_baton +{ + apr_hash_t *paths; +}; + +/* Compare two sets of denormalized paths from mergeinfo entries, + removing duplicates. */ +static svn_error_t * +filter_mergeinfo_paths(void *baton, const void *key, apr_ssize_t klen, + void *val, apr_pool_t *iterpool) +{ + struct filter_mergeinfo_paths_baton *const fb = baton; + + if (apr_hash_get(fb->paths, key, klen)) + apr_hash_set(fb->paths, key, klen, NULL); + + return SVN_NO_ERROR; +} + +/* Baton used by the check_mergeinfo_normalization hash iterator. */ +struct verify_mergeinfo_normalization_baton +{ + const char* path; + apr_hash_t *normalized_paths; + svn_membuf_t buffer; + svn_repos_notify_func_t notify_func; + void *notify_baton; +}; + +/* Hash iterator that verifies normalization and collision of paths in + an svn:mergeinfo property. */ +static svn_error_t * +verify_mergeinfo_normalization(void *baton, const void *key, apr_ssize_t klen, + void *val, apr_pool_t *iterpool) +{ + struct verify_mergeinfo_normalization_baton *const vb = baton; + + const char *const path = key; + const char *normpath; + const char *found; + + SVN_ERR(svn_utf__normalize(&normpath, path, klen, &vb->buffer)); + found = svn_hash_gets(vb->normalized_paths, normpath); + if (!found) + svn_hash_sets(vb->normalized_paths, + apr_pstrdup(vb->buffer.pool, normpath), + normalized_unique); + else if (found == normalized_collision) + /* Skip already reported collision */; + else + { + /* Report path collision in mergeinfo */ + svn_hash_sets(vb->normalized_paths, + apr_pstrdup(vb->buffer.pool, normpath), + normalized_collision); + + notify_warning(iterpool, vb->notify_func, vb->notify_baton, + svn_repos_notify_warning_mergeinfo_collision, + _("Duplicate representation of path '%s'" + " in %s property of '%s'"), + normpath, SVN_PROP_MERGEINFO, vb->path); + } + return SVN_NO_ERROR; +} + +/* Check UCS normalization of mergeinfo for PATH. NEW_MERGEINFO is the + svn:mergeinfo property value being set; OLD_MERGEINFO is the + previous property value, which may be NULL. Only the paths that + were added in are checked, including collision checks. This + minimizes the number of notifications we generate for a given + mergeinfo property. */ +static svn_error_t * +check_mergeinfo_normalization(const char *path, + const char *new_mergeinfo, + const char *old_mergeinfo, + svn_repos_notify_func_t notify_func, + void *notify_baton, + apr_pool_t *pool) +{ + svn_mergeinfo_t mergeinfo; + apr_hash_t *normalized_paths; + apr_hash_t *added_paths; + struct extract_mergeinfo_paths_baton extract_baton; + struct verify_mergeinfo_normalization_baton verify_baton; + + SVN_ERR(svn_mergeinfo_parse(&mergeinfo, new_mergeinfo, pool)); + + extract_baton.result = apr_hash_make(pool); + extract_baton.normalize = FALSE; + svn_membuf__create(&extract_baton.buffer, 0, pool); + SVN_ERR(svn_iter_apr_hash(NULL, mergeinfo, + extract_mergeinfo_paths, + &extract_baton, pool)); + added_paths = extract_baton.result; + + if (old_mergeinfo) + { + struct filter_mergeinfo_paths_baton filter_baton; + svn_mergeinfo_t oldinfo; + + extract_baton.result = apr_hash_make(pool); + extract_baton.normalize = TRUE; + SVN_ERR(svn_mergeinfo_parse(&oldinfo, old_mergeinfo, pool)); + SVN_ERR(svn_iter_apr_hash(NULL, oldinfo, + extract_mergeinfo_paths, + &extract_baton, pool)); + normalized_paths = extract_baton.result; + + filter_baton.paths = added_paths; + SVN_ERR(svn_iter_apr_hash(NULL, oldinfo, + filter_mergeinfo_paths, + &filter_baton, pool)); + } + else + normalized_paths = apr_hash_make(pool); + + verify_baton.path = path; + verify_baton.normalized_paths = normalized_paths; + verify_baton.buffer = extract_baton.buffer; + verify_baton.notify_func = notify_func; + verify_baton.notify_baton = notify_baton; + SVN_ERR(svn_iter_apr_hash(NULL, added_paths, + verify_mergeinfo_normalization, + &verify_baton, pool)); + + return SVN_NO_ERROR; +} + + +/* A special case of dump_node(), for a delete record. + * + * The only thing special about this version is it only writes one blank + * line, not two, after the headers. Why? Historical precedent for the + * case where a delete record is used as part of a (delete + add-with-history) + * in implementing a replacement. + * + * Also it doesn't do a path-tracker check. + */ +static svn_error_t * +dump_node_delete(svn_stream_t *stream, + const char *node_relpath, + apr_pool_t *pool) +{ + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); + + /* Node-path: ... */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); + + /* Node-action: delete */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); + + SVN_ERR(svn_repos__dump_headers(stream, headers, pool)); + return SVN_NO_ERROR; +} /* This helper is the main "meat" of the editor -- it does all the work of writing a node record. Write out a node record for PATH of type KIND under EB->FS_ROOT. ACTION describes what is happening to the node (see enum svn_node_action). - Write record to writable EB->STREAM, using EB->BUFFER to write in chunks. + Write record to writable EB->STREAM. If the node was itself copied, IS_COPY is TRUE and the path/revision of the copy source are in CMP_PATH/CMP_REV. If @@ -283,13 +1091,15 @@ dump_node(struct edit_baton *eb, apr_pool_t *pool) { svn_stringbuf_t *propstring; - svn_filesize_t content_length = 0; apr_size_t len; svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE; const char *compare_path = path; svn_revnum_t compare_rev = eb->current_rev - 1; svn_fs_root_t *compare_root = NULL; apr_file_t *delta_file = NULL; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); + svn_filesize_t textlen; /* Maybe validate the path. */ if (eb->verify || eb->notify_func) @@ -301,17 +1111,12 @@ dump_node(struct edit_baton *eb, if (eb->notify_func) { char errbuf[512]; /* ### svn_strerror() magic number */ - svn_repos_notify_t *notify; - notify = svn_repos_notify_create(svn_repos_notify_warning, pool); - notify->warning = svn_repos_notify_warning_invalid_fspath; - notify->warning_str = apr_psprintf( - pool, - _("E%06d: While validating fspath '%s': %s"), - err->apr_err, path, - svn_err_best_message(err, errbuf, sizeof(errbuf))); - - eb->notify_func(eb->notify_baton, notify, pool); + notify_warning(pool, eb->notify_func, eb->notify_baton, + svn_repos_notify_warning_invalid_fspath, + _("E%06d: While validating fspath '%s': %s"), + err->apr_err, path, + svn_err_best_message(err, errbuf, sizeof(errbuf))); } /* Return the error in addition to notifying about it. */ @@ -323,15 +1128,14 @@ dump_node(struct edit_baton *eb, } /* Write out metadata headers for this file node. */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - path)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, path); if (kind == svn_node_file) - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file"); else if (kind == svn_node_dir) - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Remove leading slashes from copyfrom paths. */ if (cmp_path) @@ -344,10 +1148,16 @@ dump_node(struct edit_baton *eb, compare_rev = cmp_rev; } - if (action == svn_node_action_change) + switch (action) { - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); + case svn_node_action_change: + if (eb->path_tracker) + SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool), + apr_psprintf(pool, _("Change invalid path '%s' in r%ld"), + path, eb->current_rev)); + + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change"); /* either the text or props changed, or possibly both. */ SVN_ERR(svn_fs_revision_root(&compare_root, @@ -361,58 +1171,83 @@ dump_node(struct edit_baton *eb, SVN_ERR(svn_fs_contents_changed(&must_dump_text, compare_root, compare_path, eb->fs_root, path, pool)); - } - else if (action == svn_node_action_replace) - { + break; + + case svn_node_action_delete: + if (eb->path_tracker) + { + SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool), + apr_psprintf(pool, _("Deleting invalid path '%s' in r%ld"), + path, eb->current_rev)); + tracker_path_delete(eb->path_tracker, path); + } + + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); + + /* we can leave this routine quietly now, don't need to dump + any content. */ + must_dump_text = FALSE; + must_dump_props = FALSE; + break; + + case svn_node_action_replace: + if (eb->path_tracker) + SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, + svn_node_unknown, pool), + apr_psprintf(pool, + _("Replacing non-existent path '%s' in r%ld"), + path, eb->current_rev)); + if (! is_copy) { + if (eb->path_tracker) + tracker_path_replace(eb->path_tracker, path); + /* a simple delete+add, implied by a single 'replace' action. */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION - ": replace\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace"); /* definitely need to dump all content for a replace. */ if (kind == svn_node_file) must_dump_text = TRUE; must_dump_props = TRUE; + break; } else { /* more complex: delete original, then add-with-history. */ + /* ### Why not write a 'replace' record? Don't know. */ - /* the path & kind headers have already been printed; just - add a delete action, and end the current record.*/ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION - ": delete\n\n")); + if (eb->path_tracker) + { + tracker_path_delete(eb->path_tracker, path); + } - /* recurse: print an additional add-with-history record. */ - SVN_ERR(dump_node(eb, path, kind, svn_node_action_add, - is_copy, compare_path, compare_rev, pool)); + /* ### Unusually, we end this 'delete' node record with only a single + blank line after the header block -- no extra blank line. */ + SVN_ERR(dump_node_delete(eb->stream, path, pool)); - /* we can leave this routine quietly now, don't need to dump - any content; that was already done in the second record. */ - must_dump_text = FALSE; - must_dump_props = FALSE; + /* The remaining action is a non-replacing add-with-history */ + /* action = svn_node_action_add; */ } - } - else if (action == svn_node_action_delete) - { - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); + /* FALL THROUGH to 'add' */ - /* we can leave this routine quietly now, don't need to dump - any content. */ - must_dump_text = FALSE; - must_dump_props = FALSE; - } - else if (action == svn_node_action_add) - { - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + case svn_node_action_add: + if (eb->path_tracker) + SVN_ERR_W(node_must_not_exist(eb, path, eb->current_rev, pool), + apr_psprintf(pool, + _("Adding already existing path '%s' in r%ld"), + path, eb->current_rev)); + + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); if (! is_copy) { + if (eb->path_tracker) + tracker_path_add(eb->path_tracker, path); + /* Dump all contents for a simple 'add'. */ if (kind == svn_node_file) must_dump_text = TRUE; @@ -420,32 +1255,37 @@ dump_node(struct edit_baton *eb, } else { + if (eb->path_tracker) + { + SVN_ERR_W(node_must_exist(eb, compare_path, compare_rev, + kind, pool), + apr_psprintf(pool, + _("Copying from invalid path to " + "'%s' in r%ld"), + path, eb->current_rev)); + tracker_path_copy(eb->path_tracker, path, compare_path, + compare_rev); + } + if (!eb->verify && cmp_rev < eb->oldest_dumped_rev && eb->notify_func) { - svn_repos_notify_t *notify = - svn_repos_notify_create(svn_repos_notify_warning, pool); - - notify->warning = svn_repos_notify_warning_found_old_reference; - notify->warning_str = apr_psprintf( - pool, - _("Referencing data in revision %ld," - " which is older than the oldest" - " dumped revision (r%ld). Loading this dump" - " into an empty repository" - " will fail."), - cmp_rev, eb->oldest_dumped_rev); + notify_warning(pool, eb->notify_func, eb->notify_baton, + svn_repos_notify_warning_found_old_reference, + _("Referencing data in revision %ld," + " which is older than the oldest" + " dumped revision (r%ld). Loading this dump" + " into an empty repository" + " will fail."), + cmp_rev, eb->oldest_dumped_rev); if (eb->found_old_reference) *eb->found_old_reference = TRUE; - eb->notify_func(eb->notify_baton, notify, pool); } - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV - ": %ld\n" - SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH - ": %s\n", - cmp_rev, cmp_path)); + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", cmp_rev); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, cmp_path); SVN_ERR(svn_fs_revision_root(&compare_root, svn_fs_root_fs(eb->fs_root), @@ -469,20 +1309,19 @@ dump_node(struct edit_baton *eb, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5, hex_digest); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1, hex_digest); } } + break; } if ((! must_dump_text) && (! must_dump_props)) @@ -492,8 +1331,9 @@ dump_node(struct edit_baton *eb, then our dumpstream format demands that at a *minimum*, we see a lone "PROPS-END" as a divider between text and props content within the content-block. */ - len = 2; - return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */ + SVN_ERR(svn_repos__dump_headers(eb->stream, headers, pool)); + len = 1; + return svn_stream_write(eb->stream, "\n", &len); /* ### needed? */ } /*** Start prepping content to dump... ***/ @@ -504,7 +1344,6 @@ dump_node(struct edit_baton *eb, if (must_dump_props) { apr_hash_t *prophash, *oldhash = NULL; - apr_size_t proplen; svn_stream_t *propstream; SVN_ERR(svn_fs_node_proplist(&prophash, eb->fs_root, path, pool)); @@ -528,14 +1367,42 @@ dump_node(struct edit_baton *eb, } } + /* If we're checking UCS normalization, also parse any changed + mergeinfo and warn about denormalized paths and name + collisions there. */ + if (eb->verify && eb->check_normalization && eb->notify_func) + { + /* N.B.: This hash lookup happens only once; the conditions + for verifying historic mergeinfo references and checking + UCS normalization are mutually exclusive. */ + svn_string_t *mergeinfo_str = svn_hash_gets(prophash, + SVN_PROP_MERGEINFO); + if (mergeinfo_str) + { + svn_string_t *oldinfo_str = NULL; + if (compare_root) + { + SVN_ERR(svn_fs_node_proplist(&oldhash, + compare_root, compare_path, + pool)); + oldinfo_str = svn_hash_gets(oldhash, SVN_PROP_MERGEINFO); + } + SVN_ERR(check_mergeinfo_normalization( + path, mergeinfo_str->data, + (oldinfo_str ? oldinfo_str->data : NULL), + eb->notify_func, eb->notify_baton, pool)); + } + } + if (eb->use_deltas && compare_root) { /* Fetch the old property hash to diff against and output a header saying that our property contents are a delta. */ - SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path, - pool)); - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_PROP_DELTA ": true\n")); + if (!oldhash) /* May have been set for normalization check */ + SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path, + pool)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true"); } else oldhash = apr_hash_make(pool); @@ -544,11 +1411,6 @@ dump_node(struct edit_baton *eb, SVN_ERR(svn_hash_write_incremental(prophash, oldhash, propstream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(propstream)); - proplen = propstring->len; - content_length += proplen; - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", proplen)); } /* If we are supposed to dump text, write out a text length header @@ -557,7 +1419,6 @@ dump_node(struct edit_baton *eb, { svn_checksum_t *checksum; const char *hex_digest; - svn_filesize_t textlen; if (eb->use_deltas) { @@ -566,8 +1427,8 @@ dump_node(struct edit_baton *eb, saying our text contents are a delta. */ SVN_ERR(store_delta(&delta_file, &textlen, compare_root, compare_path, eb->fs_root, path, pool)); - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_TEXT_DELTA ": true\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true"); if (compare_root) { @@ -576,18 +1437,16 @@ dump_node(struct edit_baton *eb, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, hex_digest); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1, hex_digest); } } else @@ -596,42 +1455,30 @@ dump_node(struct edit_baton *eb, SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool)); } - content_length += textlen; - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH - ": %" SVN_FILESIZE_T_FMT "\n", textlen)); - SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, hex_digest); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1 - ": %s\n", hex_digest)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1, hex_digest); } /* 'Content-length:' is the last header before we dump the content, and is the sum of the text and prop contents lengths. We write this only for the benefit of non-Subversion RFC-822 parsers. */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" SVN_FILESIZE_T_FMT "\n\n", - content_length)); - - /* Dump property content if we're supposed to do so. */ - if (must_dump_props) - { - len = propstring->len; - SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len)); - } + SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, + must_dump_props ? propstring : NULL, + must_dump_text, + must_dump_text ? textlen : 0, + TRUE /*content_length_always*/, + pool)); /* Dump text content */ if (must_dump_text && (kind == svn_node_file)) @@ -663,7 +1510,7 @@ open_root(void *edit_baton, void **root_baton) { *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, FALSE, pool); + edit_baton, NULL, pool); return SVN_NO_ERROR; } @@ -694,13 +1541,13 @@ add_directory(const char *path, { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; - void *val; + void *was_deleted; svn_boolean_t is_copy = FALSE; struct dir_baton *new_db - = make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pool); + = make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect an add-with-history. */ is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); @@ -708,16 +1555,23 @@ add_directory(const char *path, /* Dump the node. */ SVN_ERR(dump_node(eb, path, svn_node_dir, - val ? svn_node_action_replace : svn_node_action_add, + was_deleted ? svn_node_action_replace : svn_node_action_add, is_copy, is_copy ? copyfrom_path : NULL, is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, pool)); - if (val) + if (was_deleted) /* Delete the path, it's now been dumped. */ svn_hash_sets(pb->deleted_entries, path, NULL); + /* Check for normalized name clashes, but only if this is actually a + new name in the parent, not a replacement. */ + if (!was_deleted && eb->verify && eb->check_normalization && eb->notify_func) + { + pb->check_name_collision = TRUE; + } + new_db->written_out = TRUE; *child_baton = new_db; @@ -747,7 +1601,7 @@ open_directory(const char *path, cmp_rev = pb->cmp_rev; } - new_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, FALSE, pool); + new_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, pool); *child_baton = new_db; return SVN_NO_ERROR; } @@ -799,11 +1653,11 @@ add_file(const char *path, { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; - void *val; + void *was_deleted; svn_boolean_t is_copy = FALSE; /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect add-with-history. */ is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); @@ -811,16 +1665,23 @@ add_file(const char *path, /* Dump the node. */ SVN_ERR(dump_node(eb, path, svn_node_file, - val ? svn_node_action_replace : svn_node_action_add, + was_deleted ? svn_node_action_replace : svn_node_action_add, is_copy, is_copy ? copyfrom_path : NULL, is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, pool)); - if (val) + if (was_deleted) /* delete the path, it's now been dumped. */ svn_hash_sets(pb->deleted_entries, path, NULL); + /* Check for normalized name clashes, but only if this is actually a + new name in the parent, not a replacement. */ + if (!was_deleted && eb->verify && eb->check_normalization && eb->notify_func) + { + pb->check_name_collision = TRUE; + } + *file_baton = NULL; /* muhahahaha */ return SVN_NO_ERROR; } @@ -867,11 +1728,16 @@ change_dir_prop(void *parent_baton, /* This function is what distinguishes between a directory that is opened to merely get somewhere, vs. one that is opened because it - *actually* changed by itself. */ + *actually* changed by itself. + + Instead of recording the prop changes here, we just use this method + to trigger writing the node; dump_node() finds all the changes. */ if (! db->written_out) { SVN_ERR(dump_node(eb, db->path, svn_node_dir, svn_node_action_change, + /* ### We pass is_copy=FALSE; this might be wrong + but the parameter isn't used when action=change. */ FALSE, db->cmp_path, db->cmp_rev, pool)); db->written_out = TRUE; } @@ -984,6 +1850,7 @@ get_dump_editor(const svn_delta_editor_t **editor, svn_revnum_t oldest_dumped_rev, svn_boolean_t use_deltas, svn_boolean_t verify, + svn_boolean_t check_normalization, apr_pool_t *pool) { /* Allocate an edit baton to be stored in every directory baton. @@ -999,16 +1866,24 @@ get_dump_editor(const svn_delta_editor_t **editor, eb->notify_func = notify_func; eb->notify_baton = notify_baton; eb->oldest_dumped_rev = oldest_dumped_rev; - eb->bufsize = sizeof(eb->buffer); eb->path = apr_pstrdup(pool, root_path); SVN_ERR(svn_fs_revision_root(&(eb->fs_root), fs, to_rev, pool)); eb->fs = fs; eb->current_rev = to_rev; eb->use_deltas = use_deltas; eb->verify = verify; + eb->check_normalization = check_normalization; eb->found_old_reference = found_old_reference; eb->found_old_mergeinfo = found_old_mergeinfo; + /* In non-verification mode, we will allow anything to be dumped because + it might be an incremental dump with possible manual intervention. + Also, this might be the last resort when it comes to data recovery. + + Else, make sure that all paths exists at their respective revisions. + */ + eb->path_tracker = verify ? tracker_create(to_rev, pool) : NULL; + /* Set up the editor. */ dump_editor->open_root = open_root; dump_editor->delete_entry = delete_entry; @@ -1051,15 +1926,10 @@ write_revision_record(svn_stream_t *stream, svn_revnum_t rev, apr_pool_t *pool) { - apr_size_t len; apr_hash_t *props; - svn_stringbuf_t *encoded_prophash; apr_time_t timetemp; svn_string_t *datevalue; - svn_stream_t *propstream; - /* Read the revision props even if we're aren't going to dump - them for verification purposes */ SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool)); /* Run revision date properties through the time conversion to @@ -1074,33 +1944,10 @@ write_revision_record(svn_stream_t *stream, svn_hash_sets(props, SVN_PROP_REVISION_DATE, datevalue); } - encoded_prophash = svn_stringbuf_create_ensure(0, pool); - propstream = svn_stream_from_stringbuf(encoded_prophash, pool); - SVN_ERR(svn_hash_write2(props, propstream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(propstream)); - - /* ### someday write a revision-content-checksum */ - - SVN_ERR(svn_stream_printf(stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", rev)); - SVN_ERR(svn_stream_printf(stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", - encoded_prophash->len)); - - /* Write out a regular Content-length header for the benefit of - non-Subversion RFC-822 parsers. */ - SVN_ERR(svn_stream_printf(stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - encoded_prophash->len)); - - len = encoded_prophash->len; - SVN_ERR(svn_stream_write(stream, encoded_prophash->data, &len)); - - len = 1; - return svn_stream_write(stream, "\n", &len); + SVN_ERR(svn_repos__dump_revision_record(stream, rev, NULL, props, + TRUE /*props_section_always*/, + pool)); + return SVN_NO_ERROR; } @@ -1121,7 +1968,7 @@ svn_repos_dump_fs3(svn_repos_t *repos, { const svn_delta_editor_t *dump_editor; void *dump_edit_baton = NULL; - svn_revnum_t i; + svn_revnum_t rev; svn_fs_t *fs = svn_repos_fs(repos); apr_pool_t *subpool = svn_pool_create(pool); svn_revnum_t youngest; @@ -1153,10 +2000,6 @@ svn_repos_dump_fs3(svn_repos_t *repos, _("End revision %ld is invalid " "(youngest revision is %ld)"), end_rev, youngest); - if ((start_rev == 0) && incremental) - incremental = FALSE; /* revision 0 looks the same regardless of - whether or not this is an incremental - dump, so just simplify things. */ /* Write out the UUID. */ SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); @@ -1180,10 +2023,9 @@ svn_repos_dump_fs3(svn_repos_t *repos, notify = svn_repos_notify_create(svn_repos_notify_dump_rev_end, pool); - /* Main loop: we're going to dump revision i. */ - for (i = start_rev; i <= end_rev; i++) + /* Main loop: we're going to dump revision REV. */ + for (rev = start_rev; rev <= end_rev; rev++) { - svn_revnum_t from_rev, to_rev; svn_fs_root_t *to_root; svn_boolean_t use_deltas_for_rev; @@ -1193,56 +2035,36 @@ svn_repos_dump_fs3(svn_repos_t *repos, if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - /* Special-case the initial revision dump: it needs to contain - *all* nodes, because it's the foundation of all future - revisions in the dumpfile. */ - if ((i == start_rev) && (! incremental)) - { - /* Special-special-case a dump of revision 0. */ - if (i == 0) - { - /* Just write out the one revision 0 record and move on. - The parser might want to use its properties. */ - SVN_ERR(write_revision_record(stream, fs, 0, subpool)); - to_rev = 0; - goto loop_end; - } - - /* Compare START_REV to revision 0, so that everything - appears to be added. */ - from_rev = 0; - to_rev = i; - } - else - { - /* In the normal case, we want to compare consecutive revs. */ - from_rev = i - 1; - to_rev = i; - } - /* Write the revision record. */ - SVN_ERR(write_revision_record(stream, fs, to_rev, subpool)); + SVN_ERR(write_revision_record(stream, fs, rev, subpool)); + + /* When dumping revision 0, we just write out the revision record. + The parser might want to use its properties. */ + if (rev == 0) + goto loop_end; /* Fetch the editor which dumps nodes to a file. Regardless of what we've been told, don't use deltas for the first rev of a non-incremental dump. */ - use_deltas_for_rev = use_deltas && (incremental || i != start_rev); - SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, to_rev, + use_deltas_for_rev = use_deltas && (incremental || rev != start_rev); + SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, rev, "", stream, &found_old_reference, &found_old_mergeinfo, NULL, notify_func, notify_baton, - start_rev, use_deltas_for_rev, FALSE, subpool)); + start_rev, use_deltas_for_rev, FALSE, FALSE, + subpool)); /* Drive the editor in one way or another. */ - SVN_ERR(svn_fs_revision_root(&to_root, fs, to_rev, subpool)); + SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool)); /* If this is the first revision of a non-incremental dump, we're in for a full tree dump. Otherwise, we want to simply replay the revision. */ - if ((i == start_rev) && (! incremental)) + if ((rev == start_rev) && (! incremental)) { + /* Compare against revision 0, so everything appears to be added. */ svn_fs_root_t *from_root; - SVN_ERR(svn_fs_revision_root(&from_root, fs, from_rev, subpool)); + SVN_ERR(svn_fs_revision_root(&from_root, fs, 0, subpool)); SVN_ERR(svn_repos_dir_delta2(from_root, "", "", to_root, "", dump_editor, dump_edit_baton, @@ -1256,6 +2078,7 @@ svn_repos_dump_fs3(svn_repos_t *repos, } else { + /* The normal case: compare consecutive revs. */ SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE, dump_editor, dump_edit_baton, NULL, NULL, subpool)); @@ -1268,7 +2091,7 @@ svn_repos_dump_fs3(svn_repos_t *repos, loop_end: if (notify_func) { - notify->revision = to_rev; + notify->revision = rev; notify_func(notify_baton, notify, subpool); } } @@ -1285,28 +2108,24 @@ svn_repos_dump_fs3(svn_repos_t *repos, if (found_old_reference) { - notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); - - notify->warning = svn_repos_notify_warning_found_old_reference; - notify->warning_str = _("The range of revisions dumped " - "contained references to " - "copy sources outside that " - "range."); - notify_func(notify_baton, notify, subpool); + notify_warning(subpool, notify_func, notify_baton, + svn_repos_notify_warning_found_old_reference, + _("The range of revisions dumped " + "contained references to " + "copy sources outside that " + "range.")); } /* Ditto if we issued any warnings about old revisions referenced in dumped mergeinfo. */ if (found_old_mergeinfo) { - notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); - - notify->warning = svn_repos_notify_warning_found_old_mergeinfo; - notify->warning_str = _("The range of revisions dumped " - "contained mergeinfo " - "which reference revisions outside " - "that range."); - notify_func(notify_baton, notify, subpool); + notify_warning(subpool, notify_func, notify_baton, + svn_repos_notify_warning_found_old_mergeinfo, + _("The range of revisions dumped " + "contained mergeinfo " + "which reference revisions outside " + "that range.")); } } @@ -1341,23 +2160,32 @@ verify_directory_entry(void *baton, const void *key, apr_ssize_t klen, { struct dir_baton *db = baton; svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val; - char *path = svn_relpath_join(db->path, (const char *)key, pool); - apr_hash_t *dirents; - svn_filesize_t len; + char *path; + svn_boolean_t right_kind; + + path = svn_relpath_join(db->path, (const char *)key, pool); /* since we can't access the directory entries directly by their ID, we need to navigate from the FS_ROOT to them (relatively expensive - because we may start at a never rev than the last change to node). */ + because we may start at a never rev than the last change to node). + We check that the node kind stored in the noderev matches the dir + entry. This also ensures that all entries point to valid noderevs. + */ switch (dirent->kind) { case svn_node_dir: - /* Getting this directory's contents is enough to ensure that our - link to it is correct. */ - SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root, path, pool)); + SVN_ERR(svn_fs_is_dir(&right_kind, db->edit_baton->fs_root, path, pool)); + if (!right_kind) + return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("Node '%s' is not a directory."), + path); + break; case svn_node_file: - /* Getting this file's size is enough to ensure that our link to it - is correct. */ - SVN_ERR(svn_fs_file_length(&len, db->edit_baton->fs_root, path, pool)); + SVN_ERR(svn_fs_is_file(&right_kind, db->edit_baton->fs_root, path, pool)); + if (!right_kind) + return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("Node '%s' is not a file."), + path); break; default: return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, @@ -1368,9 +2196,54 @@ verify_directory_entry(void *baton, const void *key, apr_ssize_t klen, return SVN_NO_ERROR; } +/* Baton used by the check_name_collision hash iterator. */ +struct check_name_collision_baton +{ + struct dir_baton *dir_baton; + apr_hash_t *normalized; + svn_membuf_t buffer; +}; + +/* Scan the directory and report all entry names that differ only in + Unicode character representation. */ static svn_error_t * -verify_close_directory(void *dir_baton, - apr_pool_t *pool) +check_name_collision(void *baton, const void *key, apr_ssize_t klen, + void *val, apr_pool_t *iterpool) +{ + struct check_name_collision_baton *const cb = baton; + const char *name; + const char *found; + + SVN_ERR(svn_utf__normalize(&name, key, klen, &cb->buffer)); + + found = svn_hash_gets(cb->normalized, name); + if (!found) + svn_hash_sets(cb->normalized, apr_pstrdup(cb->buffer.pool, name), + normalized_unique); + else if (found == normalized_collision) + /* Skip already reported collision */; + else + { + struct dir_baton *const db = cb->dir_baton; + struct edit_baton *const eb = db->edit_baton; + const char* normpath; + + svn_hash_sets(cb->normalized, apr_pstrdup(cb->buffer.pool, name), + normalized_collision); + + SVN_ERR(svn_utf__normalize( + &normpath, svn_relpath_join(db->path, name, iterpool), + SVN_UTF__UNKNOWN_LENGTH, &cb->buffer)); + notify_warning(iterpool, eb->notify_func, eb->notify_baton, + svn_repos_notify_warning_name_collision, + _("Duplicate representation of path '%s'"), normpath); + } + return SVN_NO_ERROR; +} + + +static svn_error_t * +verify_close_directory(void *dir_baton, apr_pool_t *pool) { struct dir_baton *db = dir_baton; apr_hash_t *dirents; @@ -1378,11 +2251,72 @@ verify_close_directory(void *dir_baton, db->path, pool)); SVN_ERR(svn_iter_apr_hash(NULL, dirents, verify_directory_entry, dir_baton, pool)); + + if (db->check_name_collision) + { + struct check_name_collision_baton check_baton; + check_baton.dir_baton = db; + check_baton.normalized = apr_hash_make(pool); + svn_membuf__create(&check_baton.buffer, 0, pool); + SVN_ERR(svn_iter_apr_hash(NULL, dirents, check_name_collision, + &check_baton, pool)); + } + return close_directory(dir_baton, pool); } +/* Verify revision REV in file system FS. */ +static svn_error_t * +verify_one_revision(svn_fs_t *fs, + svn_revnum_t rev, + svn_repos_notify_func_t notify_func, + void *notify_baton, + svn_revnum_t start_rev, + svn_boolean_t check_normalization, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + const svn_delta_editor_t *dump_editor; + void *dump_edit_baton; + svn_fs_root_t *to_root; + apr_hash_t *props; + const svn_delta_editor_t *cancel_editor; + void *cancel_edit_baton; + + /* Get cancellable dump editor, but with our close_directory handler.*/ + SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, + fs, rev, "", + svn_stream_empty(scratch_pool), + NULL, NULL, + verify_close_directory, + notify_func, notify_baton, + start_rev, + FALSE, TRUE, /* use_deltas, verify */ + check_normalization, + scratch_pool)); + SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, + dump_editor, dump_edit_baton, + &cancel_editor, + &cancel_edit_baton, + scratch_pool)); + SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, scratch_pool)); + SVN_ERR(svn_fs_verify_root(to_root, scratch_pool)); + SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE, + cancel_editor, cancel_edit_baton, + NULL, NULL, scratch_pool)); + + /* While our editor close_edit implementation is a no-op, we still + do this for completeness. */ + SVN_ERR(cancel_editor->close_edit(cancel_edit_baton, scratch_pool)); + + SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, scratch_pool)); + + return SVN_NO_ERROR; +} + /* Baton type used for forwarding notifications from FS API to REPOS API. */ -struct verify_fs2_notify_func_baton_t +struct verify_fs_notify_func_baton_t { /* notification function to call (must not be NULL) */ svn_repos_notify_func_t notify_func; @@ -1396,23 +2330,53 @@ struct verify_fs2_notify_func_baton_t /* Forward the notification to BATON. */ static void -verify_fs2_notify_func(svn_revnum_t revision, +verify_fs_notify_func(svn_revnum_t revision, void *baton, apr_pool_t *pool) { - struct verify_fs2_notify_func_baton_t *notify_baton = baton; + struct verify_fs_notify_func_baton_t *notify_baton = baton; notify_baton->notify->revision = revision; notify_baton->notify_func(notify_baton->notify_baton, notify_baton->notify, pool); } +static svn_error_t * +report_error(svn_revnum_t revision, + svn_error_t *verify_err, + svn_repos_verify_callback_t verify_callback, + void *verify_baton, + apr_pool_t *pool) +{ + if (verify_callback) + { + svn_error_t *cb_err; + + /* The caller provided us with a callback, so make him responsible + for what's going to happen with the error. */ + cb_err = verify_callback(verify_baton, revision, verify_err, pool); + svn_error_clear(verify_err); + SVN_ERR(cb_err); + + return SVN_NO_ERROR; + } + else + { + /* No callback -- no second guessing. Just return the error. */ + return svn_error_trace(verify_err); + } +} + svn_error_t * -svn_repos_verify_fs2(svn_repos_t *repos, +svn_repos_verify_fs3(svn_repos_t *repos, svn_revnum_t start_rev, svn_revnum_t end_rev, + svn_boolean_t check_normalization, + svn_boolean_t metadata_only, svn_repos_notify_func_t notify_func, void *notify_baton, + svn_repos_verify_callback_t verify_callback, + void *verify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) @@ -1423,7 +2387,8 @@ svn_repos_verify_fs2(svn_repos_t *repos, apr_pool_t *iterpool = svn_pool_create(pool); svn_repos_notify_t *notify; svn_fs_progress_notify_func_t verify_notify = NULL; - struct verify_fs2_notify_func_baton_t *verify_notify_baton = NULL; + struct verify_fs_notify_func_baton_t *verify_notify_baton = NULL; + svn_error_t *err; /* Determine the current youngest revision of the filesystem. */ SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); @@ -1450,10 +2415,9 @@ svn_repos_verify_fs2(svn_repos_t *repos, forwarding structure for notifications from inside svn_fs_verify(). */ if (notify_func) { - notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end, - pool); + notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end, pool); - verify_notify = verify_fs2_notify_func; + verify_notify = verify_fs_notify_func; verify_notify_baton = apr_palloc(pool, sizeof(*verify_notify_baton)); verify_notify_baton->notify_func = notify_func; verify_notify_baton->notify_baton = notify_baton; @@ -1462,56 +2426,48 @@ svn_repos_verify_fs2(svn_repos_t *repos, } /* Verify global metadata and backend-specific data first. */ - SVN_ERR(svn_fs_verify(svn_fs_path(fs, pool), svn_fs_config(fs, pool), - start_rev, end_rev, - verify_notify, verify_notify_baton, - cancel_func, cancel_baton, pool)); + err = svn_fs_verify(svn_fs_path(fs, pool), svn_fs_config(fs, pool), + start_rev, end_rev, + verify_notify, verify_notify_baton, + cancel_func, cancel_baton, pool); - for (rev = start_rev; rev <= end_rev; rev++) + if (err && err->apr_err == SVN_ERR_CANCELLED) { - const svn_delta_editor_t *dump_editor; - void *dump_edit_baton; - const svn_delta_editor_t *cancel_editor; - void *cancel_edit_baton; - svn_fs_root_t *to_root; - apr_hash_t *props; + return svn_error_trace(err); + } + else if (err) + { + SVN_ERR(report_error(SVN_INVALID_REVNUM, err, verify_callback, + verify_baton, iterpool)); + } - svn_pool_clear(iterpool); + if (!metadata_only) + for (rev = start_rev; rev <= end_rev; rev++) + { + svn_pool_clear(iterpool); - /* Get cancellable dump editor, but with our close_directory handler. */ - SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, - fs, rev, "", - svn_stream_empty(iterpool), - NULL, NULL, - verify_close_directory, - notify_func, notify_baton, - start_rev, - FALSE, TRUE, /* use_deltas, verify */ - iterpool)); - SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, - dump_editor, dump_edit_baton, - &cancel_editor, - &cancel_edit_baton, - iterpool)); - - SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, iterpool)); - SVN_ERR(svn_fs_verify_root(to_root, iterpool)); - - SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE, - cancel_editor, cancel_edit_baton, - NULL, NULL, iterpool)); - /* While our editor close_edit implementation is a no-op, we still - do this for completeness. */ - SVN_ERR(cancel_editor->close_edit(cancel_edit_baton, iterpool)); - - SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, iterpool)); + /* Wrapper function to catch the possible errors. */ + err = verify_one_revision(fs, rev, notify_func, notify_baton, + start_rev, check_normalization, + cancel_func, cancel_baton, + iterpool); - if (notify_func) - { - notify->revision = rev; - notify_func(notify_baton, notify, iterpool); - } - } + if (err && err->apr_err == SVN_ERR_CANCELLED) + { + return svn_error_trace(err); + } + else if (err) + { + SVN_ERR(report_error(rev, err, verify_callback, verify_baton, + iterpool)); + } + else if (notify_func) + { + /* Tell the caller that we're done with this revision. */ + notify->revision = rev; + notify_func(notify_baton, notify, iterpool); + } + } /* We're done. */ if (notify_func) @@ -1520,7 +2476,6 @@ svn_repos_verify_fs2(svn_repos_t *repos, notify_func(notify_baton, notify, iterpool); } - /* Per-backend verification. */ svn_pool_destroy(iterpool); return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_repos/fs-wrap.c b/contrib/subversion/subversion/libsvn_repos/fs-wrap.c index 006b2861e..b46cda650 100644 --- a/contrib/subversion/subversion/libsvn_repos/fs-wrap.c +++ b/contrib/subversion/subversion/libsvn_repos/fs-wrap.c @@ -36,6 +36,7 @@ #include "repos.h" #include "svn_private_config.h" #include "private/svn_repos_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_utf_private.h" #include "private/svn_fspath.h" @@ -66,13 +67,14 @@ svn_repos_fs_commit_txn(const char **conflict_p, SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool)); - /* Remove any ephemeral transaction properties. */ + /* Remove any ephemeral transaction properties. If the commit fails + we will attempt to restore the properties but if that fails, or + the process is killed, the properties will be lost. */ SVN_ERR(svn_fs_txn_proplist(&props, txn, pool)); iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { - const void *key; - apr_hash_this(hi, &key, NULL, NULL); + const char *key = apr_hash_this_key(hi); svn_pool_clear(iterpool); @@ -87,7 +89,24 @@ svn_repos_fs_commit_txn(const char **conflict_p, /* Commit. */ err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool); if (! SVN_IS_VALID_REVNUM(*new_rev)) - return err; + { + /* The commit failed, try to restore the ephemeral properties. */ + iterpool = svn_pool_create(pool); + for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) + { + const char *key = apr_hash_this_key(hi); + svn_string_t *val = apr_hash_this_val(hi); + + svn_pool_clear(iterpool); + + if (strncmp(key, SVN_PROP_TXN_PREFIX, + (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0) + svn_error_clear(svn_fs_change_txn_prop(txn, key, val, iterpool)); + } + svn_pool_destroy(iterpool); + + return err; + } /* Run post-commit hooks. */ if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env, @@ -148,7 +167,7 @@ svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p, if (err) return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool)); - /* We have API promise that *TXN_P is unaffected on faulure. */ + /* We have API promise that *TXN_P is unaffected on failure. */ *txn_p = txn; return SVN_NO_ERROR; } @@ -488,115 +507,319 @@ svn_repos_fs_revision_proplist(apr_hash_t **table_p, return SVN_NO_ERROR; } +struct lock_many_baton_t { + svn_boolean_t need_lock; + apr_array_header_t *paths; + svn_fs_lock_callback_t lock_callback; + void *lock_baton; + svn_error_t *cb_err; + apr_pool_t *pool; +}; + +/* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock_many + and svn_repos_fs_unlock_many to record the paths for use by post- + hooks, forward to the supplied callback and record any callback + error. */ +static svn_error_t * +lock_many_cb(void *lock_baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + struct lock_many_baton_t *b = lock_baton; + + if (!b->cb_err && b->lock_callback) + b->cb_err = b->lock_callback(b->lock_baton, path, lock, fs_err, pool); + + if ((b->need_lock && lock) || (!b->need_lock && !fs_err)) + APR_ARRAY_PUSH(b->paths, const char *) = apr_pstrdup(b->pool, path); + + return SVN_NO_ERROR; +} + svn_error_t * -svn_repos_fs_lock(svn_lock_t **lock, - svn_repos_t *repos, - const char *path, - const char *token, - const char *comment, - svn_boolean_t is_dav_comment, - apr_time_t expiration_date, - svn_revnum_t current_rev, - svn_boolean_t steal_lock, - apr_pool_t *pool) +svn_repos_fs_lock_many(svn_repos_t *repos, + apr_hash_t *targets, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_boolean_t steal_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_error_t *err; + svn_error_t *err, *cb_err = SVN_NO_ERROR; svn_fs_access_t *access_ctx = NULL; const char *username = NULL; - const char *new_token; - apr_array_header_t *paths; apr_hash_t *hooks_env; + apr_hash_t *pre_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + struct lock_many_baton_t baton; + + if (!apr_hash_count(targets)) + return SVN_NO_ERROR; /* Parse the hooks-env file (if any). */ SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, - pool, pool)); - - /* Setup an array of paths in anticipation of the ra layers handling - multiple locks in one request (1.3 most likely). This is only - used by svn_repos__hooks_post_lock. */ - paths = apr_array_make(pool, 1, sizeof(const char *)); - APR_ARRAY_PUSH(paths, const char *) = path; + scratch_pool, scratch_pool)); SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); if (access_ctx) SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); if (! username) - return svn_error_createf + return svn_error_create (SVN_ERR_FS_NO_USER, NULL, - "Cannot lock path '%s', no authenticated username available.", path); + "Cannot lock path, no authenticated username available."); /* Run pre-lock hook. This could throw error, preventing - svn_fs_lock() from happening. */ - SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path, - username, comment, steal_lock, pool)); - if (*new_token) - token = new_token; - - /* Lock. */ - SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment, - expiration_date, current_rev, steal_lock, pool)); - - /* Run post-lock hook. */ - if ((err = svn_repos__hooks_post_lock(repos, hooks_env, - paths, username, pool))) - return svn_error_create - (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err, - "Lock succeeded, but post-lock hook failed"); + svn_fs_lock2() from happening for that path. */ + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *new_token; + svn_fs_lock_target_t *target; + const char *path = apr_hash_this_key(hi); - return SVN_NO_ERROR; + svn_pool_clear(iterpool); + + err = svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path, + username, comment, steal_lock, iterpool); + if (err) + { + if (!cb_err && lock_callback) + cb_err = lock_callback(lock_baton, path, NULL, err, iterpool); + svn_error_clear(err); + + continue; + } + + target = apr_hash_this_val(hi); + if (*new_token) + svn_fs_lock_target_set_token(target, new_token); + svn_hash_sets(pre_targets, path, target); + } + + if (!apr_hash_count(pre_targets)) + return svn_error_trace(cb_err); + + baton.need_lock = TRUE; + baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets), + sizeof(const char *)); + baton.lock_callback = lock_callback; + baton.lock_baton = lock_baton; + baton.cb_err = cb_err; + baton.pool = scratch_pool; + + err = svn_fs_lock_many(repos->fs, pre_targets, comment, + is_dav_comment, expiration_date, steal_lock, + lock_many_cb, &baton, result_pool, iterpool); + + /* If there are locks run the post-lock even if there is an error. */ + if (baton.paths->nelts) + { + svn_error_t *perr = svn_repos__hooks_post_lock(repos, hooks_env, + baton.paths, username, + iterpool); + if (perr) + { + perr = svn_error_create(SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, perr, + _("Locking succeeded, but post-lock hook failed")); + err = svn_error_compose_create(err, perr); + } + } + + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; + + return svn_error_trace(err); } +struct lock_baton_t { + const svn_lock_t *lock; + svn_error_t *fs_err; +}; + +/* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock and + svn_repos_fs_unlock to record the lock and error from + svn_repos_fs_lock_many and svn_repos_fs_unlock_many. */ +static svn_error_t * +lock_cb(void *lock_baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + struct lock_baton_t *b = lock_baton; + + b->lock = lock; + b->fs_err = svn_error_dup(fs_err); + + return SVN_NO_ERROR; +} svn_error_t * -svn_repos_fs_unlock(svn_repos_t *repos, - const char *path, - const char *token, - svn_boolean_t break_lock, - apr_pool_t *pool) +svn_repos_fs_lock(svn_lock_t **lock, + svn_repos_t *repos, + const char *path, + const char *token, + const char *comment, + svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_revnum_t current_rev, + svn_boolean_t steal_lock, + apr_pool_t *pool) { + apr_hash_t *targets = apr_hash_make(pool); + svn_fs_lock_target_t *target = svn_fs_lock_target_create(token, current_rev, + pool); svn_error_t *err; + struct lock_baton_t baton = {0}; + + svn_hash_sets(targets, path, target); + + err = svn_repos_fs_lock_many(repos, targets, comment, is_dav_comment, + expiration_date, steal_lock, lock_cb, &baton, + pool, pool); + + if (baton.lock) + *lock = (svn_lock_t*)baton.lock; + + if (err && baton.fs_err) + svn_error_compose(err, baton.fs_err); + else if (!err) + err = baton.fs_err; + + return svn_error_trace(err); +} + + +svn_error_t * +svn_repos_fs_unlock_many(svn_repos_t *repos, + apr_hash_t *targets, + svn_boolean_t break_lock, + svn_fs_lock_callback_t lock_callback, + void *lock_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err, *cb_err = SVN_NO_ERROR; svn_fs_access_t *access_ctx = NULL; const char *username = NULL; - apr_array_header_t *paths; apr_hash_t *hooks_env; + apr_hash_t *pre_targets = apr_hash_make(scratch_pool); + apr_hash_index_t *hi; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + struct lock_many_baton_t baton; + + if (!apr_hash_count(targets)) + return SVN_NO_ERROR; /* Parse the hooks-env file (if any). */ SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, - pool, pool)); - - /* Setup an array of paths in anticipation of the ra layers handling - multiple locks in one request (1.3 most likely). This is only - used by svn_repos__hooks_post_lock. */ - paths = apr_array_make(pool, 1, sizeof(const char *)); - APR_ARRAY_PUSH(paths, const char *) = path; + scratch_pool, scratch_pool)); SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); if (access_ctx) SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); if (! break_lock && ! username) - return svn_error_createf + return svn_error_create (SVN_ERR_FS_NO_USER, NULL, - _("Cannot unlock path '%s', no authenticated username available"), - path); + _("Cannot unlock, no authenticated username available")); /* Run pre-unlock hook. This could throw error, preventing - svn_fs_unlock() from happening. */ - SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token, - break_lock, pool)); + svn_fs_unlock_many() from happening for that path. */ + for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *path = apr_hash_this_key(hi); + const char *token = apr_hash_this_val(hi); - /* Unlock. */ - SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool)); + svn_pool_clear(iterpool); - /* Run post-unlock hook. */ - if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths, - username, pool))) - return svn_error_create - (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err, - _("Unlock succeeded, but post-unlock hook failed")); + err = svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token, + break_lock, iterpool); + if (err) + { + if (!cb_err && lock_callback) + cb_err = lock_callback(lock_baton, path, NULL, err, iterpool); + svn_error_clear(err); - return SVN_NO_ERROR; + continue; + } + + svn_hash_sets(pre_targets, path, token); + } + + if (!apr_hash_count(pre_targets)) + return svn_error_trace(cb_err); + + baton.need_lock = FALSE; + baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets), + sizeof(const char *)); + baton.lock_callback = lock_callback; + baton.lock_baton = lock_baton; + baton.cb_err = cb_err; + baton.pool = scratch_pool; + + err = svn_fs_unlock_many(repos->fs, pre_targets, break_lock, + lock_many_cb, &baton, result_pool, iterpool); + + /* If there are 'unlocks' run the post-unlock even if there is an error. */ + if (baton.paths->nelts) + { + svn_error_t *perr = svn_repos__hooks_post_unlock(repos, hooks_env, + baton.paths, + username, iterpool); + if (perr) + { + perr = svn_error_create(SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, perr, + _("Unlock succeeded, but post-unlock hook failed")); + err = svn_error_compose_create(err, perr); + } + } + + svn_pool_destroy(iterpool); + + if (err && cb_err) + svn_error_compose(err, cb_err); + else if (!err) + err = cb_err; + + return svn_error_trace(err); +} + +svn_error_t * +svn_repos_fs_unlock(svn_repos_t *repos, + const char *path, + const char *token, + svn_boolean_t break_lock, + apr_pool_t *pool) +{ + apr_hash_t *targets = apr_hash_make(pool); + svn_error_t *err; + struct lock_baton_t baton = {0}; + + if (!token) + token = ""; + + svn_hash_sets(targets, path, token); + + err = svn_repos_fs_unlock_many(repos, targets, break_lock, lock_cb, &baton, + pool, pool); + + if (err && baton.fs_err) + svn_error_compose(err, baton.fs_err); + else if (!err) + err = baton.fs_err; + + return svn_error_trace(err); } @@ -841,7 +1064,7 @@ svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p, apr_pstrdup(result_pool, parent_path + 1); i_props->prop_hash = parent_properties; /* Build the output array in depth-first order. */ - svn_sort__array_insert(&i_props, inherited_props, 0); + svn_sort__array_insert(inherited_props, &i_props, 0); } } } diff --git a/contrib/subversion/subversion/libsvn_repos/hooks.c b/contrib/subversion/subversion/libsvn_repos/hooks.c index 972759915..a4cc24914 100644 --- a/contrib/subversion/subversion/libsvn_repos/hooks.c +++ b/contrib/subversion/subversion/libsvn_repos/hooks.c @@ -184,8 +184,8 @@ env_from_env_hash(apr_hash_t *env_hash, for (hi = apr_hash_first(scratch_pool, env_hash); hi; hi = apr_hash_next(hi)) { *envp = apr_psprintf(result_pool, "%s=%s", - (const char *)svn__apr_hash_index_key(hi), - (const char *)svn__apr_hash_index_val(hi)); + (const char *)apr_hash_this_key(hi), + (const char *)apr_hash_this_val(hi)); envp++; } *envp = NULL; @@ -318,7 +318,7 @@ check_hook_cmd(const char *hook, svn_boolean_t *broken_link, apr_pool_t *pool) #ifdef WIN32 /* For WIN32, we need to check with file name extension(s) added. - As Windows Scripting Host (.wsf) files can accomodate (at least) + As Windows Scripting Host (.wsf) files can accommodate (at least) JavaScript (.js) and VB Script (.vbs) code, extensions for the corresponding file types need not be enumerated explicitly. */ ".exe", ".cmd", ".bat", ".wsf", /* ### Any other extensions? */ @@ -334,7 +334,7 @@ check_hook_cmd(const char *hook, svn_boolean_t *broken_link, apr_pool_t *pool) for (extn = check_extns; *extn; ++extn) { const char *const hook_path = - (**extn ? apr_pstrcat(pool, hook, *extn, (char *)NULL) : hook); + (**extn ? apr_pstrcat(pool, hook, *extn, SVN_VA_NULL) : hook); svn_node_kind_t kind; if (!(err = svn_io_check_resolved_path(hook_path, &kind, pool)) @@ -363,7 +363,7 @@ struct parse_hooks_env_option_baton { * options apply. */ const char *section; apr_hash_t *hooks_env; -} parse_hooks_env_option_baton; +}; /* An implementation of svn_config_enumerator2_t. * Set environment variable NAME to value VALUE in the environment for @@ -393,7 +393,7 @@ parse_hooks_env_option(const char *name, const char *value, struct parse_hooks_env_section_baton { svn_config_t *cfg; apr_hash_t *hooks_env; -} parse_hooks_env_section_baton; +}; /* An implementation of svn_config_section_enumerator2_t. */ static svn_boolean_t @@ -416,17 +416,25 @@ svn_repos__parse_hooks_env(apr_hash_t **hooks_env_p, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_config_t *cfg; struct parse_hooks_env_section_baton b; - if (local_abspath) { - SVN_ERR(svn_config_read3(&cfg, local_abspath, FALSE, - TRUE, TRUE, scratch_pool)); - b.cfg = cfg; + svn_node_kind_t kind; + SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); + b.hooks_env = apr_hash_make(result_pool); - (void)svn_config_enumerate_sections2(cfg, parse_hooks_env_section, &b, - scratch_pool); + + if (kind != svn_node_none) + { + svn_config_t *cfg; + SVN_ERR(svn_config_read3(&cfg, local_abspath, FALSE, + TRUE, TRUE, scratch_pool)); + b.cfg = cfg; + + (void)svn_config_enumerate_sections2(cfg, parse_hooks_env_section, + &b, scratch_pool); + } + *hooks_env_p = b.hooks_env; } else @@ -511,15 +519,22 @@ lock_token_content(apr_file_t **handle, apr_hash_t *lock_tokens, for (hi = apr_hash_first(pool, lock_tokens); hi; hi = apr_hash_next(hi)) { - void *val; - const char *path, *token; + const char *token = apr_hash_this_key(hi); + const char *path = apr_hash_this_val(hi); + + if (path == (const char *) 1) + { + /* Special handling for svn_fs_access_t * created by using deprecated + svn_fs_access_add_lock_token() function. */ + path = ""; + } + else + { + path = svn_path_uri_autoescape(path, pool); + } - apr_hash_this(hi, (void *)&token, NULL, &val); - path = val; svn_stringbuf_appendstr(lock_str, - svn_stringbuf_createf(pool, "%s|%s\n", - svn_path_uri_autoescape(path, pool), - token)); + svn_stringbuf_createf(pool, "%s|%s\n", path, token)); } svn_stringbuf_appendcstr(lock_str, "\n"); diff --git a/contrib/subversion/subversion/libsvn_repos/libsvn_repos.pc.in b/contrib/subversion/subversion/libsvn_repos/libsvn_repos.pc.in new file mode 100644 index 000000000..af70b94d9 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_repos/libsvn_repos.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_repos +Description: Subversion Repository Library +Version: @PACKAGE_VERSION@ +Requires: apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_fs libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_repos +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_repos/load-fs-vtable.c b/contrib/subversion/subversion/libsvn_repos/load-fs-vtable.c index d1aa339d7..ca3a5cd34 100644 --- a/contrib/subversion/subversion/libsvn_repos/load-fs-vtable.c +++ b/contrib/subversion/subversion/libsvn_repos/load-fs-vtable.c @@ -31,19 +31,17 @@ #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 -#include "private/svn_repos_private.h" #include "private/svn_fspath.h" #include "private/svn_dep_compat.h" #include "private/svn_mergeinfo_private.h" +#include "private/svn_repos_private.h" /*----------------------------------------------------------------------*/ @@ -56,6 +54,7 @@ struct parse_baton svn_boolean_t use_history; svn_boolean_t validate_props; + svn_boolean_t ignore_dates; svn_boolean_t use_pre_commit_hook; svn_boolean_t use_post_commit_hook; enum svn_repos_load_uuid uuid_action; @@ -84,22 +83,27 @@ struct parse_baton SVN_INVALID_REVNUM. */ svn_revnum_t last_rev_mapped; - /* The oldest old revision loaded from the dump stream. If no revisions + /* The oldest 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; + svn_revnum_t oldest_dumpstream_rev; }; struct revision_baton { + /* rev num from dump file */ svn_revnum_t rev; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const svn_string_t *datestamp; + /* (rev num from dump file) minus (rev num to be committed) */ apr_int32_t rev_offset; svn_boolean_t skipped; + /* Array of svn_prop_t with revision properties. */ + apr_array_header_t *revprops; + struct parse_baton *pb; apr_pool_t *pool; }; @@ -189,8 +193,6 @@ change_node_prop(svn_fs_root_t *txn_root, /* 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, @@ -199,17 +201,16 @@ prefix_mergeinfo_paths(svn_string_t **mergeinfo_val, { 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; + const char *merge_source = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); + const char *path; - apr_hash_this(hi, &key, NULL, &rangelist); - merge_source = svn_relpath_canonicalize(key, pool); + merge_source = svn_relpath_canonicalize(merge_source, pool); /* The svn:mergeinfo property syntax demands a repos abspath */ path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, @@ -223,13 +224,20 @@ prefix_mergeinfo_paths(svn_string_t **mergeinfo_val, /* 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() */ + (allocated from POOL). + + Adjust any mergeinfo revisions not older than OLDEST_DUMPSTREAM_REV by + using REV_MAP which maps (svn_revnum_t) old rev to (svn_revnum_t) new rev. + + Adjust any mergeinfo revisions older than OLDEST_DUMPSTREAM_REV by + (-OLDER_REVS_OFFSET), dropping any that become <= 0. + */ static svn_error_t * renumber_mergeinfo_revs(svn_string_t **final_val, const svn_string_t *initial_val, - struct revision_baton *rb, + apr_hash_t *rev_map, + svn_revnum_t oldest_dumpstream_rev, + apr_int32_t older_revs_offset, apr_pool_t *pool) { apr_pool_t *subpool = svn_pool_create(pool); @@ -244,19 +252,22 @@ renumber_mergeinfo_revs(svn_string_t **final_val, 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) + if (oldest_dumpstream_rev > 1) { + /* predates_stream_mergeinfo := mergeinfo that refers to revs before + oldest_dumpstream_rev */ SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &predates_stream_mergeinfo, mergeinfo, - rb->pb->oldest_old_rev - 1, 0, + oldest_dumpstream_rev - 1, 0, TRUE, subpool, subpool)); + /* mergeinfo := mergeinfo that refers to revs >= oldest_dumpstream_rev */ SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &mergeinfo, mergeinfo, - rb->pb->oldest_old_rev - 1, 0, + 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)); + -older_revs_offset, subpool, subpool)); } else { @@ -265,16 +276,9 @@ renumber_mergeinfo_revs(svn_string_t **final_val, 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; + const char *merge_source = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); 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++) @@ -282,27 +286,27 @@ renumber_mergeinfo_revs(svn_string_t **final_val, 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); + rev_from_map = get_revision_mapping(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) + else if (range->start == 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 + won't be found in the REV_MAP mapping of load stream revsions to loaded revisions: The revision immediately - preceeding the oldest revision from the load stream. + preceding 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 + copy from revision (which 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); + rev_from_map = get_revision_mapping(rev_map, + oldest_dumpstream_rev); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->start = rev_from_map - 1; } @@ -319,7 +323,7 @@ renumber_mergeinfo_revs(svn_string_t **final_val, continue; } - rev_from_map = get_revision_mapping(pb->rev_map, range->end); + rev_from_map = get_revision_mapping(rev_map, range->end); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->end = rev_from_map; } @@ -327,8 +331,10 @@ renumber_mergeinfo_revs(svn_string_t **final_val, } if (predates_stream_mergeinfo) + { SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, subpool, subpool)); + } SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool)); @@ -343,6 +349,12 @@ renumber_mergeinfo_revs(svn_string_t **final_val, /** vtable for doing commits to a fs **/ +/* Make a node baton, parsing the relevant HEADERS. + * + * If RB->pb->parent_dir: + * prefix it to NB->path + * prefix it to NB->copyfrom_path (if present) + */ static svn_error_t * make_node_baton(struct node_baton **node_baton_p, apr_hash_t *headers, @@ -429,6 +441,10 @@ make_node_baton(struct node_baton **node_baton_p, return SVN_NO_ERROR; } +/* Make a revision baton, parsing the relevant HEADERS. + * + * Set RB->skipped iff the revision number is outside the range given in PB. + */ static struct revision_baton * make_revision_baton(apr_hash_t *headers, struct parse_baton *pb, @@ -440,6 +456,7 @@ make_revision_baton(apr_hash_t *headers, rb->pb = pb; rb->pool = pool; rb->rev = SVN_INVALID_REVNUM; + rb->revprops = apr_array_make(rb->pool, 8, sizeof(svn_prop_t)); if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER))) { @@ -489,7 +506,8 @@ new_revision_record(void **revision_baton, 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_begin_txn2(&(rb->txn), pb->fs, head_rev, + SVN_FS_TXN_CLIENT_DATE, pool)); SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool)); if (pb->notify_func) @@ -505,8 +523,8 @@ new_revision_record(void **revision_baton, } /* 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 (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev)) + pb->oldest_dumpstream_rev = rb->rev; } /* If we're skipping this revision, try to notify someone. */ @@ -522,7 +540,7 @@ new_revision_record(void **revision_baton, svn_pool_clear(pb->notify_pool); } - /* If we're parsing revision 0, only the revision are (possibly) + /* If we're parsing revision 0, only the revision props 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. */ @@ -533,7 +551,11 @@ new_revision_record(void **revision_baton, -/* Factorized helper func for new_node_record() */ +/* Perform a copy or a plain add. + * + * For a copy, also adjust the copy-from rev, check any copy-source checksum, + * and send a notification. + */ static svn_error_t * maybe_add_with_history(struct node_baton *nb, struct revision_baton *rb, @@ -702,59 +724,50 @@ set_revision_property(void *baton, const svn_string_t *value) { struct revision_baton *rb = baton; + struct parse_baton *pb = rb->pb; + svn_boolean_t is_date = strcmp(name, SVN_PROP_REVISION_DATE) == 0; + svn_prop_t *prop; /* 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)); + /* If we're ignoring dates, and this is one, we're done here. */ + if (is_date && pb->ignore_dates) + return SVN_NO_ERROR; - /* 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; + /* Collect property changes to apply them in one FS call in + close_revision. */ + prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t); + prop->name = apr_pstrdup(rb->pool, name); + prop->value = svn_string_dup(value, rb->pool); - 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)); - } + /* Remember any datestamp that passes through! (See comment in + close_revision() below.) */ + if (is_date) + rb->datestamp = svn_string_dup(value, 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) +svn_error_t * +svn_repos__adjust_mergeinfo_property(svn_string_t **new_value_p, + const svn_string_t *old_value, + const char *parent_dir, + apr_hash_t *rev_map, + svn_revnum_t oldest_dumpstream_rev, + apr_int32_t older_revs_offset, + svn_repos_notify_func_t notify_func, + void *notify_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_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 + the line endings to '\n' and notify that we have made this correction. */ if (strstr(prop_val.data, "\r")) { @@ -770,28 +783,29 @@ adjust_mergeinfo_property(struct revision_baton *rb, prop_val.data = prop_eol_normalized; prop_val.len = strlen(prop_eol_normalized); - if (pb->notify_func) + if (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); + scratch_pool); - pb->notify_func(pb->notify_baton, notify, pb->notify_pool); - svn_pool_clear(pb->notify_pool); + notify_func(notify_baton, notify, scratch_pool); } } /* Renumber mergeinfo as appropriate. */ - SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, rb, + SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, + rev_map, oldest_dumpstream_rev, + older_revs_offset, result_pool)); - if (pb->parent_dir) + + if (parent_dir) { - /* Prefix the merge source paths with PB->parent_dir. */ + /* Prefix the merge source paths with 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)); + parent_dir, result_pool)); } return SVN_NO_ERROR; @@ -821,7 +835,14 @@ set_node_property(void *baton, svn_string_t *new_value; svn_error_t *err; - err = adjust_mergeinfo_property(rb, &new_value, value, nb->pool); + err = svn_repos__adjust_mergeinfo_property(&new_value, value, + pb->parent_dir, + pb->rev_map, + pb->oldest_dumpstream_rev, + rb->rev_offset, + pb->notify_func, pb->notify_baton, + nb->pool, pb->notify_pool); + svn_pool_clear(pb->notify_pool); if (err) { if (pb->validate_props) @@ -836,7 +857,7 @@ set_node_property(void *baton, = svn_repos_notify_create(svn_repos_notify_warning, pb->notify_pool); - notify->warning = svn_repos__notify_warning_invalid_mergeinfo; + 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); @@ -888,9 +909,8 @@ remove_node_props(void *baton) for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi)) { - const void *key; + const char *key = apr_hash_this_key(hi); - 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)); } @@ -983,11 +1003,58 @@ close_revision(void *baton) 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)) + /* If we're skipping this revision we're done here. */ + if (rb->skipped) return SVN_NO_ERROR; + if (rb->rev == 0) + { + /* Special case: set revision 0 properties when loading into an + 'empty' filesystem. */ + svn_revnum_t youngest_rev; + + SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool)); + + if (youngest_rev == 0) + { + apr_hash_t *orig_props; + apr_hash_t *new_props; + apr_array_header_t *diff; + int i; + + SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, 0, rb->pool)); + new_props = svn_prop_array_to_hash(rb->revprops, rb->pool); + SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool)); + + for (i = 0; i < diff->nelts; i++) + { + const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t); + + SVN_ERR(change_rev_prop(pb->repos, 0, prop->name, prop->value, + pb->validate_props, rb->pool)); + } + } + + return SVN_NO_ERROR; + } + + /* If the dumpstream doesn't have an 'svn:date' property and we + aren't ignoring the dates in the dumpstream altogether, remove + any 'svn:date' revision property that was set by FS layer when + the TXN was created. */ + if (! (pb->ignore_dates || rb->datestamp)) + { + svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t); + prop->name = SVN_PROP_REVISION_DATE; + prop->value = NULL; + } + + /* Apply revision property changes. */ + if (rb->pb->validate_props) + SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); + else + SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); + /* Get the txn name and hooks environment if they will be needed. */ if (pb->use_pre_commit_hook || pb->use_post_commit_hook) { @@ -1073,15 +1140,6 @@ close_revision(void *baton) /* 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) { /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ @@ -1107,7 +1165,7 @@ close_revision(void *baton) svn_error_t * -svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks, +svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **callbacks, void **parse_baton, svn_repos_t *repos, svn_revnum_t start_rev, @@ -1116,6 +1174,9 @@ svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks, svn_boolean_t validate_props, 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 ignore_dates, svn_repos_notify_func_t notify_func, void *notify_baton, apr_pool_t *pool) @@ -1157,10 +1218,13 @@ svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks, 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->oldest_dumpstream_rev = SVN_INVALID_REVNUM; pb->last_rev_mapped = SVN_INVALID_REVNUM; pb->start_rev = start_rev; pb->end_rev = end_rev; + pb->use_pre_commit_hook = use_pre_commit_hook; + pb->use_post_commit_hook = use_post_commit_hook; + pb->ignore_dates = ignore_dates; *callbacks = parser; *parse_baton = pb; @@ -1168,9 +1232,8 @@ svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks, } - svn_error_t * -svn_repos_load_fs4(svn_repos_t *repos, +svn_repos_load_fs5(svn_repos_t *repos, svn_stream_t *dumpstream, svn_revnum_t start_rev, svn_revnum_t end_rev, @@ -1179,6 +1242,7 @@ svn_repos_load_fs4(svn_repos_t *repos, svn_boolean_t use_pre_commit_hook, svn_boolean_t use_post_commit_hook, svn_boolean_t validate_props, + svn_boolean_t ignore_dates, svn_repos_notify_func_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, @@ -1187,27 +1251,23 @@ svn_repos_load_fs4(svn_repos_t *repos, { 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, + SVN_ERR(svn_repos_get_fs_build_parser5(&parser, &parse_baton, repos, start_rev, end_rev, TRUE, /* look for copyfrom revs */ validate_props, uuid_action, parent_dir, + use_pre_commit_hook, + use_post_commit_hook, + ignore_dates, 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); } diff --git a/contrib/subversion/subversion/libsvn_repos/load.c b/contrib/subversion/subversion/libsvn_repos/load.c index 691ff9202..96eda85cb 100644 --- a/contrib/subversion/subversion/libsvn_repos/load.c +++ b/contrib/subversion/subversion/libsvn_repos/load.c @@ -21,26 +21,18 @@ */ -#include "svn_private_config.h" +#include + #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_path.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 - #include "private/svn_dep_compat.h" -#include "private/svn_mergeinfo_private.h" /*----------------------------------------------------------------------*/ @@ -150,7 +142,7 @@ read_key_or_val(char **pbuf, char c; numread = len; - SVN_ERR(svn_stream_read(stream, buf, &numread)); + SVN_ERR(svn_stream_read_full(stream, buf, &numread)); *actual_length += numread; if (numread != len) return svn_error_trace(stream_ran_dry()); @@ -158,7 +150,7 @@ read_key_or_val(char **pbuf, /* Suck up extra newline after key data */ numread = 1; - SVN_ERR(svn_stream_read(stream, &c, &numread)); + SVN_ERR(svn_stream_read_full(stream, &c, &numread)); *actual_length += numread; if (numread != 1) return svn_error_trace(stream_ran_dry()); @@ -291,7 +283,8 @@ parse_property_block(svn_stream_t *stream, } -/* Read CONTENT_LENGTH bytes from STREAM, and use +/* Read CONTENT_LENGTH bytes from STREAM. If IS_DELTA is true, use + PARSE_FNS->apply_textdelta to push a text delta, otherwise use PARSE_FNS->set_fulltext to push those bytes as replace fulltext for a node. Use BUFFER/BUFLEN to push the fulltext in "chunks". @@ -324,15 +317,6 @@ parse_text_block(svn_stream_t *stream, SVN_ERR(parse_fns->set_fulltext(&text_stream, record_baton)); } - /* If there are no contents to read, just write an empty buffer - through our callback. */ - if (content_length == 0) - { - wlen = 0; - if (text_stream) - SVN_ERR(svn_stream_write(text_stream, "", &wlen)); - } - /* Regardless of whether or not we have a sink for our data, we need to read it. */ while (content_length) @@ -343,7 +327,7 @@ parse_text_block(svn_stream_t *stream, rlen = (apr_size_t) content_length; num_to_read = rlen; - SVN_ERR(svn_stream_read(stream, buffer, &rlen)); + SVN_ERR(svn_stream_read_full(stream, buffer, &rlen)); content_length -= rlen; if (rlen != num_to_read) return stream_ran_dry(); @@ -654,7 +638,7 @@ svn_repos_parse_dumpstream3(svn_stream_t *stream, rlen = (apr_size_t) remaining; num_to_read = rlen; - SVN_ERR(svn_stream_read(stream, buffer, &rlen)); + SVN_ERR(svn_stream_read_full(stream, buffer, &rlen)); remaining -= rlen; if (rlen != num_to_read) return stream_ran_dry(); diff --git a/contrib/subversion/subversion/libsvn_repos/log.c b/contrib/subversion/subversion/libsvn_repos/log.c index 8ca870b99..82caf0219 100644 --- a/contrib/subversion/subversion/libsvn_repos/log.c +++ b/contrib/subversion/subversion/libsvn_repos/log.c @@ -39,8 +39,10 @@ #include "svn_mergeinfo.h" #include "repos.h" #include "private/svn_fspath.h" +#include "private/svn_fs_private.h" #include "private/svn_mergeinfo_private.h" #include "private/svn_subr_private.h" +#include "private/svn_sorts_private.h" @@ -80,14 +82,11 @@ svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level, subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) { - const void *key; - void *val; - svn_fs_path_change2_t *change; + const char *key = apr_hash_this_key(hi); + svn_fs_path_change2_t *change = apr_hash_this_val(hi); svn_boolean_t readable; svn_pool_clear(subpool); - apr_hash_this(hi, &key, NULL, &val); - change = val; SVN_ERR(authz_read_func(&readable, rev_root, key, authz_read_baton, subpool)); @@ -169,18 +168,23 @@ svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level, * AUTHZ_READ_BATON and FS) to check whether each changed-path (and * copyfrom_path) is readable: * + * - If absolutely every changed-path (and copyfrom_path) is + * readable, then return the full CHANGED hash, and set + * *ACCESS_LEVEL to svn_repos_revision_access_full. + * * - If some paths are readable and some are not, then silently - * omit the unreadable paths from the CHANGED hash, and return - * SVN_ERR_AUTHZ_PARTIALLY_READABLE. + * omit the unreadable paths from the CHANGED hash, and set + * *ACCESS_LEVEL to svn_repos_revision_access_partial. * * - If absolutely every changed-path (and copyfrom_path) is - * unreadable, then return an empty CHANGED hash and - * SVN_ERR_AUTHZ_UNREADABLE. (This is to distinguish a revision - * which truly has no changed paths from a revision in which all - * paths are unreadable.) + * unreadable, then return an empty CHANGED hash, and set + * *ACCESS_LEVEL to svn_repos_revision_access_none. (This is + * to distinguish a revision which truly has no changed paths + * from a revision in which all paths are unreadable.) */ static svn_error_t * -detect_changed(apr_hash_t **changed, +detect_changed(svn_repos_revision_access_level_t *access_level, + apr_hash_t **changed, svn_fs_root_t *root, svn_fs_t *fs, apr_hash_t *prefetched_changes, @@ -190,39 +194,50 @@ detect_changed(apr_hash_t **changed, { apr_hash_t *changes = prefetched_changes; apr_hash_index_t *hi; - apr_pool_t *subpool; + apr_pool_t *iterpool; svn_boolean_t found_readable = FALSE; svn_boolean_t found_unreadable = FALSE; - *changed = svn_hash__make(pool); + /* If we create the CHANGES hash ourselves, we can reuse it as the + * result hash as it contains the exact same keys - but with _all_ + * values being replaced by structs of a different type. */ if (changes == NULL) - SVN_ERR(svn_fs_paths_changed2(&changes, root, pool)); + { + SVN_ERR(svn_fs_paths_changed2(&changes, root, pool)); - if (apr_hash_count(changes) == 0) - /* No paths changed in this revision? Uh, sure, I guess the - revision is readable, then. */ - return SVN_NO_ERROR; + /* If we are going to filter the results, we won't use the exact + * same keys but put them into a new hash. */ + if (authz_read_func) + *changed = svn_hash__make(pool); + else + *changed = changes; + } + else + { + *changed = svn_hash__make(pool); + } - subpool = svn_pool_create(pool); + if (apr_hash_count(changes) == 0) + { + /* No paths changed in this revision? Uh, sure, I guess the + revision is readable, then. */ + *access_level = svn_repos_revision_access_full; + return SVN_NO_ERROR; + } + iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) { /* NOTE: Much of this loop is going to look quite similar to svn_repos_check_revision_access(), but we have to do more things here, so we'll live with the duplication. */ - const void *key; - void *val; - svn_fs_path_change2_t *change; - const char *path; + const char *path = apr_hash_this_key(hi); + apr_ssize_t path_len = apr_hash_this_key_len(hi); + svn_fs_path_change2_t *change = apr_hash_this_val(hi); char action; svn_log_changed_path2_t *item; - svn_pool_clear(subpool); - - /* KEY will be the path, VAL the change. */ - apr_hash_this(hi, &key, NULL, &val); - path = (const char *) key; - change = val; + svn_pool_clear(iterpool); /* Skip path if unreadable. */ if (authz_read_func) @@ -230,7 +245,7 @@ detect_changed(apr_hash_t **changed, svn_boolean_t readable; SVN_ERR(authz_read_func(&readable, root, path, - authz_read_baton, subpool)); + authz_read_baton, iterpool)); if (! readable) { found_unreadable = TRUE; @@ -288,24 +303,26 @@ detect_changed(apr_hash_t **changed, svn_revnum_t prev_rev; const char *parent_path, *name; - svn_fspath__split(&parent_path, &name, path, subpool); + svn_fspath__split(&parent_path, &name, path, iterpool); - SVN_ERR(svn_fs_node_history(&history, root, parent_path, - subpool)); + SVN_ERR(svn_fs_node_history2(&history, root, parent_path, + iterpool, iterpool)); /* Two calls because the first call returns the original revision as the deleted child means it is 'interesting' */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, subpool)); - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, subpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, history, - subpool)); - SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, subpool)); - check_path = svn_fspath__join(parent_path, name, subpool); + iterpool)); + SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, iterpool)); + check_path = svn_fspath__join(parent_path, name, iterpool); } SVN_ERR(svn_fs_check_path(&item->node_kind, check_root, check_path, - subpool)); + iterpool)); } @@ -318,8 +335,11 @@ detect_changed(apr_hash_t **changed, we will follow the DAG from ROOT to PATH and that requires actually reading the directories along the way. */ if (!change->copyfrom_known) - SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, - root, path, subpool)); + { + SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, + root, path, iterpool)); + copyfrom_path = apr_pstrdup(pool, copyfrom_path); + } if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev)) { @@ -330,37 +350,43 @@ detect_changed(apr_hash_t **changed, svn_fs_root_t *copyfrom_root; SVN_ERR(svn_fs_revision_root(©from_root, fs, - copyfrom_rev, subpool)); + copyfrom_rev, iterpool)); SVN_ERR(authz_read_func(&readable, copyfrom_root, copyfrom_path, - authz_read_baton, subpool)); + authz_read_baton, iterpool)); if (! readable) found_unreadable = TRUE; } if (readable) { - item->copyfrom_path = apr_pstrdup(pool, copyfrom_path); + item->copyfrom_path = copyfrom_path; item->copyfrom_rev = copyfrom_rev; } } } - svn_hash_sets(*changed, apr_pstrdup(pool, path), item); + + apr_hash_set(*changed, path, path_len, item); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); if (! found_readable) - /* Every changed-path was unreadable. */ - return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, - NULL, NULL); - - if (found_unreadable) - /* At least one changed-path was unreadable. */ - return svn_error_create(SVN_ERR_AUTHZ_PARTIALLY_READABLE, - NULL, NULL); + { + /* Every changed-path was unreadable. */ + *access_level = svn_repos_revision_access_none; + } + else if (found_unreadable) + { + /* At least one changed-path was unreadable. */ + *access_level = svn_repos_revision_access_partial; + } + else + { + /* Every changed-path was readable. */ + *access_level = svn_repos_revision_access_full; + } - /* Every changed-path was readable. */ return SVN_NO_ERROR; } @@ -410,7 +436,8 @@ get_history(struct path_info *info, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, svn_revnum_t start, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_fs_root_t *history_root = NULL; svn_fs_history_t *hist; @@ -421,27 +448,30 @@ get_history(struct path_info *info, { subpool = info->newpool; - SVN_ERR(svn_fs_history_prev(&info->hist, info->hist, ! strict, subpool)); + SVN_ERR(svn_fs_history_prev2(&info->hist, info->hist, ! strict, + subpool, scratch_pool)); hist = info->hist; } else { - subpool = svn_pool_create(pool); + subpool = svn_pool_create(result_pool); /* Open the history located at the last rev we were at. */ SVN_ERR(svn_fs_revision_root(&history_root, fs, info->history_rev, subpool)); - SVN_ERR(svn_fs_node_history(&hist, history_root, info->path->data, - subpool)); + SVN_ERR(svn_fs_node_history2(&hist, history_root, info->path->data, + subpool, scratch_pool)); - SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool)); + SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool, + scratch_pool)); if (info->first_time) info->first_time = FALSE; else - SVN_ERR(svn_fs_history_prev(&hist, hist, ! strict, subpool)); + SVN_ERR(svn_fs_history_prev2(&hist, hist, ! strict, subpool, + scratch_pool)); } if (! hist) @@ -476,11 +506,11 @@ get_history(struct path_info *info, svn_boolean_t readable; SVN_ERR(svn_fs_revision_root(&history_root, fs, info->history_rev, - subpool)); + scratch_pool)); SVN_ERR(authz_read_func(&readable, history_root, info->path->data, authz_read_baton, - subpool)); + scratch_pool)); if (! readable) info->done = TRUE; } @@ -518,7 +548,8 @@ check_history(svn_boolean_t *changed, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, svn_revnum_t start, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { /* If we're already done with histories for this path, don't try to fetch any more. */ @@ -537,7 +568,7 @@ check_history(svn_boolean_t *changed, rev where this path was changed. */ *changed = TRUE; return get_history(info, fs, strict, authz_read_func, - authz_read_baton, start, pool); + authz_read_baton, start, result_pool, scratch_pool); } /* Return the next interesting revision in our list of HISTORIES. */ @@ -562,7 +593,7 @@ next_history_rev(const apr_array_header_t *histories) /* Set *DELETED_MERGEINFO_CATALOG and *ADDED_MERGEINFO_CATALOG to catalogs describing how mergeinfo values on paths (which are the - keys of those catalogs) were changed in REV. If *PREFETCHED_CAHNGES + keys of those catalogs) were changed in REV. If *PREFETCHED_CHANGES already contains the changed paths for REV, use that. Otherwise, request that data and return it in *PREFETCHED_CHANGES. */ /* ### TODO: This would make a *great*, useful public function, @@ -575,11 +606,12 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, svn_revnum_t rev, apr_pool_t *result_pool, apr_pool_t *scratch_pool) - { svn_fs_root_t *root; apr_pool_t *iterpool; apr_hash_index_t *hi; + svn_boolean_t any_mergeinfo = FALSE; + svn_boolean_t any_copy = FALSE; /* Initialize return variables. */ *deleted_mergeinfo_catalog = svn_hash__make(result_pool); @@ -595,8 +627,33 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, if (*prefetched_changes == NULL) SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool)); - /* No changed paths? We're done. */ - if (apr_hash_count(*prefetched_changes) == 0) + /* Look for copies and (potential) mergeinfo changes. + We will use both flags to take shortcuts further down the road. */ + for (hi = apr_hash_first(scratch_pool, *prefetched_changes); + hi; + hi = apr_hash_next(hi)) + { + svn_fs_path_change2_t *change = apr_hash_this_val(hi); + + /* If there was a prop change and we are not positive that _no_ + mergeinfo change happened, we must assume that it might have. */ + if (change->mergeinfo_mod != svn_tristate_false && change->prop_mod) + any_mergeinfo = TRUE; + + switch (change->change_kind) + { + case svn_fs_path_change_add: + case svn_fs_path_change_replace: + any_copy = TRUE; + break; + + default: + break; + } + } + + /* No potential mergeinfo changes? We're done. */ + if (! any_mergeinfo) return SVN_NO_ERROR; /* Loop over changes, looking for anything that might carry an @@ -607,25 +664,27 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, hi; hi = apr_hash_next(hi)) { - const void *key; - void *val; - svn_fs_path_change2_t *change; - const char *changed_path, *base_path = NULL; + const char *changed_path; + svn_fs_path_change2_t *change = apr_hash_this_val(hi); + const char *base_path = NULL; svn_revnum_t base_rev = SVN_INVALID_REVNUM; svn_fs_root_t *base_root = NULL; svn_string_t *prev_mergeinfo_value = NULL, *mergeinfo_value; - svn_pool_clear(iterpool); + /* Cheap pre-checks that don't require memory allocation etc. */ - /* KEY will be the path, VAL the change. */ - apr_hash_this(hi, &key, NULL, &val); - changed_path = key; - change = val; + /* No mergeinfo change? -> nothing to do here. */ + if (change->mergeinfo_mod == svn_tristate_false) + continue; /* If there was no property change on this item, ignore it. */ if (! change->prop_mod) continue; + /* Begin actual processing */ + changed_path = apr_hash_this_key(hi); + svn_pool_clear(iterpool); + switch (change->change_kind) { @@ -634,25 +693,6 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, ### difference would be the fallback case (path/rev-1 for ### modifies, NULL otherwise). -- cmpilato */ - /* If the path was added or replaced, see if it was created via - copy. If so, that will tell us where its previous location - was. If not, there's no previous location to examine. */ - case svn_fs_path_change_add: - case svn_fs_path_change_replace: - { - const char *copyfrom_path; - svn_revnum_t copyfrom_rev; - - SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path, - root, changed_path, iterpool)); - if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev)) - { - base_path = apr_pstrdup(scratch_pool, copyfrom_path); - base_rev = copyfrom_rev; - } - break; - } - /* If the path was merely modified, see if its previous location was affected by a copy which happened in this revision before assuming it holds the same path it did the @@ -661,15 +701,26 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, { svn_revnum_t appeared_rev; - SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path, - &base_rev, fs, rev, - changed_path, iterpool)); - - /* If this path isn't the result of a copy that occurred - in this revision, we can find the previous version of - it in REV - 1 at the same path. */ - if (! (base_path && SVN_IS_VALID_REVNUM(base_rev) - && (appeared_rev == rev))) + /* If there were no copies in this revision, the path will have + existed in the previous rev. Otherwise, we might just got + copied here and need to check for that eventuality. */ + if (any_copy) + { + SVN_ERR(svn_repos__prev_location(&appeared_rev, &base_path, + &base_rev, fs, rev, + changed_path, iterpool)); + + /* If this path isn't the result of a copy that occurred + in this revision, we can find the previous version of + it in REV - 1 at the same path. */ + if (! (base_path && SVN_IS_VALID_REVNUM(base_rev) + && (appeared_rev == rev))) + { + base_path = changed_path; + base_rev = rev - 1; + } + } + else { base_path = changed_path; base_rev = rev - 1; @@ -677,6 +728,26 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, break; } + /* If the path was added or replaced, see if it was created via + copy. If so, set BASE_REV/BASE_PATH to its previous location. + If not, there's no previous location to examine -- leave + BASE_REV/BASE_PATH = -1/NULL. */ + case svn_fs_path_change_add: + case svn_fs_path_change_replace: + { + if (change->copyfrom_known) + { + base_rev = change->copyfrom_rev; + base_path = change->copyfrom_path; + } + else + { + SVN_ERR(svn_fs_copied_from(&base_rev, &base_path, + root, changed_path, iterpool)); + } + break; + } + /* We don't care about any of the other cases. */ case svn_fs_path_change_delete: case svn_fs_path_change_reset: @@ -704,22 +775,23 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, if (! (mergeinfo_value || prev_mergeinfo_value)) continue; + /* Mergeinfo on both sides but it did not change? Skip that too. */ + if ( mergeinfo_value && prev_mergeinfo_value + && svn_string_compare(mergeinfo_value, prev_mergeinfo_value)) + continue; + /* If mergeinfo was explicitly added or removed on this path, we need to check to see if that was a real semantic change of meaning. So, fill in the "missing" mergeinfo value with the inherited mergeinfo for that path/revision. */ if (prev_mergeinfo_value && (! mergeinfo_value)) { - apr_array_header_t *query_paths = - apr_array_make(iterpool, 1, sizeof(const char *)); svn_mergeinfo_t tmp_mergeinfo; - svn_mergeinfo_catalog_t tmp_catalog; - APR_ARRAY_PUSH(query_paths, const char *) = changed_path; - SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, root, - query_paths, svn_mergeinfo_inherited, - FALSE, TRUE, iterpool, iterpool)); - tmp_mergeinfo = svn_hash_gets(tmp_catalog, changed_path); + SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo, + root, changed_path, + svn_mergeinfo_inherited, TRUE, + iterpool, iterpool)); if (tmp_mergeinfo) SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_value, tmp_mergeinfo, @@ -728,29 +800,23 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, else if (mergeinfo_value && (! prev_mergeinfo_value) && base_path && SVN_IS_VALID_REVNUM(base_rev)) { - apr_array_header_t *query_paths = - apr_array_make(iterpool, 1, sizeof(const char *)); svn_mergeinfo_t tmp_mergeinfo; - svn_mergeinfo_catalog_t tmp_catalog; - APR_ARRAY_PUSH(query_paths, const char *) = base_path; - SVN_ERR(svn_fs_get_mergeinfo2(&tmp_catalog, base_root, - query_paths, svn_mergeinfo_inherited, - FALSE, TRUE, iterpool, iterpool)); - tmp_mergeinfo = svn_hash_gets(tmp_catalog, base_path); + SVN_ERR(svn_fs__get_mergeinfo_for_path(&tmp_mergeinfo, + base_root, base_path, + svn_mergeinfo_inherited, TRUE, + iterpool, iterpool)); if (tmp_mergeinfo) SVN_ERR(svn_mergeinfo_to_string(&prev_mergeinfo_value, tmp_mergeinfo, iterpool)); } - /* If the old and new mergeinfo differ in any way, store the - before and after mergeinfo values in our return hashes. */ - if ((prev_mergeinfo_value && (! mergeinfo_value)) - || ((! prev_mergeinfo_value) && mergeinfo_value) - || (prev_mergeinfo_value && mergeinfo_value - && (! svn_string_compare(mergeinfo_value, - prev_mergeinfo_value)))) + /* Old and new mergeinfo probably differ in some way (we already + checked for textual equality further up). Store the before and + after mergeinfo values in our return hashes. They may still be + equal as manual intervention may have only changed the formatting + but not the relevant contents. */ { svn_mergeinfo_t prev_mergeinfo = NULL, mergeinfo = NULL; svn_mergeinfo_t deleted, added; @@ -781,10 +847,9 @@ fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog, /* Determine what (if any) mergeinfo for PATHS was modified in revision REV, returning the differences for added mergeinfo in *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO. - If *PREFETCHED_CAHNGES already contains the changed paths for + If *PREFETCHED_CHANGES already contains the changed paths for REV, use that. Otherwise, request that data and return it in - *PREFETCHED_CHANGES. - Use POOL for all allocations. */ + *PREFETCHED_CHANGES. */ static svn_error_t * get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, svn_mergeinfo_t *deleted_mergeinfo, @@ -814,14 +879,12 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, if (! paths->nelts) return SVN_NO_ERROR; - /* Create a work subpool and get a root for REV. */ - SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool)); - /* Fetch the mergeinfo changes for REV. */ err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog, &added_mergeinfo_catalog, prefetched_changes, - fs, rev, scratch_pool, scratch_pool); + fs, rev, + scratch_pool, scratch_pool); if (err) { if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) @@ -842,7 +905,10 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, if ( apr_hash_count(deleted_mergeinfo_catalog) == 0 && apr_hash_count(added_mergeinfo_catalog) == 0) return SVN_NO_ERROR; - + + /* Create a work subpool and get a root for REV. */ + SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool)); + /* Check our PATHS for any changes to their inherited mergeinfo. (We deal with changes to mergeinfo directly *on* the paths in the following loop.) */ @@ -851,13 +917,10 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, { const char *path = APR_ARRAY_IDX(paths, i, const char *); const char *prev_path; - apr_ssize_t klen; svn_revnum_t appeared_rev, prev_rev; svn_fs_root_t *prev_root; - svn_mergeinfo_catalog_t catalog, inherited_catalog; svn_mergeinfo_t prev_mergeinfo, mergeinfo, deleted, added, prev_inherited_mergeinfo, inherited_mergeinfo; - apr_array_header_t *query_paths; svn_pool_clear(iterpool); @@ -893,11 +956,10 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, this path. Ignore not-found errors returned by the filesystem or invalid mergeinfo (Issue #3896).*/ SVN_ERR(svn_fs_revision_root(&prev_root, fs, prev_rev, iterpool)); - query_paths = apr_array_make(iterpool, 1, sizeof(const char *)); - APR_ARRAY_PUSH(query_paths, const char *) = prev_path; - err = svn_fs_get_mergeinfo2(&catalog, prev_root, query_paths, - svn_mergeinfo_inherited, FALSE, TRUE, - iterpool, iterpool); + err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo, + prev_root, prev_path, + svn_mergeinfo_inherited, TRUE, + iterpool, iterpool); if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND || err->apr_err == SVN_ERR_FS_NOT_DIRECTORY || err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)) @@ -917,31 +979,25 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, To check for this we must fetch the "raw" previous inherited mergeinfo and the "raw" mergeinfo @REV then compare these. */ - SVN_ERR(svn_fs_get_mergeinfo2(&inherited_catalog, prev_root, query_paths, - svn_mergeinfo_nearest_ancestor, FALSE, - FALSE, /* adjust_inherited_mergeinfo */ - iterpool, iterpool)); - - klen = strlen(prev_path); - prev_mergeinfo = apr_hash_get(catalog, prev_path, klen); - prev_inherited_mergeinfo = apr_hash_get(inherited_catalog, prev_path, klen); + SVN_ERR(svn_fs__get_mergeinfo_for_path(&prev_inherited_mergeinfo, + prev_root, prev_path, + svn_mergeinfo_nearest_ancestor, + FALSE, /* adjust_inherited_mergeinfo */ + iterpool, iterpool)); /* Fetch the current mergeinfo (as of REV, and including inherited stuff) for this path. */ - APR_ARRAY_IDX(query_paths, 0, const char *) = path; - SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, query_paths, - svn_mergeinfo_inherited, FALSE, TRUE, - iterpool, iterpool)); + SVN_ERR(svn_fs__get_mergeinfo_for_path(&mergeinfo, + root, path, + svn_mergeinfo_inherited, TRUE, + iterpool, iterpool)); /* Issue #4022 again, fetch the raw inherited mergeinfo. */ - SVN_ERR(svn_fs_get_mergeinfo2(&inherited_catalog, root, query_paths, - svn_mergeinfo_nearest_ancestor, FALSE, - FALSE, /* adjust_inherited_mergeinfo */ - iterpool, iterpool)); - - klen = strlen(path); - mergeinfo = apr_hash_get(catalog, path, klen); - inherited_mergeinfo = apr_hash_get(inherited_catalog, path, klen); + SVN_ERR(svn_fs__get_mergeinfo_for_path(&inherited_mergeinfo, + root, path, + svn_mergeinfo_nearest_ancestor, + FALSE, /* adjust_inherited_mergeinfo */ + iterpool, iterpool)); if (!prev_mergeinfo && !mergeinfo) continue; @@ -965,7 +1021,7 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, svn_boolean_t same_mergeinfo; SVN_ERR(svn_mergeinfo__equals(&same_mergeinfo, prev_inherited_mergeinfo, - FALSE, + NULL, TRUE, iterpool)); if (same_mergeinfo) continue; @@ -985,16 +1041,10 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, for (hi = apr_hash_first(scratch_pool, added_mergeinfo_catalog); hi; hi = apr_hash_next(hi)) { - const void *key; - apr_ssize_t klen; - void *val; - const char *changed_path; - svn_mergeinfo_t added, deleted; - - /* The path is the key, the mergeinfo delta is the value. */ - apr_hash_this(hi, &key, &klen, &val); - changed_path = key; - added = val; + const char *changed_path = apr_hash_this_key(hi); + apr_ssize_t klen = apr_hash_this_key_len(hi); + svn_mergeinfo_t added = apr_hash_this_val(hi); + svn_mergeinfo_t deleted; for (i = 0; i < paths->nelts; i++) { @@ -1002,7 +1052,7 @@ get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo, if (! svn_fspath__skip_ancestor(path, changed_path)) continue; svn_pool_clear(iterpool); - deleted = apr_hash_get(deleted_mergeinfo_catalog, key, klen); + deleted = apr_hash_get(deleted_mergeinfo_catalog, changed_path, klen); SVN_ERR(svn_mergeinfo_merge2(*deleted_mergeinfo, svn_mergeinfo_dup(deleted, result_pool), result_pool, iterpool)); @@ -1033,6 +1083,7 @@ fill_log_entry(svn_log_entry_t *log_entry, { apr_hash_t *r_props, *changed_paths = NULL; svn_boolean_t get_revprops = TRUE, censor_revprops = FALSE; + svn_boolean_t want_revprops = !revprops || revprops->nelts; /* Discover changed paths if the user requested them or if we need to check that they are readable. */ @@ -1040,33 +1091,27 @@ fill_log_entry(svn_log_entry_t *log_entry, && (authz_read_func || discover_changed_paths)) { svn_fs_root_t *newroot; - svn_error_t *patherr; + svn_repos_revision_access_level_t access_level; SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool)); - patherr = detect_changed(&changed_paths, - newroot, fs, prefetched_changes, - authz_read_func, authz_read_baton, - pool); + SVN_ERR(detect_changed(&access_level, &changed_paths, + newroot, fs, prefetched_changes, + authz_read_func, authz_read_baton, + pool)); - if (patherr - && patherr->apr_err == SVN_ERR_AUTHZ_UNREADABLE) + if (access_level == svn_repos_revision_access_none) { /* All changed-paths are unreadable, so clear all fields. */ - svn_error_clear(patherr); changed_paths = NULL; get_revprops = FALSE; } - else if (patherr - && patherr->apr_err == SVN_ERR_AUTHZ_PARTIALLY_READABLE) + else if (access_level == svn_repos_revision_access_partial) { /* At least one changed-path was unreadable, so censor all but author and date. (The unreadable paths are already missing from the hash.) */ - svn_error_clear(patherr); censor_revprops = TRUE; } - else if (patherr) - return patherr; /* It may be the case that an authz func was passed in, but the user still doesn't want to see any changed-paths. */ @@ -1074,7 +1119,7 @@ fill_log_entry(svn_log_entry_t *log_entry, changed_paths = NULL; } - if (get_revprops) + if (get_revprops && want_revprops) { /* User is allowed to see at least some revprops. */ SVN_ERR(svn_fs_revision_proplist(&r_props, fs, rev, pool)); @@ -1096,21 +1141,54 @@ fill_log_entry(svn_log_entry_t *log_entry, } else { - /* Requested only some revprops... */ int i; - for (i = 0; i < revprops->nelts; i++) + + /* Requested only some revprops... */ + + /* Make "svn:author" and "svn:date" available as svn_string_t + for efficient comparison via svn_string_compare(). Note that + we want static initialization here and must therefore emulate + strlen(x) by sizeof(x)-1. */ + static const svn_string_t svn_prop_revision_author + = {SVN_PROP_REVISION_AUTHOR, sizeof(SVN_PROP_REVISION_AUTHOR)-1}; + static const svn_string_t svn_prop_revision_date + = {SVN_PROP_REVISION_DATE, sizeof(SVN_PROP_REVISION_DATE)-1}; + + /* often only the standard revprops got requested and delivered. + In that case, we can simply pass the hash on. */ + if (revprops->nelts == apr_hash_count(r_props) && !censor_revprops) { - char *name = APR_ARRAY_IDX(revprops, i, char *); - svn_string_t *value = svn_hash_gets(r_props, name); - if (censor_revprops - && !(strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0 - || strcmp(name, SVN_PROP_REVISION_DATE) == 0)) - /* ... but we can only return author/date. */ - continue; - if (log_entry->revprops == NULL) - log_entry->revprops = svn_hash__make(pool); - svn_hash_sets(log_entry->revprops, name, value); + log_entry->revprops = r_props; + for (i = 0; i < revprops->nelts; i++) + { + const svn_string_t *name + = APR_ARRAY_IDX(revprops, i, const svn_string_t *); + if (!apr_hash_get(r_props, name->data, name->len)) + { + /* hash does not match list of revprops we want */ + log_entry->revprops = NULL; + break; + } + } } + + /* slow, revprop-by-revprop filtering */ + if (log_entry->revprops == NULL) + for (i = 0; i < revprops->nelts; i++) + { + const svn_string_t *name + = APR_ARRAY_IDX(revprops, i, const svn_string_t *); + svn_string_t *value + = apr_hash_get(r_props, name->data, name->len); + if (censor_revprops + && !svn_string_compare(name, &svn_prop_revision_author) + && !svn_string_compare(name, &svn_prop_revision_date)) + /* ... but we can only return author/date. */ + continue; + if (log_entry->revprops == NULL) + log_entry->revprops = svn_hash__make(pool); + apr_hash_set(log_entry->revprops, name->data, name->len, value); + } } } @@ -1136,8 +1214,8 @@ fill_log_entry(svn_log_entry_t *log_entry, (i.e. retrieve none if the array is empty). LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and - NESTED_MERGES are as per the arguments of the same name to DO_LOGS. If - HANDLING_MERGED_REVISION is true and *all* changed paths within REV are + NESTED_MERGES are as per the arguments of the same name to DO_LOGS. + If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send the log message for REV. If SUBTRACTIVE_MERGE is true, then REV was reverse merged. @@ -1151,7 +1229,7 @@ send_log(svn_revnum_t rev, svn_fs_t *fs, apr_hash_t *prefetched_changes, svn_mergeinfo_t log_target_history_as_mergeinfo, - apr_hash_t *nested_merges, + svn_bit_array__t *nested_merges, svn_boolean_t discover_changed_paths, svn_boolean_t subtractive_merge, svn_boolean_t handling_merged_revision, @@ -1170,8 +1248,7 @@ send_log(svn_revnum_t rev, log_entry = svn_log_entry_create(pool); SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes, discover_changed_paths || handling_merged_revision, - revprops, authz_read_func, authz_read_baton, - pool)); + revprops, authz_read_func, authz_read_baton, pool)); log_entry->has_children = has_children; log_entry->subtractive_merge = subtractive_merge; @@ -1184,32 +1261,29 @@ send_log(svn_revnum_t rev, && apr_hash_count(log_target_history_as_mergeinfo)) { apr_hash_index_t *hi; - apr_pool_t *subpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); /* REV was merged in, but it might already be part of the log target's natural history, so change our starting assumption. */ found_rev_of_interest = FALSE; /* Look at each changed path in REV. */ - for (hi = apr_hash_first(subpool, log_entry->changed_paths2); + for (hi = apr_hash_first(pool, log_entry->changed_paths2); hi; hi = apr_hash_next(hi)) { svn_boolean_t path_is_in_history = FALSE; - const char *changed_path = svn__apr_hash_index_key(hi); + const char *changed_path = apr_hash_this_key(hi); apr_hash_index_t *hi2; - apr_pool_t *inner_subpool = svn_pool_create(subpool); /* Look at each path on the log target's mergeinfo. */ - for (hi2 = apr_hash_first(inner_subpool, + for (hi2 = apr_hash_first(iterpool, log_target_history_as_mergeinfo); hi2; hi2 = apr_hash_next(hi2)) { - const char *mergeinfo_path = - svn__apr_hash_index_key(hi2); - svn_rangelist_t *rangelist = - svn__apr_hash_index_val(hi2); + const char *mergeinfo_path = apr_hash_this_key(hi2); + svn_rangelist_t *rangelist = apr_hash_this_val(hi2); /* Check whether CHANGED_PATH at revision REV is a child of a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */ @@ -1232,7 +1306,7 @@ send_log(svn_revnum_t rev, if (path_is_in_history) break; } - svn_pool_destroy(inner_subpool); + svn_pool_clear(iterpool); if (!path_is_in_history) { @@ -1243,7 +1317,7 @@ send_log(svn_revnum_t rev, break; } } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); } /* If we only got changed paths the sake of detecting redundant merged @@ -1255,13 +1329,12 @@ send_log(svn_revnum_t rev, revision. */ if (found_rev_of_interest) { + apr_pool_t *scratch_pool; + /* Is REV a merged revision we've already sent? */ if (nested_merges && handling_merged_revision) { - svn_revnum_t *merged_rev = apr_hash_get(nested_merges, &rev, - sizeof(svn_revnum_t *)); - - if (merged_rev) + if (svn_bit_array__get(nested_merges, rev)) { /* We already sent REV. */ return SVN_NO_ERROR; @@ -1271,21 +1344,18 @@ send_log(svn_revnum_t rev, /* NESTED_REVS needs to last across all the send_log, do_logs, handle_merged_revisions() recursions, so use the pool it was created in at the top of the recursion. */ - apr_pool_t *hash_pool = apr_hash_pool_get(nested_merges); - svn_revnum_t *long_lived_rev = apr_palloc(hash_pool, - sizeof(svn_revnum_t)); - *long_lived_rev = rev; - apr_hash_set(nested_merges, long_lived_rev, - sizeof(svn_revnum_t *), long_lived_rev); + svn_bit_array__set(nested_merges, rev, TRUE); } } - return (*receiver)(receiver_baton, log_entry, pool); - } - else - { - return SVN_NO_ERROR; + /* Pass a scratch pool to ensure no temporary state stored + by the receiver callback persists. */ + scratch_pool = svn_pool_create(pool); + SVN_ERR(receiver(receiver_baton, log_entry, scratch_pool)); + svn_pool_destroy(scratch_pool); } + + return SVN_NO_ERROR; } /* This controls how many history objects we keep open. For any targets @@ -1334,13 +1404,11 @@ get_path_histories(apr_array_header_t **histories, const char *this_path = APR_ARRAY_IDX(paths, i, const char *); struct path_info *info = apr_palloc(pool, sizeof(struct path_info)); + svn_pool_clear(iterpool); if (authz_read_func) { svn_boolean_t readable; - - svn_pool_clear(iterpool); - SVN_ERR(authz_read_func(&readable, root, this_path, authz_read_baton, iterpool)); if (! readable) @@ -1354,7 +1422,8 @@ get_path_histories(apr_array_header_t **histories, if (i < MAX_OPEN_HISTORIES) { - err = svn_fs_node_history(&info->hist, root, this_path, pool); + err = svn_fs_node_history2(&info->hist, root, this_path, pool, + iterpool); if (err && ignore_missing_locations && (err->apr_err == SVN_ERR_FS_NOT_FOUND || @@ -1378,7 +1447,7 @@ get_path_histories(apr_array_header_t **histories, err = get_history(info, fs, strict_node_history, authz_read_func, authz_read_baton, - hist_start, pool); + hist_start, pool, iterpool); if (err && ignore_missing_locations && (err->apr_err == SVN_ERR_FS_NOT_FOUND || @@ -1486,8 +1555,9 @@ combine_mergeinfo_path_lists(apr_array_header_t **combined_list, { int i; struct rangelist_path *rp = apr_palloc(subpool, sizeof(*rp)); - apr_hash_this(hi, (void *) &rp->path, NULL, - (void *) &rp->rangelist); + + rp->path = apr_hash_this_key(hi); + rp->rangelist = apr_hash_this_val(hi); APR_ARRAY_PUSH(rangelist_paths, struct rangelist_path *) = rp; /* We need to make local copies of the rangelist, since we will be @@ -1515,8 +1585,7 @@ combine_mergeinfo_path_lists(apr_array_header_t **combined_list, /* First, sort the list such that the start revision of the first revision arrays are sorted. */ - qsort(rangelist_paths->elts, rangelist_paths->nelts, - rangelist_paths->elt_size, compare_rangelist_paths); + svn_sort__array(rangelist_paths, compare_rangelist_paths); /* Next, find the number of revision ranges which start with the same revision. */ @@ -1627,7 +1696,7 @@ do_logs(svn_fs_t *fs, const apr_array_header_t *paths, svn_mergeinfo_t log_target_history_as_mergeinfo, svn_mergeinfo_t processed, - apr_hash_t *nested_merges, + svn_bit_array__t *nested_merges, svn_revnum_t hist_start, svn_revnum_t hist_end, int limit, @@ -1680,7 +1749,7 @@ static svn_error_t * handle_merged_revisions(svn_revnum_t rev, svn_fs_t *fs, svn_mergeinfo_t log_target_history_as_mergeinfo, - apr_hash_t *nested_merges, + svn_bit_array__t *nested_merges, svn_mergeinfo_t processed, svn_mergeinfo_t added_mergeinfo, svn_mergeinfo_t deleted_mergeinfo, @@ -1711,8 +1780,7 @@ handle_merged_revisions(svn_revnum_t rev, TRUE, pool)); SVN_ERR_ASSERT(combined_list != NULL); - qsort(combined_list->elts, combined_list->nelts, - combined_list->elt_size, compare_path_list_range); + svn_sort__array(combined_list, compare_path_list_range); /* Because the combined_lists are ordered youngest to oldest, iterate over them in reverse. */ @@ -1889,7 +1957,7 @@ do_logs(svn_fs_t *fs, const apr_array_header_t *paths, svn_mergeinfo_t log_target_history_as_mergeinfo, svn_mergeinfo_t processed, - apr_hash_t *nested_merges, + svn_bit_array__t *nested_merges, svn_revnum_t hist_start, svn_revnum_t hist_end, int limit, @@ -1907,7 +1975,7 @@ do_logs(svn_fs_t *fs, void *authz_read_baton, apr_pool_t *pool) { - apr_pool_t *iterpool; + apr_pool_t *iterpool, *iterpool2; apr_pool_t *subpool = NULL; apr_array_header_t *revs = NULL; apr_hash_t *rev_mergeinfo = NULL; @@ -1943,6 +2011,7 @@ do_logs(svn_fs_t *fs, where a path was changed to the array, or if they wanted history in reverse order just send it to them right away. */ iterpool = svn_pool_create(pool); + iterpool2 = svn_pool_create(pool); for (current = hist_end; any_histories_left; current = next_history_rev(histories)) @@ -1956,14 +2025,19 @@ do_logs(svn_fs_t *fs, struct path_info *info = APR_ARRAY_IDX(histories, i, struct path_info *); + svn_pool_clear(iterpool2); + /* Check history for this path in current rev. */ SVN_ERR(check_history(&changed, info, fs, current, strict_node_history, authz_read_func, - authz_read_baton, hist_start, pool)); + authz_read_baton, hist_start, pool, + iterpool2)); if (! info->done) any_histories_left = TRUE; } + svn_pool_clear(iterpool2); + /* If any of the paths changed in this rev then add or send it. */ if (changed) { @@ -1993,8 +2067,8 @@ do_logs(svn_fs_t *fs, &deleted_mergeinfo, &changes, fs, cur_paths, - current, iterpool, - iterpool)); + current, + iterpool, iterpool)); has_children = (apr_hash_count(added_mergeinfo) > 0 || apr_hash_count(deleted_mergeinfo) > 0); } @@ -2020,7 +2094,7 @@ do_logs(svn_fs_t *fs, single hash to be shared across all of the merged recursions so we can track and squelch duplicates. */ subpool = svn_pool_create(pool); - nested_merges = svn_hash__make(subpool); + nested_merges = svn_bit_array__create(hist_end, subpool); processed = svn_hash__make(subpool); } @@ -2052,19 +2126,18 @@ do_logs(svn_fs_t *fs, if (added_mergeinfo || deleted_mergeinfo) { - svn_revnum_t *cur_rev = apr_pcalloc(pool, sizeof(*cur_rev)); + svn_revnum_t *cur_rev = + apr_pmemdup(pool, ¤t, sizeof(*cur_rev)); struct added_deleted_mergeinfo *add_and_del_mergeinfo = apr_palloc(pool, sizeof(*add_and_del_mergeinfo)); - if (added_mergeinfo) - add_and_del_mergeinfo->added_mergeinfo = - svn_mergeinfo_dup(added_mergeinfo, pool); - - if (deleted_mergeinfo) - add_and_del_mergeinfo->deleted_mergeinfo = - svn_mergeinfo_dup(deleted_mergeinfo, pool); + /* If we have added or deleted mergeinfo, both are non-null */ + SVN_ERR_ASSERT(added_mergeinfo && deleted_mergeinfo); + add_and_del_mergeinfo->added_mergeinfo = + svn_mergeinfo_dup(added_mergeinfo, pool); + add_and_del_mergeinfo->deleted_mergeinfo = + svn_mergeinfo_dup(deleted_mergeinfo, pool); - *cur_rev = current; if (! rev_mergeinfo) rev_mergeinfo = svn_hash__make(pool); apr_hash_set(rev_mergeinfo, cur_rev, sizeof(*cur_rev), @@ -2073,6 +2146,7 @@ do_logs(svn_fs_t *fs, } } } + svn_pool_destroy(iterpool2); svn_pool_destroy(iterpool); if (subpool) @@ -2112,7 +2186,8 @@ do_logs(svn_fs_t *fs, SVN_ERR(send_log(current, fs, NULL, log_target_history_as_mergeinfo, nested_merges, discover_changed_paths, subtractive_merge, - handling_merged_revisions, revprops, has_children, + handling_merged_revisions, + revprops, has_children, receiver, receiver_baton, authz_read_func, authz_read_baton, iterpool)); if (has_children) @@ -2120,7 +2195,7 @@ do_logs(svn_fs_t *fs, if (!nested_merges) { subpool = svn_pool_create(pool); - nested_merges = svn_hash__make(subpool); + nested_merges = svn_bit_array__create(current, subpool); } SVN_ERR(handle_merged_revisions(current, fs, @@ -2130,7 +2205,8 @@ do_logs(svn_fs_t *fs, added_mergeinfo, deleted_mergeinfo, discover_changed_paths, - strict_node_history, revprops, + strict_node_history, + revprops, receiver, receiver_baton, authz_read_func, authz_read_baton, @@ -2252,6 +2328,19 @@ svn_repos_get_logs4(svn_repos_t *repos, svn_boolean_t descending_order; svn_mergeinfo_t paths_history_mergeinfo = NULL; + if (revprops) + { + int i; + apr_array_header_t *new_revprops + = apr_array_make(pool, revprops->nelts, sizeof(svn_string_t *)); + + for (i = 0; i < revprops->nelts; ++i) + APR_ARRAY_PUSH(new_revprops, svn_string_t *) + = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *), pool); + + revprops = new_revprops; + } + /* Setup log range. */ SVN_ERR(svn_fs_youngest_rev(&head, fs, pool)); @@ -2321,7 +2410,7 @@ svn_repos_get_logs4(svn_repos_t *repos, } send_count = end - start + 1; - if (limit && send_count > limit) + if (limit > 0 && send_count > limit) send_count = limit; for (i = 0; i < send_count; ++i) { @@ -2335,9 +2424,8 @@ svn_repos_get_logs4(svn_repos_t *repos, rev = start + i; SVN_ERR(send_log(rev, fs, NULL, NULL, NULL, discover_changed_paths, FALSE, - FALSE, revprops, FALSE, receiver, - receiver_baton, authz_read_func, - authz_read_baton, iterpool)); + FALSE, revprops, FALSE, receiver, receiver_baton, + authz_read_func, authz_read_baton, iterpool)); } svn_pool_destroy(iterpool); @@ -2363,7 +2451,7 @@ svn_repos_get_logs4(svn_repos_t *repos, return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end, limit, discover_changed_paths, strict_node_history, - include_merged_revisions, FALSE, FALSE, FALSE, revprops, - descending_order, receiver, receiver_baton, + include_merged_revisions, FALSE, FALSE, FALSE, + revprops, descending_order, receiver, receiver_baton, authz_read_func, authz_read_baton, pool); } diff --git a/contrib/subversion/subversion/libsvn_repos/replay.c b/contrib/subversion/subversion/libsvn_repos/replay.c index 985a67392..bcf260c47 100644 --- a/contrib/subversion/subversion/libsvn_repos/replay.c +++ b/contrib/subversion/subversion/libsvn_repos/replay.c @@ -39,6 +39,7 @@ #include "private/svn_fspath.h" #include "private/svn_repos_private.h" #include "private/svn_delta_private.h" +#include "private/svn_sorts_private.h" /*** Backstory ***/ @@ -183,11 +184,10 @@ add_subdir(svn_fs_root_t *source_root, for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { - const void *key; - void *val; + const char *key = apr_hash_this_key(phi); + svn_string_t *val = apr_hash_this_val(phi); svn_pool_clear(subpool); - apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_dir_prop(*dir_baton, key, val, subpool)); } @@ -200,18 +200,13 @@ add_subdir(svn_fs_root_t *source_root, { svn_fs_path_change2_t *change; svn_boolean_t readable = TRUE; - svn_fs_dirent_t *dent; + svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; const char *new_edit_path; - void *val; svn_pool_clear(subpool); - apr_hash_this(hi, NULL, NULL, &val); - - dent = val; - new_edit_path = svn_relpath_join(edit_path, dent->name, subpool); /* If a file or subdirectory of the copied directory is listed as a @@ -308,9 +303,9 @@ add_subdir(svn_fs_root_t *source_root, for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { - const void *key; + const char *key = apr_hash_this_key(phi); + svn_string_t *val = apr_hash_this_val(phi); - apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_file_prop(file_baton, key, val, subpool)); } @@ -553,6 +548,17 @@ path_driver_cb_func(void **dir_baton, svn_boolean_t src_readable; svn_fs_root_t *copyfrom_root; + /* E.g. when verifying corrupted repositories, their changed path + lists may contain an ADD for "/". The delta path driver will + call us with a NULL parent in that case. */ + if (*edit_path == 0) + return svn_error_create(SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Root directory already exists.")); + + /* A NULL parent_baton will cause a segfault. It should never be + NULL for non-root paths. */ + SVN_ERR_ASSERT(parent_baton); + /* Was this node copied? */ SVN_ERR(fill_copyfrom(©from_root, ©from_path, ©from_rev, &src_readable, root, change, @@ -882,17 +888,11 @@ svn_repos_replay2(svn_fs_root_t *root, changed_paths = apr_hash_make(pool); for (hi = apr_hash_first(pool, fs_changes); hi; hi = apr_hash_next(hi)) { - const void *key; - void *val; - apr_ssize_t keylen; - const char *path; - svn_fs_path_change2_t *change; + const char *path = apr_hash_this_key(hi); + apr_ssize_t keylen = apr_hash_this_key_len(hi); + svn_fs_path_change2_t *change = apr_hash_this_val(hi); svn_boolean_t allowed = TRUE; - apr_hash_this(hi, &key, &keylen, &val); - path = key; - change = val; - if (authz_read_func) SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, pool)); @@ -1063,7 +1063,7 @@ add_subdir_ev2(svn_fs_root_t *source_root, { svn_fs_path_change2_t *change; svn_boolean_t readable = TRUE; - svn_fs_dirent_t *dent = svn__apr_hash_index_val(hi); + svn_fs_dirent_t *dent = apr_hash_this_val(hi); const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; const char *child_relpath; @@ -1457,8 +1457,8 @@ replay_node(svn_fs_root_t *root, } SVN_ERR(svn_editor_alter_file(editor, repos_relpath, - SVN_INVALID_REVNUM, props, checksum, - contents)); + SVN_INVALID_REVNUM, + checksum, contents, props)); } if (change->node_kind == svn_node_dir @@ -1514,17 +1514,11 @@ svn_repos__replay_ev2(svn_fs_root_t *root, for (hi = apr_hash_first(scratch_pool, fs_changes); hi; hi = apr_hash_next(hi)) { - const void *key; - void *val; - apr_ssize_t keylen; - const char *path; - svn_fs_path_change2_t *change; + const char *path = apr_hash_this_key(hi); + apr_ssize_t keylen = apr_hash_this_key_len(hi); + svn_fs_path_change2_t *change = apr_hash_this_val(hi); svn_boolean_t allowed = TRUE; - apr_hash_this(hi, &key, &keylen, &val); - path = key; - change = val; - if (authz_read_func) SVN_ERR(authz_read_func(&allowed, root, path, authz_read_baton, scratch_pool)); @@ -1564,7 +1558,7 @@ svn_repos__replay_ev2(svn_fs_root_t *root, /* Sort the paths. Although not strictly required by the API, this has the pleasant side effect of maintaining a consistent ordering of dumpfile contents. */ - qsort(paths->elts, paths->nelts, paths->elt_size, svn_sort_compare_paths); + svn_sort__array(paths, svn_sort_compare_paths); /* Now actually handle the various paths. */ iterpool = svn_pool_create(scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_repos/reporter.c b/contrib/subversion/subversion/libsvn_repos/reporter.c index de4685879..7bc8d476d 100644 --- a/contrib/subversion/subversion/libsvn_repos/reporter.c +++ b/contrib/subversion/subversion/libsvn_repos/reporter.c @@ -495,11 +495,11 @@ get_revision_info(report_baton_t *b, /* Create a result object */ info = apr_palloc(b->pool, sizeof(*info)); info->rev = rev; - info->date = cdate ? svn_string_dup(cdate, b->pool) : NULL; - info->author = author ? svn_string_dup(author, b->pool) : NULL; + info->date = svn_string_dup(cdate, b->pool); + info->author = svn_string_dup(author, b->pool); /* Cache it */ - apr_hash_set(b->revision_infos, &info->rev, sizeof(rev), info); + apr_hash_set(b->revision_infos, &info->rev, sizeof(info->rev), info); } *revision_info = info; @@ -576,8 +576,8 @@ delta_proplists(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, SVN_ERR(get_source_root(b, &s_root, s_rev)); /* Is this deltification worth our time? */ - SVN_ERR(svn_fs_props_changed(&changed, b->t_root, t_path, s_root, - s_path, pool)); + SVN_ERR(svn_fs_props_different(&changed, b->t_root, t_path, s_root, + s_path, pool)); if (! changed) return SVN_NO_ERROR; @@ -603,10 +603,9 @@ delta_proplists(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, /* So source, i.e. all new. Transmit all target props. */ for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi)) { - const void *key; - void *val; + const char *key = apr_hash_this_key(hi); + svn_string_t *val = apr_hash_this_val(hi); - apr_hash_this(hi, &key, NULL, &val); SVN_ERR(change_fn(b, object, key, val, pool)); } } @@ -842,7 +841,7 @@ add_file_smartly(report_baton_t *b, starting with '/', so make sure o_path always starts with a '/' too. */ if (*o_path != '/') - o_path = apr_pstrcat(pool, "/", o_path, (char *)NULL); + o_path = apr_pstrcat(pool, "/", o_path, SVN_VA_NULL); SVN_ERR(svn_fs_closest_copy(&closest_copy_root, &closest_copy_path, b->t_root, o_path, pool)); @@ -1140,13 +1139,11 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, svn_boolean_t start_empty, svn_depth_t wc_depth, svn_depth_t requested_depth, apr_pool_t *pool) { - svn_fs_root_t *s_root; apr_hash_t *s_entries = NULL, *t_entries; apr_hash_index_t *hi; apr_pool_t *subpool = svn_pool_create(pool); - apr_pool_t *iterpool; - const char *name, *s_fullpath, *t_fullpath, *e_fullpath; - path_info_t *info; + apr_array_header_t *t_ordered_entries = NULL; + int i; /* Compare the property lists. If we're starting empty, pass a NULL source path so that we add all the properties. @@ -1159,19 +1156,25 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, if (requested_depth > svn_depth_empty || requested_depth == svn_depth_unknown) { + apr_pool_t *iterpool; + /* Get the list of entries in each of source and target. */ if (s_path && !start_empty) { + svn_fs_root_t *s_root; + SVN_ERR(get_source_root(b, &s_root, s_rev)); SVN_ERR(svn_fs_dir_entries(&s_entries, s_root, s_path, subpool)); } SVN_ERR(svn_fs_dir_entries(&t_entries, b->t_root, t_path, subpool)); /* Iterate over the report information for this directory. */ - iterpool = svn_pool_create(pool); + iterpool = svn_pool_create(subpool); while (1) { + path_info_t *info; + const char *name, *s_fullpath, *t_fullpath, *e_fullpath; const svn_fs_dirent_t *s_entry, *t_entry; svn_pool_clear(iterpool); @@ -1192,6 +1195,8 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, but don't update the entry yet. */ if (s_entries) svn_hash_sets(s_entries, name, NULL); + + svn_pool_destroy(info->pool); continue; } @@ -1199,10 +1204,9 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, t_fullpath = svn_fspath__join(t_path, name, iterpool); t_entry = svn_hash_gets(t_entries, name); s_fullpath = s_path ? svn_fspath__join(s_path, name, iterpool) : NULL; - s_entry = s_entries ? - svn_hash_gets(s_entries, name) : NULL; + s_entry = s_entries ? svn_hash_gets(s_entries, name) : NULL; - /* The only special cases here are + /* The only special cases where we don't process the entry are - When requested_depth is files but the reported path is a directory. This is technically a client error, but we @@ -1210,10 +1214,10 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, - When the reported depth is svn_depth_exclude. */ - if ((! info || info->depth != svn_depth_exclude) - && (requested_depth != svn_depth_files - || ((! t_entry || t_entry->kind != svn_node_dir) - && (! s_entry || s_entry->kind != svn_node_dir)))) + if (! ((requested_depth == svn_depth_files + && ((t_entry && t_entry->kind == svn_node_dir) + || (s_entry && s_entry->kind == svn_node_dir))) + || (info && info->depth == svn_depth_exclude))) SVN_ERR(update_entry(b, s_rev, s_fullpath, s_entry, t_fullpath, t_entry, dir_baton, e_fullpath, info, info ? info->depth @@ -1242,13 +1246,13 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, hi; hi = apr_hash_next(hi)) { - const svn_fs_dirent_t *s_entry; + const svn_fs_dirent_t *s_entry = apr_hash_this_val(hi); svn_pool_clear(iterpool); - s_entry = svn__apr_hash_index_val(hi); if (svn_hash_gets(t_entries, s_entry->name) == NULL) { + const char *e_fullpath; svn_revnum_t deleted_rev; if (s_entry->kind == svn_node_file @@ -1277,14 +1281,16 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, } /* Loop over the dirents in the target. */ - for (hi = apr_hash_first(subpool, t_entries); - hi; - hi = apr_hash_next(hi)) + SVN_ERR(svn_fs_dir_optimal_order(&t_ordered_entries, b->t_root, + t_entries, subpool, iterpool)); + for (i = 0; i < t_ordered_entries->nelts; ++i) { - const svn_fs_dirent_t *s_entry, *t_entry; + const svn_fs_dirent_t *t_entry + = APR_ARRAY_IDX(t_ordered_entries, i, svn_fs_dirent_t *); + const svn_fs_dirent_t *s_entry; + const char *s_fullpath, *t_fullpath, *e_fullpath; svn_pool_clear(iterpool); - t_entry = svn__apr_hash_index_val(hi); if (is_depth_upgrade(wc_depth, requested_depth, t_entry->kind)) { @@ -1305,11 +1311,9 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, || requested_depth == svn_depth_files)) continue; - /* Look for an entry with the same name - in the source dirents. */ + /* Look for an entry with the same name in the source dirents. */ s_entry = s_entries ? - svn_hash_gets(s_entries, t_entry->name) - : NULL; + svn_hash_gets(s_entries, t_entry->name) : NULL; s_fullpath = s_entry ? svn_fspath__join(s_path, t_entry->name, iterpool) : NULL; } @@ -1325,9 +1329,7 @@ delta_dirs(report_baton_t *b, svn_revnum_t s_rev, const char *s_path, iterpool)); } - - /* Destroy iteration subpool. */ - svn_pool_destroy(iterpool); + /* iterpool is destroyed by destroying its parent (subpool) below */ } svn_pool_destroy(subpool); diff --git a/contrib/subversion/subversion/libsvn_repos/repos.c b/contrib/subversion/subversion/libsvn_repos/repos.c index 9f10c068e..dc0caf5b5 100644 --- a/contrib/subversion/subversion/libsvn_repos/repos.c +++ b/contrib/subversion/subversion/libsvn_repos/repos.c @@ -267,9 +267,9 @@ create_locks(svn_repos_t *repos, apr_pool_t *pool) #define HOOKS_ENVIRONMENT_TEXT \ - "# The hook program typically does not inherit the environment of" NL \ - "# its parent process. For example, a common problem is for the" NL \ - "# PATH environment variable to not be set to its usual value, so" NL \ + "# The hook program runs in an empty environment, unless the server is" NL \ + "# explicitly configured otherwise. For example, a common problem is for" NL \ + "# the PATH environment variable to not be set to its usual value, so" NL \ "# that subprograms fail to launch unless invoked via absolute path." NL \ "# If you're having unexpected problems with a hook program, the" NL \ "# culprit may be unusual (or missing) environment variables." NL @@ -280,11 +280,87 @@ create_locks(svn_repos_t *repos, apr_pool_t *pool) "# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and" NL \ "# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/" NL +#define HOOKS_QUOTE_ARGUMENTS_TEXT \ + "# CAUTION:" NL \ + "# For security reasons, you MUST always properly quote arguments when" NL \ + "# you use them, as those arguments could contain whitespace or other" NL \ + "# problematic characters. Additionally, you should delimit the list" NL \ + "# of options with \"--\" before passing the arguments, so malicious" NL \ + "# clients cannot bootleg unexpected options to the commands your" NL \ + "# script aims to execute." NL \ + "# For similar reasons, you should also add a trailing @ to URLs which" NL \ + "# are passed to SVN commands accepting URLs with peg revisions." NL + +/* Return template text for a hook script named SCRIPT_NAME. Include + * DESCRIPTION and SCRIPT in the template text. + */ +static const char * +hook_template_text(const char *script_name, + const char *description, + const char *script, + apr_pool_t *result_pool) +{ + return apr_pstrcat(result_pool, +"#!/bin/sh" NL +"" NL, + description, +"#" NL +"# The default working directory for the invocation is undefined, so" NL +"# the program should set one explicitly if it cares." NL +"#" NL +"# On a Unix system, the normal procedure is to have '", script_name, "'" NL +"# invoke other programs to do the real work, though it may do the" NL +"# work itself too." NL +"#" NL +"# Note that '", script_name, "' must be executable by the user(s) who will" NL +"# invoke it (typically the user httpd runs as), and that user must" NL +"# have filesystem-level permission to access the repository." NL +"#" NL +"# On a Windows system, you should name the hook program" NL +"# '", script_name, ".bat' or '", script_name, ".exe'," NL +"# but the basic idea is the same." NL +"#" NL +HOOKS_ENVIRONMENT_TEXT +"#" NL +HOOKS_QUOTE_ARGUMENTS_TEXT +"#" NL +"# Here is an example hook script, for a Unix /bin/sh interpreter." NL +PREWRITTEN_HOOKS_TEXT +"" NL +"" NL, + script, + SVN_VA_NULL); +} + +/* Write a template file for a hook script named SCRIPT_NAME (appending + * '.tmpl' to that name) in REPOS. Include DESCRIPTION and SCRIPT in the + * template text. + */ +static svn_error_t * +write_hook_template_file(svn_repos_t *repos, const char *script_name, + const char *description, + const char *script, + apr_pool_t *pool) +{ + const char *template_path + = svn_dirent_join(repos->hook_path, + apr_psprintf(pool, "%s%s", + script_name, SVN_REPOS__HOOK_DESC_EXT), + pool); + const char *contents + = hook_template_text(script_name, description, script, pool); + + SVN_ERR(svn_io_file_create(template_path, contents, pool)); + SVN_ERR(svn_io_set_file_executable(template_path, TRUE, FALSE, pool)); + return SVN_NO_ERROR; +} +/* Write the hook template files in REPOS. + */ static svn_error_t * create_hooks(svn_repos_t *repos, apr_pool_t *pool) { - const char *this_path, *contents; + const char *description, *script; /* Create the hook directory. */ SVN_ERR_W(create_repos_dir(repos->hook_path, pool), @@ -293,16 +369,9 @@ create_hooks(svn_repos_t *repos, apr_pool_t *pool) /*** Write a default template for each standard hook file. */ /* Start-commit hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_start_commit_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_START_COMMIT - contents = -"#!/bin/sh" NL -"" NL + description = "# START-COMMIT HOOK" NL "#" NL "# The start-commit hook is invoked immediately after a Subversion txn is" NL @@ -333,31 +402,10 @@ create_hooks(svn_repos_t *repos, apr_pool_t *pool) "# make security assumptions based on the capabilities list, nor should" NL "# you assume that clients reliably report every capability they have." NL "#" NL -"# The working directory for this hook program's invocation is undefined," NL -"# so the program should set one explicitly if it cares." NL -"#" NL "# If the hook program exits with success, the commit continues; but" NL "# if it exits with failure (non-zero), the commit is stopped before" NL -"# a Subversion txn is created, and STDERR is returned to the client." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"# " NL -HOOKS_ENVIRONMENT_TEXT -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter." NL -PREWRITTEN_HOOKS_TEXT -"" NL -"" NL +"# a Subversion txn is created, and STDERR is returned to the client." NL; + script = "REPOS=\"$1\"" NL "USER=\"$2\"" NL "" NL @@ -367,25 +415,17 @@ PREWRITTEN_HOOKS_TEXT "# All checks passed, so allow the commit." NL "exit 0" NL; -#undef SCRIPT_NAME + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating start-commit hook")); - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - _("Creating start-commit hook")); +#undef SCRIPT_NAME - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end start-commit hook */ /* Pre-commit hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_pre_commit_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_PRE_COMMIT - contents = -"#!/bin/sh" NL -"" NL + description = "# PRE-COMMIT HOOK" NL "#" NL "# The pre-commit hook is invoked before a Subversion txn is" NL @@ -407,18 +447,11 @@ PREWRITTEN_HOOKS_TEXT "# by the separator character '|', followed by the lock token string," NL "# followed by a newline." NL "#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL -"#" NL "# If the hook program exits with success, the txn is committed; but" NL "# if it exits with failure (non-zero), the txn is aborted, no commit" NL "# takes place, and STDERR is returned to the client. The hook" NL "# program can use the 'svnlook' utility to help it examine the txn." NL "#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL "# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT ***" NL "# *** FOR REVISION PROPERTIES (like svn:log or svn:author). ***" NL "#" NL @@ -427,22 +460,8 @@ PREWRITTEN_HOOKS_TEXT "# hooks should not modify the versioned data in txns, or else come" NL "# up with a mechanism to make it safe to do so (by informing the" NL "# committing client of the changes). However, right now neither" NL -"# mechanism is implemented, so hook writers just have to be careful." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"#" NL -HOOKS_ENVIRONMENT_TEXT -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter." NL -PREWRITTEN_HOOKS_TEXT -"" NL -"" NL +"# mechanism is implemented, so hook writers just have to be careful." NL; + script = "REPOS=\"$1\"" NL "TXN=\"$2\"" NL "" NL @@ -459,26 +478,17 @@ PREWRITTEN_HOOKS_TEXT "# All checks passed, so allow the commit." NL "exit 0" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - _("Creating pre-commit hook")); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating pre-commit hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end pre-commit hook */ +#undef SCRIPT_NAME /* Pre-revprop-change hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_pre_revprop_change_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_PRE_REVPROP_CHANGE - contents = -"#!/bin/sh" NL -"" NL + description = "# PRE-REVPROP-CHANGE HOOK" NL "#" NL "# The pre-revprop-change hook is invoked before a revision property" NL @@ -506,26 +516,8 @@ PREWRITTEN_HOOKS_TEXT "# will behave as if the hook were present, but failed. The reason" NL "# for this is that revision properties are UNVERSIONED, meaning that" NL "# a successful propchange is destructive; the old value is gone" NL -"# forever. We recommend the hook back up the old value somewhere." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"#" NL -HOOKS_ENVIRONMENT_TEXT -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter." NL -PREWRITTEN_HOOKS_TEXT -"" NL -"" NL +"# forever. We recommend the hook back up the old value somewhere." NL; + script = "REPOS=\"$1\"" NL "REV=\"$2\"" NL "USER=\"$3\"" NL @@ -537,26 +529,17 @@ PREWRITTEN_HOOKS_TEXT "echo \"Changing revision properties other than svn:log is prohibited\" >&2" NL "exit 1" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - _("Creating pre-revprop-change hook")); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating pre-revprop-change hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end pre-revprop-change hook */ +#undef SCRIPT_NAME /* Pre-lock hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_pre_lock_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_PRE_LOCK - contents = -"#!/bin/sh" NL -"" NL + description = "# PRE-LOCK HOOK" NL "#" NL "# The pre-lock hook is invoked before an exclusive lock is" NL @@ -575,27 +558,10 @@ PREWRITTEN_HOOKS_TEXT "# this feature, you must guarantee the tokens generated are unique across" NL "# the repository each time." NL "#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL -"#" NL "# If the hook program exits with success, the lock is created; but" NL "# if it exits with failure (non-zero), the lock action is aborted" NL -"# and STDERR is returned to the client." NL -"" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"#" NL -"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL -"" NL +"# and STDERR is returned to the client." NL; + script = "REPOS=\"$1\"" NL "PATH=\"$2\"" NL "USER=\"$3\"" NL @@ -629,26 +595,17 @@ PREWRITTEN_HOOKS_TEXT "echo \"Error: $PATH already locked by ${LOCK_OWNER}.\" 1>&2" NL "exit 1" NL; -#undef SCRIPT_NAME + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating pre-lock hook")); - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - "Creating pre-lock hook"); - - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end pre-lock hook */ +#undef SCRIPT_NAME /* Pre-unlock hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_pre_unlock_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_PRE_UNLOCK - contents = -"#!/bin/sh" NL -"" NL + description = "# PRE-UNLOCK HOOK" NL "#" NL "# The pre-unlock hook is invoked before an exclusive lock is" NL @@ -662,27 +619,10 @@ PREWRITTEN_HOOKS_TEXT "# [4] TOKEN (the lock token to be destroyed)" NL "# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0)" NL "#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL -"#" NL "# If the hook program exits with success, the lock is destroyed; but" NL "# if it exits with failure (non-zero), the unlock action is aborted" NL -"# and STDERR is returned to the client." NL -"" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"#" NL -"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL -"" NL +"# and STDERR is returned to the client." NL; + script = "REPOS=\"$1\"" NL "PATH=\"$2\"" NL "USER=\"$3\"" NL @@ -713,27 +653,17 @@ PREWRITTEN_HOOKS_TEXT "echo \"Error: $PATH locked by ${LOCK_OWNER}.\" 1>&2" NL "exit 1" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - "Creating pre-unlock hook"); - - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end pre-unlock hook */ + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating pre-unlock hook")); +#undef SCRIPT_NAME /* Post-commit hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_post_commit_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_POST_COMMIT - contents = -"#!/bin/sh" NL -"" NL + description = "# POST-COMMIT HOOK" NL "#" NL "# The post-commit hook is invoked after a commit. Subversion runs" NL @@ -745,58 +675,28 @@ PREWRITTEN_HOOKS_TEXT "# [2] REV (the number of the revision just committed)" NL "# [3] TXN-NAME (the name of the transaction that has become REV)" NL "#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL -"#" NL "# Because the commit has already completed and cannot be undone," NL "# the exit code of the hook program is ignored. The hook program" NL "# can use the 'svnlook' utility to help it examine the" NL -"# newly-committed tree." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"# " NL -HOOKS_ENVIRONMENT_TEXT -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter." NL -PREWRITTEN_HOOKS_TEXT -"" NL -"" NL +"# newly-committed tree." NL; + script = "REPOS=\"$1\"" NL "REV=\"$2\"" NL "TXN_NAME=\"$3\"" NL NL "mailer.py commit \"$REPOS\" \"$REV\" /path/to/mailer.conf" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - _("Creating post-commit hook")); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating post-commit hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end post-commit hook */ +#undef SCRIPT_NAME /* Post-lock hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_post_lock_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_POST_LOCK - contents = -"#!/bin/sh" NL -"" NL + description = "# POST-LOCK HOOK" NL "#" NL "# The post-lock hook is run after a path is locked. Subversion runs" NL @@ -807,59 +707,31 @@ PREWRITTEN_HOOKS_TEXT "# [1] REPOS-PATH (the path to this repository)" NL "# [2] USER (the user who created the lock)" NL "#" NL -"# The paths that were just locked are passed to the hook via STDIN (as" NL -"# of Subversion 1.2, only one path is passed per invocation, but the" NL -"# plan is to pass all locked paths at once, so the hook program" NL -"# should be written accordingly)." NL -"#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL +"# The paths that were just locked are passed to the hook via STDIN." NL "#" NL -"# Because the lock has already been created and cannot be undone," NL +"# Because the locks have already been created and cannot be undone," NL "# the exit code of the hook program is ignored. The hook program" NL -"# can use the 'svnlook' utility to help it examine the" NL -"# newly-created lock." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL -"" NL +"# can use the 'svnlook' utility to examine the paths in the repository" NL +"# but since the hook is invoked asyncronously the newly-created locks" NL +"# may no longer be present." NL; + script = "REPOS=\"$1\"" NL "USER=\"$2\"" NL "" NL "# Send email to interested parties, let them know a lock was created:" NL "mailer.py lock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - "Creating post-lock hook"); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating post-lock hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end post-lock hook */ +#undef SCRIPT_NAME /* Post-unlock hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_post_unlock_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_POST_UNLOCK - contents = -"#!/bin/sh" NL -"" NL + description = "# POST-UNLOCK HOOK" NL "#" NL "# The post-unlock hook runs after a path is unlocked. Subversion runs" NL @@ -870,57 +742,28 @@ PREWRITTEN_HOOKS_TEXT "# [1] REPOS-PATH (the path to this repository)" NL "# [2] USER (the user who destroyed the lock)" NL "#" NL -"# The paths that were just unlocked are passed to the hook via STDIN" NL -"# (as of Subversion 1.2, only one path is passed per invocation, but" NL -"# the plan is to pass all unlocked paths at once, so the hook program" NL -"# should be written accordingly)." NL -"#" NL -"# The default working directory for the invocation is undefined, so" NL -"# the program should set one explicitly if it cares." NL +"# The paths that were just unlocked are passed to the hook via STDIN." NL "#" NL "# Because the lock has already been destroyed and cannot be undone," NL -"# the exit code of the hook program is ignored." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL -"" NL +"# the exit code of the hook program is ignored." NL; + script = "REPOS=\"$1\"" NL "USER=\"$2\"" NL "" NL "# Send email to interested parties, let them know a lock was removed:" NL "mailer.py unlock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - "Creating post-unlock hook"); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating post-unlock hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end post-unlock hook */ +#undef SCRIPT_NAME /* Post-revprop-change hook. */ - { - this_path = apr_psprintf(pool, "%s%s", - svn_repos_post_revprop_change_hook(repos, pool), - SVN_REPOS__HOOK_DESC_EXT); - #define SCRIPT_NAME SVN_REPOS__HOOK_POST_REVPROP_CHANGE - contents = -"#!/bin/sh" NL -"" NL + description = "# POST-REVPROP-CHANGE HOOK" NL "#" NL "# The post-revprop-change hook is invoked after a revision property" NL @@ -940,26 +783,8 @@ PREWRITTEN_HOOKS_TEXT "# Because the propchange has already completed and cannot be undone," NL "# the exit code of the hook program is ignored. The hook program" NL "# can use the 'svnlook' utility to help it examine the" NL -"# new property value." NL -"#" NL -"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL -"# invoke other programs to do the real work, though it may do the" NL -"# work itself too." NL -"#" NL -"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL -"# invoke it (typically the user httpd runs as), and that user must" NL -"# have filesystem-level permission to access the repository." NL -"#" NL -"# On a Windows system, you should name the hook program" NL -"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL -"# but the basic idea is the same." NL -"# " NL -HOOKS_ENVIRONMENT_TEXT -"# " NL -"# Here is an example hook script, for a Unix /bin/sh interpreter." NL -PREWRITTEN_HOOKS_TEXT -"" NL -"" NL +"# new property value." NL; + script = "REPOS=\"$1\"" NL "REV=\"$2\"" NL "USER=\"$3\"" NL @@ -969,13 +794,11 @@ PREWRITTEN_HOOKS_TEXT "mailer.py propchange2 \"$REPOS\" \"$REV\" \"$USER\" \"$PROPNAME\" " "\"$ACTION\" /path/to/mailer.conf" NL; -#undef SCRIPT_NAME - - SVN_ERR_W(svn_io_file_create(this_path, contents, pool), - _("Creating post-revprop-change hook")); + SVN_ERR_W(write_hook_template_file(repos, SCRIPT_NAME, + description, script, pool), + _("Creating post-revprop-change hook")); - SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); - } /* end post-revprop-change hook */ +#undef SCRIPT_NAME return SVN_NO_ERROR; } @@ -1025,11 +848,16 @@ create_conf(svn_repos_t *repos, apr_pool_t *pool) "### no path-based access control is done." NL "### Uncomment the line below to use the default authorization file." NL "# authz-db = " SVN_REPOS__CONF_AUTHZ NL -"### The groups-db option controls the location of the groups file." NL -"### Unless you specify a path starting with a /, the file's location is" NL -"### relative to the directory containing this file. The specified path" NL -"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL -"### text file in a Subversion repository." NL +"### The groups-db option controls the location of the file with the" NL +"### group definitions and allows maintaining groups separately from the" NL +"### authorization rules. The groups-db file is of the same format as the" NL +"### authz-db file and should contain a single [groups] section with the" NL +"### group definitions. If the option is enabled, the authz-db file cannot" NL +"### contain a [groups] section. Unless you specify a path starting with" NL +"### a /, the file's location is relative to the directory containing this" NL +"### file. The specified path may be a repository relative URL (^/) or an" NL +"### absolute file:// URL to a text file in a Subversion repository." NL +"### This option is not being used by default." NL "# groups-db = " SVN_REPOS__CONF_GROUPS NL "### This option specifies the authentication realm of the repository." NL "### If two repositories have the same authentication realm, they should" NL @@ -1306,15 +1134,16 @@ svn_repos_create(svn_repos_t **repos_p, const char *unused_2, apr_hash_t *config, apr_hash_t *fs_config, - apr_pool_t *pool) + apr_pool_t *result_pool) { svn_repos_t *repos; svn_error_t *err; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); const char *root_path; const char *local_abspath; /* Allocate a repository object, filling in the format we will create. */ - repos = create_svn_repos_t(path, pool); + repos = create_svn_repos_t(path, result_pool); repos->format = SVN_REPOS__FORMAT_NUMBER; /* Discover the type of the filesystem we are about to create. */ @@ -1324,48 +1153,56 @@ svn_repos_create(svn_repos_t **repos_p, repos->format = SVN_REPOS__FORMAT_NUMBER_LEGACY; /* Don't create a repository inside another repository. */ - SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - root_path = svn_repos_find_root_path(local_abspath, pool); + SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); + root_path = svn_repos_find_root_path(local_abspath, scratch_pool); if (root_path != NULL) { if (strcmp(root_path, local_abspath) == 0) return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("'%s' is an existing repository"), - svn_dirent_local_style(root_path, pool)); + svn_dirent_local_style(root_path, + scratch_pool)); else return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("'%s' is a subdirectory of an existing " "repository " "rooted at '%s'"), - svn_dirent_local_style(local_abspath, pool), - svn_dirent_local_style(root_path, pool)); + svn_dirent_local_style(local_abspath, + scratch_pool), + svn_dirent_local_style(root_path, + scratch_pool)); } /* Create the various files and subdirectories for the repository. */ - SVN_ERR_W(create_repos_structure(repos, path, fs_config, pool), + SVN_ERR_W(create_repos_structure(repos, path, fs_config, scratch_pool), _("Repository creation failed")); /* Lock if needed. */ - SVN_ERR(lock_repos(repos, FALSE, FALSE, pool)); + SVN_ERR(lock_repos(repos, FALSE, FALSE, scratch_pool)); /* Create an environment for the filesystem. */ - if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, pool))) + if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, + result_pool))) { /* If there was an error making the filesytem, e.g. unknown/supported * filesystem type. Clean up after ourselves. Yes this is safe because * create_repos_structure will fail if the path existed before we started * so we can't accidentally remove a directory that previously existed. */ + svn_pool_destroy(scratch_pool); /* Release lock to allow deleting dir */ return svn_error_trace( svn_error_compose_create( err, - svn_io_remove_dir2(path, FALSE, NULL, NULL, pool))); + svn_io_remove_dir2(path, FALSE, NULL, NULL, + result_pool))); } /* This repository is ready. Stamp it with a format number. */ SVN_ERR(svn_io_write_version_file - (svn_dirent_join(path, SVN_REPOS__FORMAT, pool), - repos->format, pool)); + (svn_dirent_join(path, SVN_REPOS__FORMAT, scratch_pool), + repos->format, scratch_pool)); + + svn_pool_destroy(scratch_pool); /* Release lock */ *repos_p = repos; return SVN_NO_ERROR; @@ -1450,25 +1287,29 @@ get_repos(svn_repos_t **repos_p, svn_boolean_t nonblocking, svn_boolean_t open_fs, apr_hash_t *fs_config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_repos_t *repos; + const char *fs_type; /* Allocate a repository object. */ - repos = create_svn_repos_t(path, pool); + repos = create_svn_repos_t(path, result_pool); /* Verify the validity of our repository format. */ - SVN_ERR(check_repos_format(repos, pool)); + SVN_ERR(check_repos_format(repos, scratch_pool)); /* Discover the FS type. */ - SVN_ERR(svn_fs_type(&repos->fs_type, repos->db_path, pool)); + SVN_ERR(svn_fs_type(&fs_type, repos->db_path, scratch_pool)); + repos->fs_type = apr_pstrdup(result_pool, fs_type); /* Lock if needed. */ - SVN_ERR(lock_repos(repos, exclusive, nonblocking, pool)); + SVN_ERR(lock_repos(repos, exclusive, nonblocking, result_pool)); /* Open up the filesystem only after obtaining the lock. */ if (open_fs) - SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, fs_config, pool)); + SVN_ERR(svn_fs_open2(&repos->fs, repos->db_path, fs_config, + result_pool, scratch_pool)); #ifdef SVN_DEBUG_CRASH_AT_REPOS_OPEN /* If $PATH/config/debug-abort exists, crash the server here. @@ -1479,8 +1320,8 @@ get_repos(svn_repos_t **repos_p, { svn_node_kind_t kind; svn_error_t *err = svn_io_check_path( - svn_dirent_join(repos->conf_path, "debug-abort", pool), - &kind, pool); + svn_dirent_join(repos->conf_path, "debug-abort", scratch_pool), + &kind, scratch_pool); svn_error_clear(err); if (!err && kind == svn_node_file) SVN_ERR_MALFUNCTION_NO_RETURN(); @@ -1521,19 +1362,68 @@ svn_repos_find_root_path(const char *path, return candidate; } - svn_error_t * -svn_repos_open2(svn_repos_t **repos_p, +svn_repos_open3(svn_repos_t **repos_p, const char *path, apr_hash_t *fs_config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { /* Fetch a repository object initialized with a shared read/write lock on the database. */ - return get_repos(repos_p, path, FALSE, FALSE, TRUE, fs_config, pool); + return get_repos(repos_p, path, FALSE, FALSE, TRUE, fs_config, + result_pool, scratch_pool); } +/* Baton used with fs_upgrade_notify, specifying the svn_repos layer + * notification parameters. + */ +struct fs_upgrade_notify_baton_t +{ + svn_repos_notify_func_t notify_func; + void *notify_baton; +}; + +/* Implements svn_fs_upgrade_notify_t as forwarding to a + * svn_repos_notify_func_t passed in a fs_upgrade_notify_baton_t* BATON. + */ +static svn_error_t * +fs_upgrade_notify(void *baton, + apr_uint64_t number, + svn_fs_upgrade_notify_action_t action, + apr_pool_t *pool) +{ + struct fs_upgrade_notify_baton_t *fs_baton = baton; + + svn_repos_notify_t *notify = svn_repos_notify_create( + svn_repos_notify_mutex_acquired, pool); + switch(action) + { + case svn_fs_upgrade_pack_revprops: + notify->shard = number; + notify->action = svn_repos_notify_pack_revprops; + break; + + case svn_fs_upgrade_cleanup_revprops: + notify->shard = number; + notify->action = svn_repos_notify_cleanup_revprops; + break; + + case svn_fs_upgrade_format_bumped: + notify->revision = number; + notify->action = svn_repos_notify_format_bumped; + break; + + default: + /* unknown notification */ + SVN_ERR_MALFUNCTION(); + } + + fs_baton->notify_func(fs_baton->notify_baton, notify, pool); + + return SVN_NO_ERROR; +} svn_error_t * svn_repos_upgrade2(const char *path, @@ -1547,12 +1437,17 @@ svn_repos_upgrade2(const char *path, int format; apr_pool_t *subpool = svn_pool_create(pool); + struct fs_upgrade_notify_baton_t fs_notify_baton; + fs_notify_baton.notify_func = notify_func; + fs_notify_baton.notify_baton = notify_baton; + /* Fetch a repository object; for the Berkeley DB backend, it is initialized with an EXCLUSIVE lock on the database. This will at least prevent others from trying to read or write to it while we run recovery. (Other backends should do their own locking; see lock_repos.) */ - SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, FALSE, NULL, subpool)); + SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, FALSE, NULL, subpool, + subpool)); if (notify_func) { @@ -1575,7 +1470,9 @@ svn_repos_upgrade2(const char *path, SVN_ERR(svn_io_write_version_file(format_path, format, subpool)); /* Try to upgrade the filesystem. */ - SVN_ERR(svn_fs_upgrade(repos->db_path, subpool)); + SVN_ERR(svn_fs_upgrade2(repos->db_path, + notify_func ? fs_upgrade_notify : NULL, + &fs_notify_baton, NULL, NULL, subpool)); /* Now overwrite our format file with the latest version. */ SVN_ERR(svn_io_write_version_file(format_path, SVN_REPOS__FORMAT_NUMBER, @@ -1598,7 +1495,7 @@ svn_repos_delete(const char *path, SVN_ERR(svn_fs_delete_fs(db_path, pool)); /* ...then blow away everything else. */ - return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); + return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); } @@ -1677,6 +1574,63 @@ svn_repos_has_capability(svn_repos_t *repos, return SVN_NO_ERROR; } +svn_error_t * +svn_repos_capabilities(apr_hash_t **capabilities, + svn_repos_t *repos, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + static const char *const queries[] = { + SVN_REPOS_CAPABILITY_MERGEINFO, + NULL + }; + const char *const *i; + + *capabilities = apr_hash_make(result_pool); + + for (i = queries; *i; i++) + { + svn_boolean_t has; + SVN_ERR(svn_repos_has_capability(repos, &has, *i, scratch_pool)); + if (has) + svn_hash_sets(*capabilities, *i, *i); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_repos_info_format(int *repos_format, + svn_version_t **supports_version, + svn_repos_t *repos, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *repos_format = repos->format; + *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); + + (*supports_version)->major = SVN_VER_MAJOR; + (*supports_version)->minor = 0; + (*supports_version)->patch = 0; + (*supports_version)->tag = ""; + + switch (repos->format) + { + case SVN_REPOS__FORMAT_NUMBER_LEGACY: + break; + case SVN_REPOS__FORMAT_NUMBER_1_4: + (*supports_version)->minor = 4; + break; +#ifdef SVN_DEBUG +# if SVN_REPOS__FORMAT_NUMBER != SVN_REPOS__FORMAT_NUMBER_1_4 +# error "Need to add a 'case' statement here" +# endif +#endif + } + + return SVN_NO_ERROR; +} + svn_fs_t * svn_repos_fs(svn_repos_t *repos) { @@ -1685,6 +1639,12 @@ svn_repos_fs(svn_repos_t *repos) return repos->fs; } +const char * +svn_repos_fs_type(svn_repos_t *repos, + apr_pool_t *result_pool) +{ + return apr_pstrdup(result_pool, repos->fs_type); +} /* For historical reasons, for the Berkeley DB backend, this code uses * repository locking, which is motivated by the need to support the @@ -1728,7 +1688,7 @@ svn_repos_recover4(const char *path, SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, FALSE, /* don't try to open the db yet. */ NULL, - subpool)); + subpool, subpool)); if (notify_func) { @@ -1756,6 +1716,9 @@ struct freeze_baton_t { int counter; svn_repos_freeze_func_t freeze_func; void *freeze_baton; + + /* Scratch pool used for every freeze callback invocation. */ + apr_pool_t *scratch_pool; }; static svn_error_t * @@ -1764,6 +1727,7 @@ multi_freeze(void *baton, { struct freeze_baton_t *fb = baton; + svn_pool_clear(fb->scratch_pool); if (fb->counter == fb->paths->nelts) { SVN_ERR(fb->freeze_func(fb->freeze_baton, pool)); @@ -1783,7 +1747,7 @@ multi_freeze(void *baton, TRUE /* exclusive (only applies to BDB) */, FALSE /* non-blocking */, FALSE /* open-fs */, - NULL, subpool)); + NULL, subpool, fb->scratch_pool)); if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) @@ -1796,7 +1760,8 @@ multi_freeze(void *baton, } else { - SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, NULL, subpool)); + SVN_ERR(svn_fs_open2(&repos->fs, repos->db_path, NULL, subpool, + fb->scratch_pool)); SVN_ERR(svn_fs_freeze(svn_repos_fs(repos), multi_freeze, fb, subpool)); } @@ -1825,9 +1790,11 @@ svn_repos_freeze(apr_array_header_t *paths, fb.counter = 0; fb.freeze_func = freeze_func; fb.freeze_baton = freeze_baton; + fb.scratch_pool = svn_pool_create(pool); SVN_ERR(multi_freeze(&fb, pool)); + svn_pool_destroy(fb.scratch_pool); return SVN_NO_ERROR; } @@ -1843,7 +1810,7 @@ svn_error_t *svn_repos_db_logfiles(apr_array_header_t **logfiles, FALSE, FALSE, FALSE, /* Do not open fs. */ NULL, - pool)); + pool, pool)); SVN_ERR(svn_fs_berkeley_logfiles(logfiles, svn_repos_db_env(repos, pool), @@ -1958,25 +1925,57 @@ lock_db_logs_file(svn_repos_t *repos, } +/* Baton used with fs_hotcopy_notify(), specifying the svn_repos layer + * notification parameters. + */ +struct fs_hotcopy_notify_baton_t +{ + svn_repos_notify_func_t notify_func; + void *notify_baton; +}; + +/* Implements svn_fs_hotcopy_notify_t as forwarding to a + * svn_repos_notify_func_t passed in a fs_hotcopy_notify_baton_t* BATON. + */ +static void +fs_hotcopy_notify(void *baton, + svn_revnum_t start_revision, + svn_revnum_t end_revision, + apr_pool_t *pool) +{ + struct fs_hotcopy_notify_baton_t *fs_baton = baton; + svn_repos_notify_t *notify; + + notify = svn_repos_notify_create(svn_repos_notify_hotcopy_rev_range, pool); + notify->start_revision = start_revision; + notify->end_revision = end_revision; + + fs_baton->notify_func(fs_baton->notify_baton, notify, pool); +} + /* Make a copy of a repository with hot backup of fs. */ svn_error_t * -svn_repos_hotcopy2(const char *src_path, +svn_repos_hotcopy3(const char *src_path, const char *dst_path, svn_boolean_t clean_logs, svn_boolean_t incremental, + svn_repos_notify_func_t notify_func, + void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { - svn_repos_t *src_repos; - svn_repos_t *dst_repos; + svn_fs_hotcopy_notify_t fs_notify_func; + struct fs_hotcopy_notify_baton_t fs_notify_baton; struct hotcopy_ctx_t hotcopy_context; - svn_error_t *err; const char *src_abspath; const char *dst_abspath; + svn_repos_t *src_repos; + svn_repos_t *dst_repos; + svn_error_t *err; - SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, pool)); - SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, pool)); + SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, scratch_pool)); + SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, scratch_pool)); if (strcmp(src_abspath, dst_abspath) == 0) return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, _("Hotcopy source and destination are equal")); @@ -1986,7 +1985,7 @@ svn_repos_hotcopy2(const char *src_path, FALSE, FALSE, FALSE, /* don't try to open the db yet. */ NULL, - pool)); + scratch_pool, scratch_pool)); /* If we are going to clean logs, then get an exclusive lock on db-logs.lock, to ensure that no one else will work with logs. @@ -1994,7 +1993,7 @@ svn_repos_hotcopy2(const char *src_path, If we are just copying, then get a shared lock to ensure that no one else will clean logs while we copying them */ - SVN_ERR(lock_db_logs_file(src_repos, clean_logs, pool)); + SVN_ERR(lock_db_logs_file(src_repos, clean_logs, scratch_pool)); /* Copy the repository to a new path, with exception of specially handled directories */ @@ -2008,16 +2007,16 @@ svn_repos_hotcopy2(const char *src_path, 0, hotcopy_structure, &hotcopy_context, - pool)); + scratch_pool)); /* Prepare dst_repos object so that we may create locks, so that we may open repository */ - dst_repos = create_svn_repos_t(dst_abspath, pool); + dst_repos = create_svn_repos_t(dst_abspath, scratch_pool); dst_repos->fs_type = src_repos->fs_type; dst_repos->format = src_repos->format; - err = create_locks(dst_repos, pool); + err = create_locks(dst_repos, scratch_pool); if (err) { if (incremental && err->apr_err == SVN_ERR_DIR_NOT_EMPTY) @@ -2026,7 +2025,8 @@ svn_repos_hotcopy2(const char *src_path, return svn_error_trace(err); } - err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT, pool); + err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT, + scratch_pool); if (err) { if (incremental && APR_STATUS_IS_EEXIST(err->apr_err)) @@ -2037,26 +2037,21 @@ svn_repos_hotcopy2(const char *src_path, /* Exclusively lock the new repository. No one should be accessing it at the moment */ - SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, pool)); + SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, scratch_pool)); - SVN_ERR(svn_fs_hotcopy2(src_repos->db_path, dst_repos->db_path, + fs_notify_func = notify_func ? fs_hotcopy_notify : NULL; + fs_notify_baton.notify_func = notify_func; + fs_notify_baton.notify_baton = notify_baton; + + SVN_ERR(svn_fs_hotcopy3(src_repos->db_path, dst_repos->db_path, clean_logs, incremental, - cancel_func, cancel_baton, pool)); + fs_notify_func, &fs_notify_baton, + cancel_func, cancel_baton, scratch_pool)); /* Destination repository is ready. Stamp it with a format number. */ return svn_io_write_version_file - (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, pool), - dst_repos->format, pool); -} - -svn_error_t * -svn_repos_hotcopy(const char *src_path, - const char *dst_path, - svn_boolean_t clean_logs, - apr_pool_t *pool) -{ - return svn_error_trace(svn_repos_hotcopy2(src_path, dst_path, clean_logs, - FALSE, NULL, NULL, pool)); + (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, scratch_pool), + dst_repos->format, scratch_pool); } /* Return the library version number. */ @@ -2077,7 +2072,6 @@ svn_repos_stat(svn_dirent_t **dirent, svn_node_kind_t kind; svn_dirent_t *ent; const char *datestring; - apr_hash_t *prophash; SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); @@ -2093,9 +2087,7 @@ svn_repos_stat(svn_dirent_t **dirent, if (kind == svn_node_file) SVN_ERR(svn_fs_file_length(&(ent->size), root, path, pool)); - SVN_ERR(svn_fs_node_proplist(&prophash, root, path, pool)); - if (apr_hash_count(prophash) > 0) - ent->has_props = TRUE; + SVN_ERR(svn_fs_node_has_props(&ent->has_props, root, path, pool)); SVN_ERR(svn_repos_get_committed_info(&(ent->created_rev), &datestring, diff --git a/contrib/subversion/subversion/libsvn_repos/repos.h b/contrib/subversion/subversion/libsvn_repos/repos.h index fd5b0b49a..b1039acd8 100644 --- a/contrib/subversion/subversion/libsvn_repos/repos.h +++ b/contrib/subversion/subversion/libsvn_repos/repos.h @@ -382,6 +382,11 @@ svn_repos__authz_read(svn_authz_t **authz_p, svn_boolean_t accept_urls, apr_pool_t *pool); +/* Walk the configuration in AUTHZ looking for any errors. */ +svn_error_t * +svn_repos__authz_validate(svn_authz_t *authz, + apr_pool_t *pool); + /*** Utility Functions ***/ diff --git a/contrib/subversion/subversion/libsvn_repos/rev_hunt.c b/contrib/subversion/subversion/libsvn_repos/rev_hunt.c index 2a8dc3ee9..d6cc49576 100644 --- a/contrib/subversion/subversion/libsvn_repos/rev_hunt.c +++ b/contrib/subversion/subversion/libsvn_repos/rev_hunt.c @@ -38,6 +38,8 @@ #include "svn_mergeinfo.h" #include "repos.h" #include "private/svn_fspath.h" +#include "private/svn_fs_private.h" +#include "private/svn_sorts_private.h" /* Note: this binary search assumes that the datestamp properties on @@ -171,12 +173,8 @@ svn_repos_get_committed_info(svn_revnum_t *committed_rev, 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_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE); + last_author_s = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR); *committed_date = committed_date_s ? committed_date_s->data : NULL; *last_author = last_author_s ? last_author_s->data : NULL; @@ -233,7 +231,7 @@ svn_repos_history2(svn_fs_t *fs, return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL); } - SVN_ERR(svn_fs_node_history(&history, root, path, oldpool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, oldpool, oldpool)); /* Now, we loop over the history items, calling svn_fs_history_prev(). */ do @@ -244,7 +242,8 @@ svn_repos_history2(svn_fs_t *fs, apr_pool_t *tmppool; svn_error_t *err; - SVN_ERR(svn_fs_history_prev(&history, history, cross_copies, newpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, cross_copies, newpool, + oldpool)); /* Only continue if there is further history to deal with. */ if (! history) @@ -311,12 +310,11 @@ svn_repos_deleted_rev(svn_fs_t *fs, svn_revnum_t *deleted, apr_pool_t *pool) { - apr_pool_t *subpool; - svn_fs_root_t *root, *copy_root; - const char *copy_path; + apr_pool_t *iterpool; + svn_fs_root_t *start_root, *root; svn_revnum_t mid_rev; - const svn_fs_id_t *start_node_id, *curr_node_id; - svn_error_t *err; + svn_node_kind_t kind; + svn_fs_node_relation_t node_relation; /* Validate the revision range. */ if (! SVN_IS_VALID_REVNUM(start)) @@ -337,32 +335,19 @@ svn_repos_deleted_rev(svn_fs_t *fs, } /* 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) + SVN_ERR(svn_fs_revision_root(&start_root, fs, start, pool)); + SVN_ERR(svn_fs_check_path(&kind, start_root, path, pool)); + if (kind == svn_node_none) { - 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); + /* Path must exist in fs at start rev. */ + *deleted = SVN_INVALID_REVNUM; + return SVN_NO_ERROR; } /* 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 + SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); + if (kind != svn_node_none) { /* path exists in the end node and the end node is equivalent or otherwise equivalent to the start node. This can mean @@ -389,9 +374,12 @@ svn_repos_deleted_rev(svn_fs_t *fs, 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_node_relation(&node_relation, start_root, path, + root, path, pool)); + if (node_relation != svn_fs_node_unrelated) { + svn_fs_root_t *copy_root; + const char *copy_path; SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, path, pool)); if (!copy_root || @@ -445,36 +433,33 @@ svn_repos_deleted_rev(svn_fs_t *fs, */ mid_rev = (start + end) / 2; - subpool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); while (1) { - svn_pool_clear(subpool); + svn_pool_clear(iterpool); /* 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) + SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, iterpool)); + SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool)); + if (kind == svn_node_none) { - 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); + /* Case D: Look lower in the range. */ + end = mid_rev; + mid_rev = (start + mid_rev) / 2; } else { + svn_fs_root_t *copy_root; + const char *copy_path; /* 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(©_root, ©_path, root, - path, subpool)); - if (cmp == -1 || + SVN_ERR(svn_fs_node_relation(&node_relation, start_root, path, + root, path, iterpool)); + if (node_relation != svn_fs_node_unrelated) + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, + path, iterpool)); + if (node_relation == svn_fs_node_unrelated || (copy_root && (svn_fs_revision_root_revision(copy_root) > start))) { @@ -497,7 +482,7 @@ svn_repos_deleted_rev(svn_fs_t *fs, } } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -542,7 +527,7 @@ check_ancestry_of_peg_path(svn_boolean_t *is_ancestor, SVN_ERR(svn_fs_revision_root(&root, fs, future_revision, pool)); - SVN_ERR(svn_fs_node_history(&history, root, fs_path, lastpool)); + SVN_ERR(svn_fs_node_history2(&history, root, fs_path, lastpool, lastpool)); /* Since paths that are different according to strcmp may still be equivalent (due to number of consecutive slashes and the fact that @@ -555,7 +540,8 @@ check_ancestry_of_peg_path(svn_boolean_t *is_ancestor, { apr_pool_t *tmppool; - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, currpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, currpool, + lastpool)); if (!history) break; @@ -657,14 +643,13 @@ svn_repos_trace_node_locations(svn_fs_t *fs, 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); + fs_path = apr_pstrcat(pool, "/", fs_path, SVN_VA_NULL); /* Another sanity check. */ if (authz_read_func) @@ -774,20 +759,22 @@ svn_repos_trace_node_locations(svn_fs_t *fs, 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)); + SVN_ERR(svn_fs_revision_root(&root, fs, revision, lastpool)); while (revision_ptr < revision_ptr_end) { svn_node_kind_t kind; - const svn_fs_id_t *lrev_id; + svn_fs_node_relation_t node_relation; + svn_fs_root_t *cur_rev_root; 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)); + SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs, *revision_ptr, + currpool)); + SVN_ERR(svn_fs_check_path(&kind, cur_rev_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)) + SVN_ERR(svn_fs_node_relation(&node_relation, root, path, + cur_rev_root, path, currpool)); + if (node_relation == svn_fs_node_unrelated) break; /* The node exists at the same path; record that and advance. */ @@ -878,7 +865,7 @@ svn_repos_node_location_segments(svn_repos_t *repos, /* 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); + path = apr_pstrcat(pool, "/", path, SVN_VA_NULL); /* Auth check. */ if (authz_read_func) @@ -942,7 +929,7 @@ svn_repos_node_location_segments(svn_repos_t *repos, /* authz_read_func requires path to have a leading slash. */ const char *abs_path = apr_pstrcat(subpool, "/", segment->path, - (char *)NULL); + SVN_VA_NULL); SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs, segment->range_end, subpool)); @@ -979,37 +966,6 @@ svn_repos_node_location_segments(svn_repos_t *repos, 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, @@ -1028,7 +984,8 @@ struct path_revision svn_revnum_t revnum; const char *path; - /* Does this path_rev have merges to also be included? */ + /* Does this path_rev have merges to also be included? If so, this is + the union of both additions and (negated) deletions of mergeinfo. */ apr_hash_t *merged_mergeinfo; /* Is this a merged revision? */ @@ -1037,6 +994,7 @@ struct path_revision /* Check for merges in OLD_PATH_REV->PATH at OLD_PATH_REV->REVNUM. Store the mergeinfo difference in *MERGED_MERGEINFO, allocated in POOL. The + difference is the union of both additions and (negated) deletions. The returned *MERGED_MERGEINFO will be NULL if there are no changes. */ static svn_error_t * get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, @@ -1047,7 +1005,7 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, { apr_hash_t *curr_mergeinfo, *prev_mergeinfo, *deleted, *changed; svn_error_t *err; - svn_fs_root_t *root; + svn_fs_root_t *root, *prev_root; apr_hash_t *changed_paths; const char *path = old_path_rev->path; @@ -1059,7 +1017,8 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, while (1) { svn_fs_path_change2_t *changed_path = svn_hash_gets(changed_paths, path); - if (changed_path && changed_path->prop_mod) + if (changed_path && changed_path->prop_mod + && changed_path->mergeinfo_mod != svn_tristate_false) break; if (svn_fspath__is_root(path, strlen(path))) { @@ -1071,9 +1030,13 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, /* 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); + /* 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() */ + err = svn_fs__get_mergeinfo_for_path(&curr_mergeinfo, + root, old_path_rev->path, + svn_mergeinfo_inherited, TRUE, + scratch_pool, scratch_pool); if (err) { if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) @@ -1091,9 +1054,12 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, } } - err = get_path_mergeinfo(&prev_mergeinfo, repos->fs, old_path_rev->path, - old_path_rev->revnum - 1, scratch_pool, - scratch_pool); + SVN_ERR(svn_fs_revision_root(&prev_root, repos->fs, old_path_rev->revnum - 1, + scratch_pool)); + err = svn_fs__get_mergeinfo_for_path(&prev_mergeinfo, + prev_root, old_path_rev->path, + svn_mergeinfo_inherited, TRUE, + scratch_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND || err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)) { @@ -1106,7 +1072,8 @@ get_merged_mergeinfo(apr_hash_t **merged_mergeinfo, else SVN_ERR(err); - /* Then calculate and merge the differences. */ + /* Then calculate and merge the differences, combining additions and + (negated) deletions as all positive changes in CHANGES. */ SVN_ERR(svn_mergeinfo_diff2(&deleted, &changed, prev_mergeinfo, curr_mergeinfo, FALSE, result_pool, scratch_pool)); @@ -1154,7 +1121,8 @@ find_interesting_revisions(apr_array_header_t *path_revisions, path, end); /* Open a history object. */ - SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, scratch_pool, + scratch_pool)); while (1) { struct path_revision *path_rev; @@ -1165,7 +1133,8 @@ find_interesting_revisions(apr_array_header_t *path_revisions, svn_pool_clear(iterpool); /* Fetch the history object to walk through. */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); if (!history) break; SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum, @@ -1287,16 +1256,14 @@ find_merged_revisions(apr_array_header_t **merged_path_revisions_out, for (hi = apr_hash_first(iterpool, old_pr->merged_mergeinfo); hi; hi = apr_hash_next(hi)) { + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(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, @@ -1343,8 +1310,7 @@ find_merged_revisions(apr_array_header_t **merged_path_revisions_out, 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); + svn_sort__array(merged_path_revisions, compare_path_revisions); /* Copy to the output array. */ *merged_path_revisions_out = apr_array_copy(result_pool, @@ -1363,6 +1329,7 @@ struct send_baton apr_hash_t *last_props; const char *last_path; svn_fs_root_t *last_root; + svn_boolean_t include_merged_revisions; }; /* Send PATH_REV to HANDLER and HANDLER_BATON, using information provided by @@ -1400,14 +1367,32 @@ send_path_revision(struct path_revision *path_rev, 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)); + /* Check if the contents *may* have changed. */ + if (! sb->last_root) + { + /* Special case: In the first revision, we always provide a delta. */ + contents_changed = TRUE; + } + else if (sb->include_merged_revisions + && strcmp(sb->last_path, path_rev->path)) + { + /* ### This is a HACK!!! + * Blame -g, in older clients anyways, relies on getting a notification + * whenever the path changes - even if there was no content change. + * + * TODO: A future release should take an extra parameter and depending + * on that either always send a text delta or only send it if there + * is a difference. */ + contents_changed = TRUE; + } else - contents_changed = TRUE; + { + /* Did the file contents actually change? + * It could e.g. be a property-only change. */ + SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root, + sb->last_path, root, path_rev->path, + sb->iterpool)); + } /* We have all we need, give to the handler. */ SVN_ERR(handler(handler_baton, path_rev->path, path_rev->revnum, @@ -1419,7 +1404,7 @@ send_path_revision(struct path_revision *path_rev, /* 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) + if (delta_handler && delta_handler != svn_delta_noop_window_handler) { /* Get the content delta. */ SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, @@ -1476,6 +1461,7 @@ get_file_revs_backwards(svn_repos_t *repos, last_pool = svn_pool_create(scratch_pool); sb.iterpool = svn_pool_create(scratch_pool); sb.last_pool = svn_pool_create(scratch_pool); + sb.include_merged_revisions = FALSE; /* We want the first txdelta to be against the empty file. */ sb.last_root = NULL; @@ -1493,7 +1479,7 @@ get_file_revs_backwards(svn_repos_t *repos, path, end); /* Open a history object. */ - SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, scratch_pool, iterpool)); while (1) { struct path_revision *path_rev; @@ -1503,7 +1489,8 @@ get_file_revs_backwards(svn_repos_t *repos, svn_pool_clear(iterpool); /* Fetch the history object to walk through. */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool)); + SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool, + iterpool)); if (!history) break; SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum, @@ -1591,6 +1578,18 @@ svn_repos_get_file_revs2(svn_repos_t *repos, struct send_baton sb; int mainline_pos, merged_pos; + if (!SVN_IS_VALID_REVNUM(start) + || !SVN_IS_VALID_REVNUM(end)) + { + svn_revnum_t youngest_rev; + SVN_ERR(svn_fs_youngest_rev(&youngest_rev, repos->fs, scratch_pool)); + + if (!SVN_IS_VALID_REVNUM(start)) + start = youngest_rev; + if (!SVN_IS_VALID_REVNUM(end)) + end = youngest_rev; + } + if (end < start) { if (include_merged_revisions) @@ -1618,6 +1617,9 @@ svn_repos_get_file_revs2(svn_repos_t *repos, /* Create an empty hash table for the first property diff. */ sb.last_props = apr_hash_make(sb.last_pool); + /* Inform send_path_revision() whether workarounds / special behavior + * may be needed. */ + sb.include_merged_revisions = include_merged_revisions; /* Get the revisions we are interested in. */ duplicate_path_revs = apr_hash_make(scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_subr/adler32.c b/contrib/subversion/subversion/libsvn_subr/adler32.c index e290e68e8..63dbb442f 100644 --- a/contrib/subversion/subversion/libsvn_subr/adler32.c +++ b/contrib/subversion/subversion/libsvn_subr/adler32.c @@ -57,7 +57,7 @@ svn__adler32(apr_uint32_t checksum, const char *data, apr_off_t len) */ if (len >= 80) { - /* Larger buffers can be effiently handled by Marc Adler's + /* Larger buffers can be efficiently handled by Marc Adler's * optimized code. Also, new zlib versions will come with * SIMD code for x86 and x64. */ @@ -76,16 +76,16 @@ svn__adler32(apr_uint32_t checksum, const char *data, apr_off_t len) * (approx. one clock tick per byte + 2 ticks loop overhead) */ for (; len >= 8; len -= 8, input += 8) - { - s1 += input[0]; s2 += s1; - s1 += input[1]; s2 += s1; - s1 += input[2]; s2 += s1; - s1 += input[3]; s2 += s1; - s1 += input[4]; s2 += s1; - s1 += input[5]; s2 += s1; - s1 += input[6]; s2 += s1; - s1 += input[7]; s2 += s1; - } + { + s1 += input[0]; s2 += s1; + s1 += input[1]; s2 += s1; + s1 += input[2]; s2 += s1; + s1 += input[3]; s2 += s1; + s1 += input[4]; s2 += s1; + s1 += input[5]; s2 += s1; + s1 += input[6]; s2 += s1; + s1 += input[7]; s2 += s1; + } /* Adler-32 calculation as a simple two ticks per iteration loop. */ diff --git a/contrib/subversion/subversion/libsvn_subr/auth.c b/contrib/subversion/subversion/libsvn_subr/auth.c index 3c874cf88..303c41e5c 100644 --- a/contrib/subversion/subversion/libsvn_subr/auth.c +++ b/contrib/subversion/subversion/libsvn_subr/auth.c @@ -37,7 +37,6 @@ #include "svn_version.h" #include "private/svn_auth_private.h" #include "private/svn_dep_compat.h" -#include "private/svn_subr_private.h" #include "auth.h" @@ -110,10 +109,10 @@ struct svn_auth_baton_t /* run-time parameters needed by providers. */ apr_hash_t *parameters; + apr_hash_t *slave_parameters; /* run-time credentials cache. */ apr_hash_t *creds_cache; - }; /* Abstracted iteration baton */ @@ -126,6 +125,7 @@ struct svn_auth_iterstate_t const char *realmstring; /* The original realmstring passed in */ const char *cache_key; /* key to use in auth_baton's creds_cache */ svn_auth_baton_t *auth_baton; /* the original auth_baton. */ + apr_hash_t *parameters; }; @@ -143,6 +143,7 @@ svn_auth_open(svn_auth_baton_t **auth_baton, ab = apr_pcalloc(pool, sizeof(*ab)); ab->tables = apr_hash_make(pool); ab->parameters = apr_hash_make(pool); + /* ab->slave_parameters = NULL; */ ab->creds_cache = apr_hash_make(pool); ab->pool = pool; @@ -171,20 +172,44 @@ svn_auth_open(svn_auth_baton_t **auth_baton, *auth_baton = ab; } - +/* Magic pointer value to allow storing 'NULL' in an apr_hash_t */ +static const void *auth_NULL = NULL; void svn_auth_set_parameter(svn_auth_baton_t *auth_baton, const char *name, const void *value) { - svn_hash_sets(auth_baton->parameters, name, value); + if (auth_baton) + { + if (auth_baton->slave_parameters) + { + if (!value) + value = &auth_NULL; + + svn_hash_sets(auth_baton->slave_parameters, name, value); + } + else + svn_hash_sets(auth_baton->parameters, name, value); + } } const void * svn_auth_get_parameter(svn_auth_baton_t *auth_baton, const char *name) { + const void *value; + if (!auth_baton) + return NULL; + else if (!auth_baton->slave_parameters) + return svn_hash_gets(auth_baton->parameters, name); + + value = svn_hash_gets(auth_baton->slave_parameters, name); + + if (value) + return (value == &auth_NULL) ? NULL + : value; + return svn_hash_gets(auth_baton->parameters, name); } @@ -196,7 +221,7 @@ make_cache_key(const char *cred_kind, const char *realmstring, apr_pool_t *pool) { - return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL); + return apr_pstrcat(pool, cred_kind, ":", realmstring, SVN_VA_NULL); } svn_error_t * @@ -215,6 +240,11 @@ svn_auth_first_credentials(void **credentials, svn_boolean_t got_first = FALSE; svn_auth_iterstate_t *iterstate; const char *cache_key; + apr_hash_t *parameters; + + if (! auth_baton) + return svn_error_create(SVN_ERR_AUTHN_NO_PROVIDER, NULL, + _("No authentication providers registered")); /* Get the appropriate table of providers for CRED_KIND. */ table = svn_hash_gets(auth_baton->tables, cred_kind); @@ -223,6 +253,26 @@ svn_auth_first_credentials(void **credentials, _("No provider registered for '%s' credentials"), cred_kind); + if (auth_baton->slave_parameters) + { + apr_hash_index_t *hi; + parameters = apr_hash_copy(pool, auth_baton->parameters); + + for (hi = apr_hash_first(pool, auth_baton->slave_parameters); + hi; + hi = apr_hash_next(hi)) + { + const void *value = apr_hash_this_val(hi); + + if (value == &auth_NULL) + value = NULL; + + svn_hash_sets(parameters, apr_hash_this_key(hi), value); + } + } + else + parameters = auth_baton->parameters; + /* First, see if we have cached creds in the auth_baton. */ cache_key = make_cache_key(cred_kind, realmstring, pool); creds = svn_hash_gets(auth_baton->creds_cache, cache_key); @@ -240,7 +290,7 @@ svn_auth_first_credentials(void **credentials, svn_auth_provider_object_t *); SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton, provider->provider_baton, - auth_baton->parameters, + parameters, realmstring, auth_baton->pool)); @@ -253,7 +303,9 @@ svn_auth_first_credentials(void **credentials, } if (! creds) - *state = NULL; + { + *state = NULL; + } else { /* Build an abstract iteration state. */ @@ -265,6 +317,7 @@ svn_auth_first_credentials(void **credentials, iterstate->realmstring = apr_pstrdup(pool, realmstring); iterstate->cache_key = cache_key; iterstate->auth_baton = auth_baton; + iterstate->parameters = parameters; *state = iterstate; /* Put the creds in the cache */ @@ -301,22 +354,26 @@ svn_auth_next_credentials(void **credentials, { SVN_ERR(provider->vtable->first_credentials( &creds, &(state->provider_iter_baton), - provider->provider_baton, auth_baton->parameters, + provider->provider_baton, state->parameters, state->realmstring, auth_baton->pool)); state->got_first = TRUE; } else if (provider->vtable->next_credentials) { - SVN_ERR(provider->vtable->next_credentials( - &creds, state->provider_iter_baton, - provider->provider_baton, auth_baton->parameters, - state->realmstring, auth_baton->pool)); + SVN_ERR(provider->vtable->next_credentials(&creds, + state->provider_iter_baton, + provider->provider_baton, + state->parameters, + state->realmstring, + auth_baton->pool)); } if (creds != NULL) { /* Put the creds in the cache */ - svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds); + svn_hash_sets(auth_baton->creds_cache, + apr_pstrdup(auth_baton->pool, state->cache_key), + creds); break; } @@ -349,7 +406,7 @@ svn_auth_save_credentials(svn_auth_iterstate_t *state, return SVN_NO_ERROR; /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */ - no_auth_cache = svn_hash_gets(auth_baton->parameters, + no_auth_cache = svn_hash_gets(state->parameters, SVN_AUTH_PARAM_NO_AUTH_CACHE); if (no_auth_cache) return SVN_NO_ERROR; @@ -362,7 +419,7 @@ svn_auth_save_credentials(svn_auth_iterstate_t *state, SVN_ERR(provider->vtable->save_credentials(&save_succeeded, creds, provider->provider_baton, - auth_baton->parameters, + state->parameters, state->realmstring, pool)); if (save_succeeded) @@ -376,12 +433,11 @@ svn_auth_save_credentials(svn_auth_iterstate_t *state, provider = APR_ARRAY_IDX(state->table->providers, i, svn_auth_provider_object_t *); if (provider->vtable->save_credentials) - SVN_ERR(provider->vtable->save_credentials - (&save_succeeded, creds, - provider->provider_baton, - auth_baton->parameters, - state->realmstring, - pool)); + SVN_ERR(provider->vtable->save_credentials(&save_succeeded, creds, + provider->provider_baton, + state->parameters, + state->realmstring, + pool)); if (save_succeeded) break; @@ -509,19 +565,19 @@ svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider, if (strcmp(provider_name, "gpg_agent") == 0 && strcmp(provider_type, "simple") == 0) { - svn_auth_get_gpg_agent_simple_provider(provider, pool); + svn_auth__get_gpg_agent_simple_provider(provider, pool); } #endif #ifdef SVN_HAVE_KEYCHAIN_SERVICES if (strcmp(provider_name, "keychain") == 0 && strcmp(provider_type, "simple") == 0) { - svn_auth_get_keychain_simple_provider(provider, pool); + svn_auth__get_keychain_simple_provider(provider, pool); } else if (strcmp(provider_name, "keychain") == 0 && strcmp(provider_type, "ssl_client_cert_pw") == 0) { - svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool); + svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool); } #endif @@ -529,20 +585,20 @@ svn_auth_get_platform_specific_provider(svn_auth_provider_object_t **provider, if (strcmp(provider_name, "windows") == 0 && strcmp(provider_type, "simple") == 0) { - svn_auth_get_windows_simple_provider(provider, pool); + svn_auth__get_windows_simple_provider(provider, pool); } else if (strcmp(provider_name, "windows") == 0 && strcmp(provider_type, "ssl_client_cert_pw") == 0) { - svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool); + svn_auth__get_windows_ssl_client_cert_pw_provider(provider, pool); } else if (strcmp(provider_name, "windows") == 0 && strcmp(provider_type, "ssl_server_trust") == 0) { - svn_auth_get_windows_ssl_server_trust_provider(provider, pool); + svn_auth__get_windows_ssl_server_trust_provider(provider, pool); } else if (strcmp(provider_name, "windows") == 0 && - strcmp(provider_type, "ssl_server_authority") == 0) + strcmp(provider_type, "ssl_server_authority") == 0) { svn_auth__get_windows_ssl_server_authority_provider(provider, pool); } @@ -656,5 +712,210 @@ svn_auth_get_platform_specific_client_providers(apr_array_header_t **providers, } } + /* Windows has two providers without a store to allow easy access to + SSL servers. We enable these unconditionally. + (This behavior was moved here from svn_cmdline_create_auth_baton()) */ + SVN_ERR(svn_auth_get_platform_specific_provider(&provider, + "windows", + "ssl_server_trust", + pool)); + SVN__MAYBE_ADD_PROVIDER(*providers, provider); + + /* The windows ssl authority certificate CRYPTOAPI provider. */ + SVN_ERR(svn_auth_get_platform_specific_provider(&provider, + "windows", + "ssl_server_authority", + pool)); + + SVN__MAYBE_ADD_PROVIDER(*providers, provider); + return SVN_NO_ERROR; } + +svn_error_t * +svn_auth__make_session_auth(svn_auth_baton_t **session_auth_baton, + const svn_auth_baton_t *auth_baton, + apr_hash_t *config, + const char *server_name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; + svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; + const char *store_plaintext_passwords + = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS; + svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP; + const char *store_pp_plaintext + = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT; + svn_config_t *servers = NULL; + const char *server_group = NULL; + + struct svn_auth_baton_t *ab; + + ab = apr_pmemdup(result_pool, auth_baton, sizeof(*ab)); + + ab->slave_parameters = apr_hash_make(result_pool); + + /* The 'store-passwords' and 'store-auth-creds' parameters used to + * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility, + * if values for these parameters have already been set by our + * callers, we use those values as defaults. + * + * Note that we can only catch the case where users explicitly set + * "store-passwords = no" or 'store-auth-creds = no". + * + * However, since the default value for both these options is + * currently (and has always been) "yes", users won't know + * the difference if they set "store-passwords = yes" or + * "store-auth-creds = yes" -- they'll get the expected behaviour. + */ + + if (svn_auth_get_parameter(ab, + SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL) + store_passwords = FALSE; + + if (svn_auth_get_parameter(ab, + SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL) + store_auth_creds = FALSE; + + /* All the svn_auth_set_parameter() calls below this not only affect the + to be created ra session, but also all the ra sessions that are already + use this auth baton! + + Please try to key things based on the realm string instead of this + construct. + */ + + if (config) + { + /* Grab the 'servers' config. */ + servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS); + if (servers) + { + /* First, look in the global section. */ + + SVN_ERR(svn_config_get_bool + (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_STORE_PASSWORDS, + store_passwords)); + + SVN_ERR(svn_config_get_yes_no_ask + (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, + SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); + + SVN_ERR(svn_config_get_bool + (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, + store_pp)); + + SVN_ERR(svn_config_get_yes_no_ask + (servers, &store_pp_plaintext, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, + SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT)); + + SVN_ERR(svn_config_get_bool + (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_STORE_AUTH_CREDS, + store_auth_creds)); + + /* Find out where we're about to connect to, and + * try to pick a server group based on the destination. */ + server_group = svn_config_find_group(servers, server_name, + SVN_CONFIG_SECTION_GROUPS, + scratch_pool); + + if (server_group) + { + /* Override global auth caching parameters with the ones + * for the server group, if any. */ + SVN_ERR(svn_config_get_bool(servers, &store_auth_creds, + server_group, + SVN_CONFIG_OPTION_STORE_AUTH_CREDS, + store_auth_creds)); + + SVN_ERR(svn_config_get_bool(servers, &store_passwords, + server_group, + SVN_CONFIG_OPTION_STORE_PASSWORDS, + store_passwords)); + + SVN_ERR(svn_config_get_yes_no_ask + (servers, &store_plaintext_passwords, server_group, + SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, + store_plaintext_passwords)); + + SVN_ERR(svn_config_get_bool + (servers, &store_pp, + server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, + store_pp)); + + SVN_ERR(svn_config_get_yes_no_ask + (servers, &store_pp_plaintext, server_group, + SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, + store_pp_plaintext)); + } + } + } + + /* Save auth caching parameters in the auth parameter hash. */ + if (! store_passwords) + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); + + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS, + store_plaintext_passwords); + + if (! store_pp) + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP, + ""); + + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, + store_pp_plaintext); + + if (! store_auth_creds) + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); + + if (server_group) + svn_auth_set_parameter(ab, + SVN_AUTH_PARAM_SERVER_GROUP, + apr_pstrdup(ab->pool, server_group)); + + *session_auth_baton = ab; + + return SVN_NO_ERROR; +} + + +static svn_error_t * +dummy_first_creds(void **credentials, + void **iter_baton, + void *provider_baton, + apr_hash_t *parameters, + const char *realmstring, + apr_pool_t *pool) +{ + *credentials = NULL; + *iter_baton = NULL; + return SVN_NO_ERROR; +} + +void +svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + static const svn_auth_provider_t vtable = { + SVN_AUTH_CRED_SIMPLE, + dummy_first_creds, + NULL, NULL + }; + + svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); + + po->vtable = &vtable; + *provider = po; +} diff --git a/contrib/subversion/subversion/libsvn_subr/auth.h b/contrib/subversion/subversion/libsvn_subr/auth.h index 0885f6d7f..bf8dfc93b 100644 --- a/contrib/subversion/subversion/libsvn_subr/auth.h +++ b/contrib/subversion/subversion/libsvn_subr/auth.h @@ -41,6 +41,130 @@ svn_auth__file_path(const char **path, const char *config_dir, apr_pool_t *pool); +#if (defined(WIN32) && !defined(__MINGW32__)) || defined(DOXYGEN) +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_simple_t that gets/sets information from the user's + * ~/.subversion configuration directory. Allocate @a *provider in + * @a pool. + * + * This is like svn_auth_get_simple_provider(), except that, when + * running on Window 2000 or newer (or any other Windows version that + * includes the CryptoAPI), the provider encrypts the password before + * storing it to disk. On earlier versions of Windows, the provider + * does nothing. + * + * @note This function is only available on Windows. + * + * @note An administrative password reset may invalidate the account's + * secret key. This function will detect that situation and behave as + * if the password were not cached at all. + */ +void +svn_auth__get_windows_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool); + +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_ssl_client_cert_pw_t that gets/sets information from the + * user's ~/.subversion configuration directory. Allocate @a *provider in + * @a pool. + * + * This is like svn_auth_get_ssl_client_cert_pw_file_provider(), except that + * when running on Window 2000 or newer, the provider encrypts the password + * before storing it to disk. On earlier versions of Windows, the provider + * does nothing. + * + * @note This function is only available on Windows. + * + * @note An administrative password reset may invalidate the account's + * secret key. This function will detect that situation and behave as + * if the password were not cached at all. + */ +void +svn_auth__get_windows_ssl_client_cert_pw_provider( + svn_auth_provider_object_t **provider, + apr_pool_t *pool); + +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_ssl_server_trust_t, allocated in @a pool. + * + * This provider automatically validates ssl server certificates with + * the CryptoApi, like Internet Explorer and the Windows network API do. + * This allows the rollout of root certificates via Windows Domain + * policies, instead of Subversion specific configuration. + * + * @note This function is only available on Windows. + */ +void +svn_auth__get_windows_ssl_server_trust_provider( + svn_auth_provider_object_t **provider, + apr_pool_t *pool); +#endif /* WIN32 && !__MINGW32__ || DOXYGEN */ + +#if defined(DARWIN) || defined(DOXYGEN) +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_simple_t that gets/sets information from the user's + * ~/.subversion configuration directory. Allocate @a *provider in + * @a pool. + * + * This is like svn_auth_get_simple_provider(), except that the + * password is stored in the Mac OS KeyChain. + * + * @note This function is only available on Mac OS 10.2 and higher. + */ +void +svn_auth__get_keychain_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool); + +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_ssl_client_cert_pw_t that gets/sets information from the + * user's ~/.subversion configuration directory. Allocate @a *provider in + * @a pool. + * + * This is like svn_auth_get_ssl_client_cert_pw_file_provider(), except + * that the password is stored in the Mac OS KeyChain. + * + * @note This function is only available on Mac OS 10.2 and higher. + */ +void +svn_auth__get_keychain_ssl_client_cert_pw_provider( + svn_auth_provider_object_t **provider, + apr_pool_t *pool); +#endif /* DARWIN || DOXYGEN */ + +#if !defined(WIN32) || defined(DOXYGEN) +/** + * Set @a *provider to an authentication provider of type @c + * svn_auth_cred_simple_t that gets/sets information from the user's + * ~/.subversion configuration directory. + * + * This is like svn_client_get_simple_provider(), except that the + * password is obtained from gpg_agent, which will keep it in + * a memory cache. + * + * Allocate @a *provider in @a pool. + * + * @note This function actually works only on systems with + * GNU Privacy Guard installed. + */ +void +svn_auth__get_gpg_agent_simple_provider + (svn_auth_provider_object_t **provider, + apr_pool_t *pool); +#endif /* !defined(WIN32) || defined(DOXYGEN) */ + +/** + * Set @a *provider to a dummy provider of type @c + * svn_auth_cred_simple_t that never returns or stores any + * credentials. + */ +void +svn_auth__get_dummmy_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool); #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/libsvn_subr/bit_array.c b/contrib/subversion/subversion/libsvn_subr/bit_array.c new file mode 100644 index 000000000..c239d1c9d --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/bit_array.c @@ -0,0 +1,194 @@ +/* + * bit_array.c : implement a simple packed bit array + * + * ==================================================================== + * 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_sorts.h" +#include "private/svn_subr_private.h" + +/* We allocate our data buffer in blocks of this size (in bytes). + * For performance reasons, this shall be a power of two. + * It should also not exceed 80kB (to prevent APR pool fragmentation) and + * not be too small (to keep the number of OS-side memory allocations low - + * avoiding hitting system-specific limits). + */ +#define BLOCK_SIZE 0x10000 + +/* Number of bits in each block. + */ +#define BLOCK_SIZE_BITS (8 * BLOCK_SIZE) + +/* Initial array size (covers INITIAL_BLOCK_COUNT * BLOCK_SIZE_BITS bits). + * For performance reasons, this shall be a power of two. + */ +#define INITIAL_BLOCK_COUNT 16 + +/* We store the bits in a lazily allocated two-dimensional array. + * For every BLOCK_SIZE_BITS range of indexes, there is one entry in the + * BLOCKS array. If index / BLOCK_SIZE_BITS exceeds BLOCK_COUNT-1, the + * blocks are implicitly empty. Only if a bit will be set to 1, will the + * BLOCKS array be auto-expanded. + * + * As long as no bit got set in a particular block, the respective entry in + * BLOCKS entry will be NULL, implying that all block contents is 0. + */ +struct svn_bit_array__t +{ + /* Data buffer of BLOCK_COUNT blocks, BLOCK_SIZE_BITS each. Never NULL. + * Every block may be NULL, though. */ + unsigned char **blocks; + + /* Number of bytes allocated to DATA. Never shrinks. */ + apr_size_t block_count; + + /* Reallocate DATA form this POOL when growing. */ + apr_pool_t *pool; +}; + +/* Given that MAX shall be an actual bit index in a packed bit array, + * return the number of blocks entries to allocate for the data buffer. */ +static apr_size_t +select_data_size(apr_size_t max) +{ + /* We allocate a power of two of bytes but at least 16 blocks. */ + apr_size_t size = INITIAL_BLOCK_COUNT; + + /* Caution: + * MAX / BLOCK_SIZE_BITS == SIZE still means that MAX is out of bounds. + * OTOH, 2 * (MAX/BLOCK_SIZE_BITS) is always within the value range of + * APR_SIZE_T. */ + while (size <= max / BLOCK_SIZE_BITS) + size *= 2; + + return size; +} + +svn_bit_array__t * +svn_bit_array__create(apr_size_t max, + apr_pool_t *pool) +{ + svn_bit_array__t *array = apr_pcalloc(pool, sizeof(*array)); + + array->block_count = select_data_size(max); + array->pool = pool; + array->blocks = apr_pcalloc(pool, + array->block_count * sizeof(*array->blocks)); + + return array; +} + +void +svn_bit_array__set(svn_bit_array__t *array, + apr_size_t idx, + svn_boolean_t value) +{ + unsigned char *block; + + /* Index within ARRAY->BLOCKS for the block containing bit IDX. */ + apr_size_t block_idx = idx / BLOCK_SIZE_BITS; + + /* Within that block, index of the byte containing IDX. */ + apr_size_t byte_idx = (idx % BLOCK_SIZE_BITS) / 8; + + /* Within that byte, index of the bit corresponding to IDX. */ + apr_size_t bit_idx = (idx % BLOCK_SIZE_BITS) % 8; + + /* If IDX is outside the allocated range, we _may_ have to grow it. + * + * Be sure to use division instead of multiplication as we need to cover + * the full value range of APR_SIZE_T for the bit indexes. + */ + if (block_idx >= array->block_count) + { + apr_size_t new_count; + unsigned char **new_blocks; + + /* Unallocated indexes are implicitly 0, so no actual allocation + * required in that case. + */ + if (!value) + return; + + /* Grow block list to cover IDX. + * Clear the new entries to guarantee our array[idx]==0 default. + */ + new_count = select_data_size(idx); + new_blocks = apr_pcalloc(array->pool, new_count * sizeof(*new_blocks)); + memcpy(new_blocks, array->blocks, + array->block_count * sizeof(*new_blocks)); + array->blocks = new_blocks; + array->block_count = new_count; + } + + /* IDX is covered by ARRAY->BLOCKS now. */ + + /* Get the block that contains IDX. Auto-allocate it if missing. */ + block = array->blocks[block_idx]; + if (block == NULL) + { + /* Unallocated indexes are implicitly 0, so no actual allocation + * required in that case. + */ + if (!value) + return; + + /* Allocate the previously missing block and clear it for our + * array[idx] == 0 default. */ + block = apr_pcalloc(array->pool, BLOCK_SIZE); + array->blocks[block_idx] = block; + } + + /* Set / reset one bit. Be sure to use unsigned shifts. */ + if (value) + block[byte_idx] |= (unsigned char)(1u << bit_idx); + else + block[byte_idx] &= ~(unsigned char)(1u << bit_idx); +} + +svn_boolean_t +svn_bit_array__get(svn_bit_array__t *array, + apr_size_t idx) +{ + unsigned char *block; + + /* Index within ARRAY->BLOCKS for the block containing bit IDX. */ + apr_size_t block_idx = idx / BLOCK_SIZE_BITS; + + /* Within that block, index of the byte containing IDX. */ + apr_size_t byte_idx = (idx % BLOCK_SIZE_BITS) / 8; + + /* Within that byte, index of the bit corresponding to IDX. */ + apr_size_t bit_idx = (idx % BLOCK_SIZE_BITS) % 8; + + /* Indexes outside the allocated range are implicitly 0. */ + if (block_idx >= array->block_count) + return 0; + + /* Same if the respective block has not been allocated. */ + block = array->blocks[block_idx]; + if (block == NULL) + return 0; + + /* Extract one bit (get the byte, shift bit to LSB, extract it). */ + return (block[byte_idx] >> bit_idx) & 1; +} + diff --git a/contrib/subversion/subversion/libsvn_subr/cache-inprocess.c b/contrib/subversion/subversion/libsvn_subr/cache-inprocess.c index 6401f9f7c..a0e0bbff4 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache-inprocess.c +++ b/contrib/subversion/subversion/libsvn_subr/cache-inprocess.c @@ -190,17 +190,22 @@ inprocess_cache_get_internal(char **buffer, { struct cache_entry *entry = apr_hash_get(cache->hash, key, cache->klen); - *buffer = NULL; if (entry) { SVN_ERR(move_page_to_front(cache, entry->page)); /* duplicate the buffer entry */ *buffer = apr_palloc(result_pool, entry->size); - memcpy(*buffer, entry->value, entry->size); + if (entry->size) + memcpy(*buffer, entry->value, entry->size); *size = entry->size; } + else + { + *buffer = NULL; + *size = 0; + } return SVN_NO_ERROR; } @@ -213,25 +218,64 @@ inprocess_cache_get(void **value_p, apr_pool_t *result_pool) { inprocess_cache_t *cache = cache_void; - char* buffer = NULL; - apr_size_t size; + + if (key) + { + char* buffer; + apr_size_t size; + + SVN_MUTEX__WITH_LOCK(cache->mutex, + inprocess_cache_get_internal(&buffer, + &size, + cache, + key, + result_pool)); + /* deserialize the buffer content. Usually, this will directly + modify the buffer content directly. */ + *found = (buffer != NULL); + if (!buffer || !size) + *value_p = NULL; + else + return cache->deserialize_func(value_p, buffer, size, result_pool); + } + else + { + *value_p = NULL; + *found = FALSE; + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +inprocess_cache_has_key_internal(svn_boolean_t *found, + inprocess_cache_t *cache, + const void *key, + apr_pool_t *scratch_pool) +{ + *found = apr_hash_get(cache->hash, key, cache->klen) != NULL; + + return SVN_NO_ERROR; +} + +static svn_error_t * +inprocess_cache_has_key(svn_boolean_t *found, + void *cache_void, + const void *key, + apr_pool_t *scratch_pool) +{ + inprocess_cache_t *cache = cache_void; if (key) SVN_MUTEX__WITH_LOCK(cache->mutex, - inprocess_cache_get_internal(&buffer, - &size, - cache, - key, - result_pool)); + inprocess_cache_has_key_internal(found, + cache, + key, + scratch_pool)); + else + *found = FALSE; - /* deserialize the buffer content. Usually, this will directly - modify the buffer content directly. - */ - *value_p = NULL; - *found = buffer != NULL; - return buffer && size - ? cache->deserialize_func(value_p, buffer, size, result_pool) - : SVN_NO_ERROR; + return SVN_NO_ERROR; } /* Removes PAGE from the LRU list, removes all of its entries from @@ -592,6 +636,7 @@ inprocess_cache_get_info(void *cache_void, static svn_cache__vtable_t inprocess_cache_vtable = { inprocess_cache_get, + inprocess_cache_has_key, inprocess_cache_set, inprocess_cache_iter, inprocess_cache_is_cachable, @@ -642,6 +687,7 @@ svn_cache__create_inprocess(svn_cache__t **cache_p, wrapper->vtable = &inprocess_cache_vtable; wrapper->cache_internal = cache; + wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT"); *cache_p = wrapper; return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_subr/cache-membuffer.c b/contrib/subversion/subversion/libsvn_subr/cache-membuffer.c index 7aa90cd79..87ac96168 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache-membuffer.c +++ b/contrib/subversion/subversion/libsvn_subr/cache-membuffer.c @@ -27,13 +27,17 @@ #include "svn_pools.h" #include "svn_checksum.h" -#include "md5.h" #include "svn_private_config.h" -#include "cache.h" #include "svn_string.h" +#include "svn_sorts.h" /* get the MIN macro */ + +#include "private/svn_atomic.h" #include "private/svn_dep_compat.h" #include "private/svn_mutex.h" -#include "private/svn_pseudo_md5.h" +#include "private/svn_string_private.h" + +#include "cache.h" +#include "fnv1a.h" /* * This svn_cache__t implementation actually consists of two parts: @@ -44,11 +48,15 @@ * 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. + * representation, prefixed by their full cache keys. There may be + * arbitrary gaps between entries. This buffer is sub-devided into + * (currently two) cache levels. + * * 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. + * that given key. The result is a GROUP_SIZE+-way associative cache + * whose associativity can be dynamically increased. * * 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. @@ -56,23 +64,31 @@ * between different processes and / or to persist them on disk. These * out-of-process features have not been implemented, yet. * + * Superficially, cache levels are being used as usual: insertion happens + * into L1 and evictions will promote items to L2. But their whole point + * is a different one. L1 uses a circular buffer, i.e. we have perfect + * caching for the last N bytes where N is the size of L1. L2 uses a more + * elaborate scheme based on priorities and hit counts as described below. + * * 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. + * associated to it as unused. First and last element of that chain are + * being referenced from the respective cache level. * - * 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. + * Insertion can occur at only one, sliding position per cache level. It is + * marked by its offset in the data buffer and the index of the first used + * entry at or behind that position. If this gap is too small to accommodate + * the new item (plus its full key), 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 + * L2 uses a randomized variant of LFU (see ensure_data_insertable_l2 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 @@ -86,14 +102,20 @@ * 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(). + * comments in ensure_data_insertable_l2(). + * + * Due to the randomized mapping of keys to entry groups, some groups may + * overflow. In that case, there are spare groups that can be chained to + * an already used group to extend it. * * 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. + * but only their hashed "fingerprint" will be stored. These are reasonably + * unique to prevent collisions, so we only need to support up to one entry + * per entry key. To guarantee that there are no conflicts, however, we + * store the actual full key immediately in front of the serialized item + * data. That is, the entry offset actually points to the full key and the + * key length stored in the entry acts as an additional offset to find the + * actual item. * * 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 @@ -108,7 +130,7 @@ * 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. + * we had before 1.8. */ #ifdef WIN32 # define USE_SIMPLE_MUTEX 1 @@ -116,14 +138,11 @@ # 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. + * Since we can't portably align pointers, this is rather the item size + * granularity which ensures *relative* alignment within the cache - still + * giving us decent copy speeds on most machines. + * * Must be a power of 2. */ #define ITEM_ALIGNMENT 16 @@ -170,11 +189,34 @@ */ #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. +/* We use this structure to identify cache entries. There cannot be two + * entries with the same entry key. However unlikely, though, two different + * full keys (see full_key_t) may have the same entry key. That is a + * collision and at most one of them can be stored in the cache at any time. + */ +typedef struct entry_key_t +{ + /* 16 byte finger print of the full key. */ + apr_uint64_t fingerprint[2]; + + /* Length of the full key. This value is aligned to ITEM_ALIGNMENT to + * make sure the subsequent item content is properly aligned. */ + apr_size_t key_len; +} entry_key_t; + +/* A full key, i.e. the combination of the cache's key prefix with some + * dynamic part appended to it. It also contains its ENTRY_KEY. */ -typedef apr_uint64_t entry_key_t[2]; +typedef struct full_key_t +{ + /* Reduced form identifying the cache entry (if such an entry exists). */ + entry_key_t entry_key; + + /* This contains the full combination. Note that the SIZE element may + * be larger than ENTRY_KEY.KEY_LEN, but only the latter determines the + * valid key size. */ + svn_membuf_t full_key; +} full_key_t; /* Debugging / corruption detection support. * If you define this macro, the getter functions will performed expensive @@ -186,9 +228,9 @@ typedef apr_uint64_t entry_key_t[2]; /* 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. + * the last 15 bytes + \0 as plaintext for easy identification by the dev. */ -#define PREFIX_TAIL_LEN 8 +#define PREFIX_TAIL_LEN 16 /* 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 @@ -196,20 +238,20 @@ typedef apr_uint64_t entry_key_t[2]; */ typedef struct entry_tag_t { - /* MD5 checksum over the serialized the item data. + /* MD5 checksum over the serialized item data. */ - unsigned char content_hash [APR_MD5_DIGESTSIZE]; + 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]; + 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]; + 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") @@ -222,36 +264,36 @@ typedef struct entry_tag_t } 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 full_key_t *prefix_key, const void *key, apr_size_t key_len, apr_pool_t *pool) { svn_checksum_t *checksum; + const char *prefix = prefix_key->full_key.data; + apr_size_t prefix_len = strlen(prefix); + + if (prefix_len > sizeof(tag->prefix_tail)) + { + prefix += prefix_len - (sizeof(tag->prefix_tail) - 1); + prefix_len = sizeof(tag->prefix_tail) - 1; + } + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, key, key_len, pool)); - memcpy(tag->prefix_hash, prefix_hash, sizeof(tag->prefix_hash)); + memcpy(tag->prefix_hash, prefix_key->entry_key.fingerprint, + sizeof(tag->prefix_hash)); memcpy(tag->key_hash, checksum->digest, sizeof(tag->key_hash)); - memcpy(tag->prefix_tail, prefix_tail, sizeof(tag->prefix_tail)); + + memset(tag->prefix_tail, 0, sizeof(tag->key_hash)); + memcpy(tag->prefix_tail, prefix, prefix_len + 1); tag->key_len = key_len; @@ -261,7 +303,7 @@ static svn_error_t *store_key_part(entry_tag_t *tag, /* Initialize the content hash member of TAG. */ static svn_error_t* store_content_part(entry_tag_t *tag, - const char *data, + const void *data, apr_size_t size, apr_pool_t *pool) { @@ -303,17 +345,17 @@ static svn_error_t* assert_equal_tags(const entry_tag_t *lhs, #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)); +#define DEBUG_CACHE_MEMBUFFER_INIT_TAG(pool) \ + entry_tag_t _tag; \ + entry_tag_t *tag = &_tag; \ + if (key) \ + SVN_ERR(store_key_part(tag, \ + &cache->prefix, \ + key, \ + cache->key_len == APR_HASH_KEY_STRING \ + ? strlen((const char *) key) \ + : cache->key_len, \ + pool)); #else @@ -321,14 +363,14 @@ static svn_error_t* assert_equal_tags(const entry_tag_t *lhs, */ #define DEBUG_CACHE_MEMBUFFER_TAG_ARG #define DEBUG_CACHE_MEMBUFFER_TAG -#define DEBUG_CACHE_MEMBUFFER_INIT_TAG +#define DEBUG_CACHE_MEMBUFFER_INIT_TAG(pool) #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. + * list of used entries per cache level. */ typedef struct entry_t { @@ -336,11 +378,13 @@ typedef struct entry_t */ entry_key_t key; - /* The offset of the cached item's serialized data within the data buffer. + /* The offset of the cached item's serialized data within the caches + * DATA buffer. */ apr_uint64_t offset; - /* Size of the serialized item data. May be 0. + /* Size of the serialized item data. May be 0. The MAX_ITEM_SIZE macro + * above ensures that there will be no overflows. * Only valid for used entries. */ apr_size_t size; @@ -348,23 +392,27 @@ typedef struct entry_t /* Number of (read) hits for this entry. Will be reset upon write. * Only valid for used entries. */ - apr_uint32_t hit_count; + svn_atomic_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. + * by the caches cache_level_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. + * by the caches cache_level_t.first member. * Only valid for used entries. */ apr_uint32_t previous; + /* Priority of this entry. This entry will not be replaced by lower- + * priority items. + */ + apr_uint32_t priority; #ifdef SVN_DEBUG_CACHE_MEMBUFFER /* Remember type, content and key hashes. */ @@ -372,39 +420,68 @@ typedef struct entry_t #endif } entry_t; -/* We group dictionary entries to make this GROUP-SIZE-way associative. +/* Group header struct. */ -typedef struct entry_group_t +typedef struct group_header_t { /* number of entries used [0 .. USED-1] */ apr_uint32_t used; - /* the actual entries */ - entry_t entries[GROUP_SIZE]; -} entry_group_t; + /* next group in the chain or NO_INDEX for the last. + * For recycleable unused spare groups, this points to the next + * unused spare group */ + apr_uint32_t next; -/* The cache header structure. + /* previously group in the chain or NO_INDEX for the first */ + apr_uint32_t previous; + + /* number of elements in the chain from start to here. + * >= 1 for used groups, 0 for unused spare groups */ + apr_uint32_t chain_length; + +} group_header_t; + +/* The size of the group struct should be a power of two make sure it does + * not cross memory page boundaries. Since we already access the cache + * randomly, having two page table lookups instead of one is bad. */ -struct svn_membuffer_t +#define GROUP_BLOCK_SIZE 512 + +/* A ~10-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 \ + ((GROUP_BLOCK_SIZE - sizeof(group_header_t)) / sizeof(entry_t)) + +/* Maximum number of groups in a chain, i.e. a cache index group can hold + * up to GROUP_SIZE * MAX_GROUP_CHAIN_LENGTH entries. + */ +#define MAX_GROUP_CHAIN_LENGTH 8 + +/* We group dictionary entries to make this GROUP-SIZE-way associative. + */ +typedef struct entry_group_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; + /* group globals */ + group_header_t header; - /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL. - */ - entry_group_t *directory; + /* padding and also room for future extensions */ + char padding[GROUP_BLOCK_SIZE - sizeof(group_header_t) + - sizeof(entry_t) * GROUP_SIZE]; - /* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements. - * Allows for efficiently marking groups as "not initialized". - */ - unsigned char *group_initialized; + /* the actual entries */ + entry_t entries[GROUP_SIZE]; - /* Size of dictionary in groups. Must be > 0. - */ - apr_uint32_t group_count; +} entry_group_t; +/* Per-cache level header structure. Instances of this are members of + * svn_membuffer_t and will use non-overlapping sections of its DATA buffer. + * All offset values are global / absolute to that whole buffer. + */ +typedef struct cache_level_t +{ /* 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. @@ -425,18 +502,61 @@ struct svn_membuffer_t apr_uint32_t next; - /* Pointer to the data buffer, data_size bytes long. Never NULL. + /* First offset in the caches DATA buffer that belongs to this level. */ - unsigned char *data; + apr_uint64_t start_offset; - /* Size of data buffer in bytes. Must be > 0. + /* Size of data buffer allocated to this level in bytes. Must be > 0. */ - apr_uint64_t data_size; + apr_uint64_t size; /* Offset in the data buffer where the next insertion shall occur. */ apr_uint64_t current_data; +} cache_level_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 + spare_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; + + /* Total number of spare groups. + */ + apr_uint32_t spare_group_count; + + /* First recycleable spare group. + */ + apr_uint32_t first_spare_group; + + /* Maximum number of spare groups ever used. I.e. group index + * group_count + max_spare_used is the first unused spare group + * if first_spare_group is NO_INDEX. + */ + apr_uint32_t max_spare_used; + + /* Pointer to the data buffer, data_size bytes long. Never NULL. + */ + unsigned char *data; + /* Total number of data buffer bytes in use. */ apr_uint64_t data_used; @@ -446,45 +566,63 @@ struct svn_membuffer_t */ apr_uint64_t max_entry_size; + /* The cache levels, organized as sub-buffers. Since entries in the + * DIRECTORY use offsets in DATA for addressing, a cache lookup does + * not need to know the cache level of a specific item. Cache levels + * are only used to implement a hybrid insertion / eviction strategy. + */ - /* 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. + /* First cache level, i.e. most insertions happen here. Very large + * items might get inserted directly into L2. L1 is a strict FIFO + * ring buffer that does not care about item priorities. All evicted + * items get a chance to be promoted to L2. */ - apr_uint32_t used_entries; + cache_level_t l1; - /* 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. + /* Second cache level, i.e. data evicted from L1 will be added here + * if the item is "important" enough or the L2 insertion window is large + * enough. */ - apr_uint64_t hit_count; + cache_level_t l2; + + /* Number of used dictionary entries, i.e. number of cached items. + * Purely statistical information that may be used for profiling only. + * Updates are not synchronized and values may be nonsensicle on some + * platforms. + */ + apr_uint32_t used_entries; /* Total number of calls to membuffer_cache_get. - * Purely statistical information that may be used for profiling. + * Purely statistical information that may be used for profiling only. + * Updates are not synchronized and values may be nonsensicle on some + * platforms. */ apr_uint64_t total_reads; /* Total number of calls to membuffer_cache_set. - * Purely statistical information that may be used for profiling. + * Purely statistical information that may be used for profiling only. + * Updates are not synchronized and values may be nonsensicle on some + * platforms. */ apr_uint64_t total_writes; /* Total number of hits since the cache's creation. - * Purely statistical information that may be used for profiling. + * Purely statistical information that may be used for profiling only. + * Updates are not synchronized and values may be nonsensicle on some + * platforms. */ apr_uint64_t total_hits; -#if APR_HAS_THREADS +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) /* 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 +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) + /* Same for read-write lock. */ 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 @@ -498,42 +636,37 @@ struct svn_membuffer_t */ #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 +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) return svn_mutex__lock(cache->lock); -# else +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) 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; +#else + return SVN_NO_ERROR; +#endif } /* If locking is supported for CACHE, acquire a write lock for it. + * Set *SUCCESS to FALSE, if we couldn't acquire the write lock; + * leave it untouched otherwise. */ static svn_error_t * write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success) { -#if APR_HAS_THREADS -# if USE_SIMPLE_MUTEX - +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) return svn_mutex__lock(cache->lock); - -# else - +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) if (cache->lock) { apr_status_t status; @@ -556,9 +689,10 @@ write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success) _("Can't write-lock cache mutex")); } -# endif -#endif return SVN_NO_ERROR; +#else + return SVN_NO_ERROR; +#endif } /* If locking is supported for CACHE, acquire an unconditional write lock @@ -567,36 +701,29 @@ write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success) static svn_error_t * force_write_lock_cache(svn_membuffer_t *cache) { -#if APR_HAS_THREADS -# if USE_SIMPLE_MUTEX - +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) return svn_mutex__lock(cache->lock); - -# else - +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) 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; +#else + return SVN_NO_ERROR; +#endif } /* If locking is supported for CACHE, release the current lock - * (read or write). + * (read or write). Return ERR upon success. */ static svn_error_t * unlock_cache(svn_membuffer_t *cache, svn_error_t *err) { -#if APR_HAS_THREADS -# if USE_SIMPLE_MUTEX - +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) return svn_mutex__unlock(cache->lock, err); - -# else - +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) if (cache->lock) { apr_status_t status = apr_thread_rwlock_unlock(cache->lock); @@ -607,13 +734,14 @@ unlock_cache(svn_membuffer_t *cache, svn_error_t *err) return svn_error_wrap_apr(status, _("Can't unlock cache mutex")); } -# endif -#endif return err; +#else + return err; +#endif } -/* If supported, guard the execution of EXPR with a read lock to cache. - * Macro has been modeled after SVN_MUTEX__WITH_LOCK. +/* If supported, guard the execution of EXPR with a read lock to CACHE. + * The macro has been modeled after SVN_MUTEX__WITH_LOCK. */ #define WITH_READ_LOCK(cache, expr) \ do { \ @@ -621,8 +749,8 @@ do { \ 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. +/* If supported, guard the execution of EXPR with a write lock to CACHE. + * The 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 @@ -647,6 +775,132 @@ do { \ SVN_ERR(unlock_cache(cache, (expr))); \ } while (0) +/* 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 + cache->spare_group_count) + last_index = cache->group_count + cache->spare_group_count; + + for (i = first_index; i < last_index; ++i) + { + group_header_t *header = &cache->directory[i].header; + header->used = 0; + header->chain_length = 1; + header->next = NO_INDEX; + header->previous = NO_INDEX; + } + + /* 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; +} + +/* Return the next available spare group from CACHE and mark it as used. + * May return NULL. + */ +static entry_group_t * +allocate_spare_group(svn_membuffer_t *cache) +{ + entry_group_t *group = NULL; + + /* is there some ready-to-use group? */ + if (cache->first_spare_group != NO_INDEX) + { + group = &cache->directory[cache->first_spare_group]; + cache->first_spare_group = group->header.next; + } + + /* any so far untouched spares available? */ + else if (cache->max_spare_used < cache->spare_group_count) + { + apr_uint32_t group_index = cache->group_count + cache->max_spare_used; + ++cache->max_spare_used; + + if (!is_group_initialized(cache, group_index)) + initialize_group(cache, group_index); + + group = &cache->directory[group_index]; + } + + /* spare groups must be empty */ + assert(!group || !group->header.used); + return group; +} + +/* Mark previously allocated spare group GROUP in CACHE as "unused". + */ +static void +free_spare_group(svn_membuffer_t *cache, + entry_group_t *group) +{ + assert(group->header.used == 0); + assert(group->header.previous != NO_INDEX); + assert(group - cache->directory >= (apr_ssize_t)cache->group_count); + + /* unchain */ + cache->directory[group->header.previous].header.next = NO_INDEX; + group->header.chain_length = 0; + group->header.previous = NO_INDEX; + + /* add to chain of spares */ + group->header.next = cache->first_spare_group; + cache->first_spare_group = (apr_uint32_t) (group - cache->directory); +} + +/* Follow the group chain from GROUP in CACHE to its end and return the last + * group. May return GROUP. + */ +static entry_group_t * +last_group_in_chain(svn_membuffer_t *cache, + entry_group_t *group) +{ + while (group->header.next != NO_INDEX) + group = &cache->directory[group->header.next]; + + return group; +} + +/* Return the CHAIN_INDEX-th element in the group chain starting from group + * START_GROUP_INDEX in CACHE. + */ +static entry_group_t * +get_group(svn_membuffer_t *cache, + apr_uint32_t start_group_index, + apr_uint32_t chain_index) +{ + entry_group_t *group = &cache->directory[start_group_index]; + for (; chain_index; --chain_index) + group = &cache->directory[group->header.next]; + + return group; +} + /* Resolve a dictionary entry reference, i.e. return the entry * for the given IDX. */ @@ -668,6 +922,96 @@ get_index(svn_membuffer_t *cache, entry_t *entry) + (apr_uint32_t)(entry - cache->directory[group_index].entries); } +/* Return the cache level of ENTRY in CACHE. + */ +static cache_level_t * +get_cache_level(svn_membuffer_t *cache, entry_t *entry) +{ + return entry->offset < cache->l1.size ? &cache->l1 + : &cache->l2; +} + +/* Insert ENTRY to the chain of items that belong to LEVEL in CACHE. IDX + * is ENTRY's item index and is only given for efficiency. The insertion + * takes place just before LEVEL->NEXT. *CACHE will not be modified. + */ +static void +chain_entry(svn_membuffer_t *cache, + cache_level_t *level, + entry_t *entry, + apr_uint32_t idx) +{ + /* insert ENTRY before this item */ + entry_t *next = level->next == NO_INDEX + ? NULL + : get_entry(cache, level->next); + assert(idx == get_index(cache, entry)); + + /* update entry chain + */ + entry->next = level->next; + if (level->first == NO_INDEX) + { + /* insert as the first entry and only in the chain + */ + entry->previous = NO_INDEX; + level->last = idx; + level->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 = level->last; + get_entry(cache, level->last)->next = idx; + level->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 + level->first = idx; + } +} + +/* Remove ENTRY from the chain of items that belong to LEVEL in CACHE. IDX + * is ENTRY's item index and is only given for efficiency. Please note + * that neither *CACHE nor *ENTRY will not be modified. + */ +static void +unchain_entry(svn_membuffer_t *cache, + cache_level_t *level, + entry_t *entry, + apr_uint32_t idx) +{ + assert(idx == get_index(cache, entry)); + + /* update + */ + if (level->next == idx) + level->next = entry->next; + + /* unlink it from the chain of used entries + */ + if (entry->previous == NO_INDEX) + level->first = entry->next; + else + get_entry(cache, entry->previous)->next = entry->next; + + if (entry->next == NO_INDEX) + level->last = entry->previous; + else + get_entry(cache, entry->next)->previous = entry->previous; +} + /* Remove the used ENTRY from the CACHE, i.e. make it "unused". * In contrast to insertion, removal is possible for any entry. */ @@ -678,83 +1022,84 @@ drop_entry(svn_membuffer_t *cache, entry_t *entry) */ 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; + entry_group_t *last_group + = last_group_in_chain(cache, &cache->directory[group_index]); + apr_uint32_t last_in_group + = (apr_uint32_t) ((last_group - cache->directory) * GROUP_SIZE + + last_group->header.used - 1); - /* Only valid to be called for used entries. - */ - assert(idx <= last_in_group); + cache_level_t *level = get_cache_level(cache, entry); /* 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; + if (idx == level->next) + level->next = entry->next; else - if (entry->next == cache->next) + if (entry->next == level->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; + level->current_data = level->start_offset; } else { /* insertion may start right behind the previous entry */ entry_t *previous = get_entry(cache, entry->previous); - cache->current_data = ALIGN_VALUE( previous->offset + level->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; + unchain_entry(cache, level, entry, idx); /* 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) + if (idx != last_in_group) { /* copy the last used entry to the removed entry's index */ - *entry = group->entries[group->used-1]; + *entry = last_group->entries[last_group->header.used-1]; + + /* this ENTRY may belong to a different cache level than the entry + * we have just removed */ + level = get_cache_level(cache, entry); /* update foreign links to new index */ - if (last_in_group == cache->next) - cache->next = idx; + if (last_in_group == level->next) + level->next = idx; if (entry->previous == NO_INDEX) - cache->first = idx; + level->first = idx; else get_entry(cache, entry->previous)->next = idx; if (entry->next == NO_INDEX) - cache->last = idx; + level->last = idx; else get_entry(cache, entry->next)->previous = idx; } /* Update the number of used entries. */ - group->used--; + last_group->header.used--; + + /* Release the last group in the chain if it is a spare group + */ + if (!last_group->header.used && last_group->header.previous != NO_INDEX) + free_spare_group(cache, last_group); } /* Insert ENTRY into the chain of used dictionary entries. The entry's @@ -769,62 +1114,30 @@ insert_entry(svn_membuffer_t *cache, entry_t *entry) 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); + cache_level_t *level = get_cache_level(cache, entry); /* 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); + assert(entry->offset == level->current_data); + assert(idx == group_index * GROUP_SIZE + group->header.used); + level->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++; + group->header.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; - } + chain_entry(cache, level, entry, idx); /* The current insertion position must never point outside our * data buffer. */ - assert(cache->current_data <= cache->data_size); + assert(level->current_data <= level->start_offset + level->size); } /* Map a KEY of 16 bytes to the CACHE and group that shall contain the @@ -832,13 +1145,19 @@ insert_entry(svn_membuffer_t *cache, entry_t *entry) */ static apr_uint32_t get_group_index(svn_membuffer_t **cache, - entry_key_t key) + const 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; + apr_uint64_t key0 = key->fingerprint[0]; + apr_uint64_t key1 = key->fingerprint[1]; + + /* select the cache segment to use. they have all the same group_count. + * Since key may not be well-distributed, pre-fold it to a smaller but + * "denser" ranger. The modulus is a prime larger than the largest + * counts. */ + *cache = &segment0[(key1 % APR_UINT64_C(2809637) + (key0 / 37)) + & (segment0->segment_count - 1)]; + return (key0 % APR_UINT64_C(5030895599)) % segment0->group_count; } /* Reduce the hit count of ENTRY and update the accumulated hit info @@ -849,48 +1168,25 @@ 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; + if (hits_removed) + { + entry->hit_count -= hits_removed; + } + else + { + entry->priority /= 2; + } } -/* 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. +/* Return whether the keys in LHS and RHS match. */ -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) +static svn_boolean_t +entry_keys_match(const entry_key_t *lhs, + const entry_key_t *rhs) { - 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; + return (lhs->fingerprint[0] == rhs->fingerprint[0]) + && (lhs->fingerprint[1] == rhs->fingerprint[1]) + && (lhs->key_len == rhs->key_len); } /* Given the GROUP_INDEX that shall contain an entry with the hash key @@ -904,11 +1200,15 @@ initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index) * 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. + * + * Note: This function requires the caller to appropriately lock the CACHE. + * For FIND_EMPTY==FALSE, a read lock is required, for FIND_EMPTY==TRUE, + * the write lock must have been acquired. */ static entry_t * find_entry(svn_membuffer_t *cache, apr_uint32_t group_index, - const apr_uint64_t to_find[2], + const full_key_t *to_find, svn_boolean_t find_empty) { entry_group_t *group; @@ -929,8 +1229,7 @@ find_entry(svn_membuffer_t *cache, entry = &group->entries[0]; /* initialize entry for the new key */ - entry->key[0] = to_find[0]; - entry->key[1] = to_find[1]; + entry->key = to_find->entry_key; } return entry; @@ -938,43 +1237,116 @@ find_entry(svn_membuffer_t *cache, /* 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; - } + while (1) + { + for (i = 0; i < group->header.used; ++i) + if (entry_keys_match(&group->entries[i].key, &to_find->entry_key)) + { + /* This is the only entry that _may_ contain the correct data. */ + entry = &group->entries[i]; + + /* If we want to preserve it, check that it is actual a match. */ + if (!find_empty) + { + /* If there is no full key to compare, we are done. */ + if (!entry->key.key_len) + return entry; + + /* Compare the full key. */ + if (memcmp(to_find->full_key.data, + cache->data + entry->offset, + entry->key.key_len) == 0) + return entry; + + /* Key conflict. The entry to find cannot be anywhere else. + * Therefore, it is not cached. */ + return NULL; + } + + /* need to empty that entry */ + drop_entry(cache, entry); + if (group->header.used == GROUP_SIZE) + group = last_group_in_chain(cache, group); + else if (group->header.chain_length == 0) + group = last_group_in_chain(cache, + &cache->directory[group_index]); + + /* No entry found (actually, none left to find). */ + entry = NULL; + break; + } + + /* end of chain? */ + if (group->header.next == NO_INDEX) + break; + + /* only full groups may chain */ + assert(group->header.used == GROUP_SIZE); + group = &cache->directory[group->header.next]; + } /* None found. Are we looking for a free entry? */ if (find_empty) { - /* if there is no empty entry, delete the oldest entry + /* There is no empty entry in the chain, try chaining a spare group. */ - if (group->used == GROUP_SIZE) + if ( group->header.used == GROUP_SIZE + && group->header.chain_length < MAX_GROUP_CHAIN_LENGTH) + { + entry_group_t *new_group = allocate_spare_group(cache); + if (new_group) + { + /* chain groups + */ + new_group->header.chain_length = group->header.chain_length + 1; + new_group->header.previous = (apr_uint32_t) (group - + cache->directory); + new_group->header.next = NO_INDEX; + group->header.next = (apr_uint32_t) (new_group - + cache->directory); + group = new_group; + } + } + + /* if GROUP is still filled, we need to remove a random entry */ + if (group->header.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. + * + * We hit only one random group instead of processing all + * groups in the chain. */ - 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]; + cache_level_t *entry_level; + int to_remove = rand() % (GROUP_SIZE * group->header.chain_length); + entry_group_t *to_shrink + = get_group(cache, group_index, to_remove / GROUP_SIZE); + + entry = &to_shrink->entries[to_remove % GROUP_SIZE]; + entry_level = get_cache_level(cache, entry); + for (i = 0; i < GROUP_SIZE; ++i) + { + /* keep L1 entries whenever possible */ + + cache_level_t *level + = get_cache_level(cache, &to_shrink->entries[i]); + if ( (level != entry_level && entry_level == &cache->l1) + || (entry->hit_count > to_shrink->entries[i].hit_count)) + { + entry_level = level; + entry = &to_shrink->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]) + if (entry != &to_shrink->entries[i]) let_entry_age(cache, entry); drop_entry(cache, entry); @@ -982,9 +1354,8 @@ find_entry(svn_membuffer_t *cache, /* initialize entry for the new key */ - entry = &group->entries[group->used]; - entry->key[0] = to_find[0]; - entry->key[1] = to_find[1]; + entry = &group->entries[group->header.used]; + entry->key = to_find->entry_key; } return entry; @@ -997,6 +1368,7 @@ static void move_entry(svn_membuffer_t *cache, entry_t *entry) { apr_size_t size = ALIGN_VALUE(entry->size); + cache_level_t *level = get_cache_level(cache, entry); /* This entry survived this cleansing run. Reset half of its * hit count so that its removal gets more likely in the next @@ -1010,141 +1382,259 @@ move_entry(svn_membuffer_t *cache, entry_t *entry) * 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) + if (entry->offset != level->current_data) { - memmove(cache->data + cache->current_data, + memmove(cache->data + level->current_data, cache->data + entry->offset, size); - entry->offset = cache->current_data; + entry->offset = level->current_data; } - /* The insertion position is now directly behind this entry. - */ - cache->current_data = entry->offset + size; - cache->next = entry->next; + /* The insertion position is now directly behind this entry. + */ + level->current_data = entry->offset + size; + level->next = entry->next; + + /* The current insertion position must never point outside our + * data buffer. + */ + assert(level->current_data <= level->start_offset + level->size); +} + +/* Move ENTRY in CACHE from L1 to L2. + */ +static void +promote_entry(svn_membuffer_t *cache, entry_t *entry) +{ + apr_uint32_t idx = get_index(cache, entry); + apr_size_t size = ALIGN_VALUE(entry->size); + assert(get_cache_level(cache, entry) == &cache->l1); + assert(idx == cache->l1.next); + + /* copy item from the current location in L1 to the start of L2's + * insertion window */ + memmove(cache->data + cache->l2.current_data, + cache->data + entry->offset, + size); + entry->offset = cache->l2.current_data; + + /* The insertion position is now directly behind this entry. + */ + cache->l2.current_data += size; + + /* remove ENTRY from chain of L1 entries and put it into L2 + */ + unchain_entry(cache, &cache->l1, entry, idx); + chain_entry(cache, &cache->l2, entry, idx); +} + +/* This function implements the cache insertion / eviction strategy for L2. + * + * If necessary, enlarge the insertion window of CACHE->L2 until it is at + * least TO_FIT_IN->SIZE bytes long. TO_FIT_IN->SIZE must not exceed the + * data buffer size allocated to CACHE->L2. IDX is the item index of + * TO_FIT_IN and is given for performance reasons. + * + * 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_l2(svn_membuffer_t *cache, + entry_t *to_fit_in) +{ + entry_t *entry; + + /* accumulated size of the entries that have been removed to make + * room for the new one. + */ + apr_size_t moved_size = 0; + + /* count the number of entries that got moved. A single large entry + * being moved is not enough to reject an insertion. + */ + apr_size_t moved_count = 0; + + /* accumulated "worth" of items dropped so far */ + apr_uint64_t drop_hits = 0; + + /* estimated "worth" of the new entry */ + apr_uint64_t drop_hits_limit = (to_fit_in->hit_count + 1) + * (apr_uint64_t)to_fit_in->priority; + + /* This loop will eventually terminate because every cache entry + * would get dropped eventually: + * + * - the incoming entry is small enough to fit into L2 + * - every iteration either frees parts of L2 or counts the moved size + * - eventually, we either moved too many items with too much total size + * to accept the new entry, or made enough room in L2 for the new entry + * + * Low-prio items get rejected even sooner. + */ + while (1) + { + /* first offset behind the insertion window + */ + apr_uint64_t end = cache->l2.next == NO_INDEX + ? cache->l2.start_offset + cache->l2.size + : get_entry(cache, cache->l2.next)->offset; + + /* leave function as soon as the insertion window is large enough + */ + if (end >= to_fit_in->size + cache->l2.current_data) + return TRUE; + + /* Don't be too eager to cache data. If a lot of data has been moved + * around, the current item has probably a relatively low priority. + * We must also limit the effort spent here (if even in case of faulty + * heuristics). Therefore, give up after some time. + */ + if (moved_size > 4 * to_fit_in->size && moved_count > 7) + return FALSE; + + /* if the net worth (in weighted hits) of items removed is already + * larger than what we want to insert, reject TO_FIT_IN because it + * still does not fit in. */ + if (drop_hits > drop_hits_limit) + return FALSE; + + /* try to enlarge the insertion window + */ + if (cache->l2.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->l2.current_data = cache->l2.start_offset; + cache->l2.next = cache->l2.first; + } + else + { + svn_boolean_t keep; + entry = get_entry(cache, cache->l2.next); + + if (to_fit_in->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY) + { + /* Low prio items can only be accepted only if the current + * entry is of even lower prio and has fewer hits. + */ + if ( entry->priority > to_fit_in->priority + || entry->hit_count > to_fit_in->hit_count) + return FALSE; + } + + if (entry->priority <= SVN_CACHE__MEMBUFFER_LOW_PRIORITY) + { + /* Be quick to remove low-prio entries - even if the incoming + * one is low-prio as well. This makes room for more important + * data and replaces existing data with newly read information. + */ + keep = FALSE; + } + else + { + /* If the existing data is the same prio as the incoming data, + * drop the existing entry if it had seen fewer (probably 0) + * hits than the entry coming in from L1. In case of different + * priorities, keep the current entry of it has higher prio. + * The new entry may still find room by ousting other entries. + */ + keep = to_fit_in->priority == entry->priority + ? entry->hit_count >= to_fit_in->hit_count + : entry->priority > to_fit_in->priority; + } + + /* keepers or destroyers? */ + if (keep) + { + /* Moving entries around is not for free -> track costs. */ + moved_size += entry->size; + moved_count++; - /* The current insertion position must never point outside our - * data buffer. - */ - assert(cache->current_data <= cache->data_size); + move_entry(cache, entry); + } + else + { + /* Drop the entry from the end of the insertion window. + * Count the "hit importance" such that we are not sacrificing + * too much of the high-hit contents. However, don't count + * low-priority hits because higher prio entries will often + * provide the same data but in a further stage of processing. + */ + if (entry->priority > SVN_CACHE__MEMBUFFER_LOW_PRIORITY) + drop_hits += entry->hit_count * (apr_uint64_t)entry->priority; + + drop_entry(cache, entry); + } + } + } + + /* This will never be reached. But if it was, "can't insert" was the + * right answer. */ } -/* 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. +/* This function implements the cache insertion / eviction strategy for L1. + * + * If necessary, enlarge the insertion window of CACHE->L1 by promoting + * entries to L2 until it is at least SIZE bytes long. + * + * Return TRUE if enough room could be found or made. A FALSE result + * indicates that the respective item shall not be added because it is + * too large. */ static svn_boolean_t -ensure_data_insertable(svn_membuffer_t *cache, apr_size_t size) +ensure_data_insertable_l1(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; + /* Guarantees that the while loop will terminate. */ + if (size > cache->l1.size) + return FALSE; /* 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. + * would get dropped eventually. */ 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; + apr_uint32_t entry_index = cache->l1.next; + entry_t *entry = get_entry(cache, entry_index); + apr_uint64_t end = cache->l1.next == NO_INDEX + ? cache->l1.start_offset + cache->l1.size + : entry->offset; /* leave function as soon as the insertion window is large enough */ - if (end >= size + cache->current_data) + if (end >= size + cache->l1.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 + /* Enlarge the insertion window */ - if (cache->next == NO_INDEX) + if (cache->l1.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; + cache->l1.current_data = cache->l1.start_offset; + cache->l1.next = cache->l1.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. + /* Remove the entry from the end of insertion window and promote + * it to L2, if it is important enough. */ - 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); - } + svn_boolean_t keep = ensure_data_insertable_l2(cache, entry); - /* keepers or destroyers? */ + /* We might have touched the group that contains ENTRY. Recheck. */ + if (entry_index == cache->l1.next) + { if (keep) - { - move_entry(cache, entry); - } + promote_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); - } + drop_entry(cache, entry); } } } @@ -1153,28 +1643,6 @@ ensure_data_insertable(svn_membuffer_t *cache, apr_size_t size) * 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, @@ -1188,6 +1656,8 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, apr_uint32_t seg; apr_uint32_t group_count; + apr_uint32_t main_group_count; + apr_uint32_t spare_group_count; apr_uint32_t group_init_size; apr_uint64_t data_size; apr_uint64_t max_entry_size; @@ -1262,8 +1732,8 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, */ 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); + if (directory_size < 2 * sizeof(entry_group_t)) + directory_size = 2 * 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. @@ -1272,13 +1742,13 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, */ 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. + /* For cache sizes > 16TB, individual cache segments will be larger + * than 32GB allowing for >4GB entries. But caching chunks larger + * than 4GB are simply not supported. */ - max_entry_size = data_size / 4 > MAX_ITEM_SIZE + max_entry_size = data_size / 8 > MAX_ITEM_SIZE ? MAX_ITEM_SIZE - : data_size / 4; + : data_size / 8; /* to keep the entries small, we use 32 bit indexes only * -> we need to ensure that no more then 4G entries exist. @@ -1291,6 +1761,11 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, ? (APR_UINT32_MAX / GROUP_SIZE) - 1 : (apr_uint32_t)(directory_size / sizeof(entry_group_t)); + /* set some of the index directory aside as over-flow (spare) buffers */ + spare_group_count = MAX(group_count / 4, 1); + main_group_count = group_count - spare_group_count; + assert(spare_group_count > 0 && main_group_count > 0); + group_init_size = 1 + group_count / (8 * GROUP_INIT_GRANULARITY); for (seg = 0; seg < segment_count; ++seg) { @@ -1298,7 +1773,11 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, */ c[seg].segment_count = (apr_uint32_t)segment_count; - c[seg].group_count = group_count; + c[seg].group_count = main_group_count; + c[seg].spare_group_count = spare_group_count; + c[seg].first_spare_group = NO_INDEX; + c[seg].max_spare_used = 0; + c[seg].directory = apr_pcalloc(pool, group_count * sizeof(entry_group_t)); @@ -1306,18 +1785,30 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, 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; + /* Allocate 1/4th of the data buffer to L1 + */ + c[seg].l1.first = NO_INDEX; + c[seg].l1.last = NO_INDEX; + c[seg].l1.next = NO_INDEX; + c[seg].l1.start_offset = 0; + c[seg].l1.size = ALIGN_VALUE(data_size / 4); + c[seg].l1.current_data = 0; + + /* The remaining 3/4th will be used as L2 + */ + c[seg].l2.first = NO_INDEX; + c[seg].l2.last = NO_INDEX; + c[seg].l2.next = NO_INDEX; + c[seg].l2.start_offset = c[seg].l1.size; + c[seg].l2.size = ALIGN_VALUE(data_size) - c[seg].l1.size; + c[seg].l2.current_data = c[seg].l2.start_offset; + + /* This cast is safe because DATA_SIZE <= MAX_SEGMENT_SIZE. */ + c[seg].data = apr_palloc(pool, (apr_size_t)ALIGN_VALUE(data_size)); 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; @@ -1332,17 +1823,14 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, return svn_error_wrap_apr(APR_ENOMEM, "OOM"); } -#if APR_HAS_THREADS +#if (APR_HAS_THREADS && USE_SIMPLE_MUTEX) /* 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 - +#elif (APR_HAS_THREADS && !USE_SIMPLE_MUTEX) + /* Same for read-write lock. */ c[seg].lock = NULL; if (thread_safe) { @@ -1352,8 +1840,6 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, 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; @@ -1366,6 +1852,61 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, return SVN_NO_ERROR; } +svn_error_t * +svn_cache__membuffer_clear(svn_membuffer_t *cache) +{ + apr_size_t seg; + apr_size_t segment_count = cache->segment_count; + + /* Length of the group_initialized array in bytes. + See also svn_cache__membuffer_cache_create(). */ + apr_size_t group_init_size + = 1 + (cache->group_count + cache->spare_group_count) + / (8 * GROUP_INIT_GRANULARITY); + + /* Clear segment by segment. This implies that other thread may read + and write to other segments after we cleared them and before the + last segment is done. + + However, that is no different from a write request coming through + right after we cleared all segments because dependencies between + cache entries (recursive lookup / access locks) are not allowed. + */ + for (seg = 0; seg < segment_count; ++seg) + { + /* Unconditionally acquire the write lock. */ + SVN_ERR(force_write_lock_cache(&cache[seg])); + + /* Mark all groups as "not initialized", which implies "empty". */ + cache[seg].first_spare_group = NO_INDEX; + cache[seg].max_spare_used = 0; + + memset(cache[seg].group_initialized, 0, group_init_size); + + /* Unlink L1 contents. */ + cache[seg].l1.first = NO_INDEX; + cache[seg].l1.last = NO_INDEX; + cache[seg].l1.next = NO_INDEX; + cache[seg].l1.current_data = cache[seg].l1.start_offset; + + /* Unlink L2 contents. */ + cache[seg].l2.first = NO_INDEX; + cache[seg].l2.last = NO_INDEX; + cache[seg].l2.next = NO_INDEX; + cache[seg].l2.current_data = cache[seg].l2.start_offset; + + /* Reset content counters. */ + cache[seg].data_used = 0; + cache[seg].used_entries = 0; + + /* Segment may be used again. */ + SVN_ERR(unlock_cache(&cache[seg], SVN_NO_ERROR)); + } + + /* done here */ + 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. * @@ -1375,7 +1916,7 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache, static svn_error_t * entry_exists_internal(svn_membuffer_t *cache, apr_uint32_t group_index, - entry_key_t to_find, + const full_key_t *to_find, svn_boolean_t *found) { *found = find_entry(cache, group_index, to_find, FALSE) != NULL; @@ -1388,7 +1929,7 @@ entry_exists_internal(svn_membuffer_t *cache, static svn_error_t * entry_exists(svn_membuffer_t *cache, apr_uint32_t group_index, - entry_key_t to_find, + const full_key_t *to_find, svn_boolean_t *found) { WITH_READ_LOCK(cache, @@ -1400,10 +1941,43 @@ entry_exists(svn_membuffer_t *cache, return SVN_NO_ERROR; } +/* Given the SIZE and PRIORITY of a new item, return the cache level + (L1 or L2) in fragment CACHE that this item shall be inserted into. + If we can't find nor make enough room for the item, return NULL. + */ +static cache_level_t * +select_level(svn_membuffer_t *cache, + apr_size_t size, + apr_uint32_t priority) +{ + if (cache->max_entry_size >= size) + { + /* Small items go into L1. */ + return ensure_data_insertable_l1(cache, size) + ? &cache->l1 + : NULL; + } + else if ( cache->l2.size >= size + && MAX_ITEM_SIZE >= size + && priority > SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY) + { + /* Large but important items go into L2. */ + entry_t dummy_entry = { { { 0 } } }; + dummy_entry.priority = priority; + dummy_entry.size = size; + + return ensure_data_insertable_l2(cache, &dummy_entry) + ? &cache->l2 + : NULL; + } + + /* Don't cache large, unimportant items. */ + return NULL; +} -/* 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. +/* Try to insert the serialized item given in BUFFER with ITEM_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, @@ -1411,17 +1985,21 @@ entry_exists(svn_membuffer_t *cache, * be inserted. * * Note: This function requires the caller to serialization access. - * Don't call it directly, call membuffer_cache_get_partial instead. + * Don't call it directly, call membuffer_cache_set instead. */ static svn_error_t * membuffer_cache_set_internal(svn_membuffer_t *cache, - entry_key_t to_find, + const full_key_t *to_find, apr_uint32_t group_index, char *buffer, - apr_size_t size, + apr_size_t item_size, + apr_uint32_t priority, DEBUG_CACHE_MEMBUFFER_TAG_ARG apr_pool_t *scratch_pool) { + cache_level_t *level; + apr_size_t size = item_size + to_find->entry_key.key_len; + /* first, look for a previous entry for the given key */ entry_t *entry = find_entry(cache, group_index, to_find, FALSE); @@ -1435,18 +2013,23 @@ membuffer_cache_set_internal(svn_membuffer_t *cache, */ cache->data_used += (apr_uint64_t)size - entry->size; entry->size = size; + entry->priority = priority; #ifdef SVN_DEBUG_CACHE_MEMBUFFER /* Remember original content, type and key (hashes) */ - SVN_ERR(store_content_part(tag, buffer, size, scratch_pool)); + SVN_ERR(store_content_part(tag, buffer, item_size, scratch_pool)); memcpy(&entry->tag, tag, sizeof(*tag)); #endif - if (size) - memcpy(cache->data + entry->offset, buffer, size); + if (entry->key.key_len) + memcpy(cache->data + entry->offset, to_find->full_key.data, + entry->key.key_len); + if (item_size) + memcpy(cache->data + entry->offset + entry->key.key_len, buffer, + item_size); cache->total_writes++; return SVN_NO_ERROR; @@ -1454,9 +2037,8 @@ membuffer_cache_set_internal(svn_membuffer_t *cache, /* if necessary, enlarge the insertion window. */ - if ( buffer != NULL - && cache->max_entry_size >= size - && ensure_data_insertable(cache, size)) + level = buffer ? select_level(cache, size, priority) : NULL; + if (level) { /* Remove old data for this key, if that exists. * Get an unused entry for the key and and initialize it with @@ -1464,13 +2046,14 @@ membuffer_cache_set_internal(svn_membuffer_t *cache, */ entry = find_entry(cache, group_index, to_find, TRUE); entry->size = size; - entry->offset = cache->current_data; + entry->offset = level->current_data; + entry->priority = priority; #ifdef SVN_DEBUG_CACHE_MEMBUFFER /* Remember original content, type and key (hashes) */ - SVN_ERR(store_content_part(tag, buffer, size, scratch_pool)); + SVN_ERR(store_content_part(tag, buffer, item_size, scratch_pool)); memcpy(&entry->tag, tag, sizeof(*tag)); #endif @@ -1481,8 +2064,12 @@ membuffer_cache_set_internal(svn_membuffer_t *cache, /* Copy the serialized item data into the cache. */ - if (size) - memcpy(cache->data + entry->offset, buffer, size); + if (entry->key.key_len) + memcpy(cache->data + entry->offset, to_find->full_key.data, + entry->key.key_len); + if (item_size) + memcpy(cache->data + entry->offset + entry->key.key_len, buffer, + item_size); cache->total_writes++; } @@ -1511,9 +2098,10 @@ membuffer_cache_set_internal(svn_membuffer_t *cache, */ static svn_error_t * membuffer_cache_set(svn_membuffer_t *cache, - entry_key_t key, + const full_key_t *key, void *item, svn_cache__serialize_func_t serializer, + apr_uint32_t priority, DEBUG_CACHE_MEMBUFFER_TAG_ARG apr_pool_t *scratch_pool) { @@ -1523,7 +2111,7 @@ membuffer_cache_set(svn_membuffer_t *cache, /* find the entry group that will hold the key. */ - group_index = get_group_index(&cache, key); + group_index = get_group_index(&cache, &key->entry_key); /* Serialize data data. */ @@ -1538,11 +2126,27 @@ membuffer_cache_set(svn_membuffer_t *cache, group_index, buffer, size, + priority, DEBUG_CACHE_MEMBUFFER_TAG scratch_pool)); return SVN_NO_ERROR; } +/* Count a hit in ENTRY within CACHE. + */ +static void +increment_hit_counters(svn_membuffer_t *cache, entry_t *entry) +{ + /* To minimize the memory footprint of the cache index, we limit local + * hit counters to 32 bits. These may overflow but we don't really + * care because at worst, ENTRY will be dropped from cache once every + * few billion hits. */ + svn_atomic_inc(&entry->hit_count); + + /* That one is for stats only. */ + cache->total_hits++; +} + /* 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 @@ -1550,12 +2154,12 @@ membuffer_cache_set(svn_membuffer_t *cache, * be done in POOL. * * Note: This function requires the caller to serialization access. - * Don't call it directly, call membuffer_cache_get_partial instead. + * Don't call it directly, call membuffer_cache_get instead. */ static svn_error_t * membuffer_cache_get_internal(svn_membuffer_t *cache, apr_uint32_t group_index, - entry_key_t to_find, + const full_key_t *to_find, char **buffer, apr_size_t *item_size, DEBUG_CACHE_MEMBUFFER_TAG_ARG @@ -1578,9 +2182,9 @@ membuffer_cache_get_internal(svn_membuffer_t *cache, 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); + size = ALIGN_VALUE(entry->size) - entry->key.key_len; + *buffer = apr_palloc(result_pool, size); + memcpy(*buffer, cache->data + entry->offset + entry->key.key_len, size); #ifdef SVN_DEBUG_CACHE_MEMBUFFER @@ -1592,18 +2196,16 @@ membuffer_cache_get_internal(svn_membuffer_t *cache, /* Compare original content, type and key (hashes) */ - SVN_ERR(store_content_part(tag, *buffer, entry->size, result_pool)); + SVN_ERR(store_content_part(tag, *buffer, entry->size - entry->key.key_len, + 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; + increment_hit_counters(cache, entry); + *item_size = entry->size - entry->key.key_len; return SVN_NO_ERROR; } @@ -1615,7 +2217,7 @@ membuffer_cache_get_internal(svn_membuffer_t *cache, */ static svn_error_t * membuffer_cache_get(svn_membuffer_t *cache, - entry_key_t key, + const full_key_t *key, void **item, svn_cache__deserialize_func_t deserializer, DEBUG_CACHE_MEMBUFFER_TAG_ARG @@ -1627,7 +2229,7 @@ membuffer_cache_get(svn_membuffer_t *cache, /* find the entry group that will hold the key. */ - group_index = get_group_index(&cache, key); + group_index = get_group_index(&cache, &key->entry_key); WITH_READ_LOCK(cache, membuffer_cache_get_internal(cache, group_index, @@ -1648,6 +2250,59 @@ membuffer_cache_get(svn_membuffer_t *cache, 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. If no item has been stored for KEY, *FOUND + * will be FALSE and TRUE otherwise. + */ +static svn_error_t * +membuffer_cache_has_key_internal(svn_membuffer_t *cache, + apr_uint32_t group_index, + const full_key_t *to_find, + svn_boolean_t *found) +{ + entry_t *entry = find_entry(cache, group_index, to_find, FALSE); + if (entry) + { + /* This often be called by "block read" when most data is already + in L2 and only a few previously evicted items are added to L1 + again. While items in L1 are well protected for a while, L2 + items may get evicted soon. Thus, mark all them as "hit" to give + them a higher chance of survival. */ + increment_hit_counters(cache, entry); + *found = TRUE; + } + else + { + *found = FALSE; + } + + return SVN_NO_ERROR; +} + +/* Look for an entry identified by KEY. If no item has been stored + * for KEY, *FOUND will be set to FALSE and TRUE otherwise. + */ +/* Implements svn_cache__has_key for membuffer caches. + */ +static svn_error_t * +membuffer_cache_has_key(svn_membuffer_t *cache, + const full_key_t *key, + svn_boolean_t *found) +{ + /* find the entry group that will hold the key. + */ + apr_uint32_t group_index = get_group_index(&cache, &key->entry_key); + cache->total_reads++; + + WITH_READ_LOCK(cache, + membuffer_cache_has_key_internal(cache, + group_index, + key, + found)); + + return SVN_NO_ERROR; +} + /* 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. @@ -1662,7 +2317,7 @@ membuffer_cache_get(svn_membuffer_t *cache, static svn_error_t * membuffer_cache_get_partial_internal(svn_membuffer_t *cache, apr_uint32_t group_index, - entry_key_t to_find, + const full_key_t *to_find, void **item, svn_boolean_t *found, svn_cache__partial_getter_func_t deserializer, @@ -1681,11 +2336,10 @@ membuffer_cache_get_partial_internal(svn_membuffer_t *cache, } else { + const void *item_data = cache->data + entry->offset + entry->key.key_len; + apr_size_t item_size = entry->size - entry->key.key_len; *found = TRUE; - - entry->hit_count++; - cache->hit_count++; - cache->total_hits++; + increment_hit_counters(cache, entry); #ifdef SVN_DEBUG_CACHE_MEMBUFFER @@ -1697,19 +2351,12 @@ membuffer_cache_get_partial_internal(svn_membuffer_t *cache, /* 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(store_content_part(tag, item_data, item_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); + return deserializer(item, item_data, item_size, baton, result_pool); } } @@ -1721,7 +2368,7 @@ membuffer_cache_get_partial_internal(svn_membuffer_t *cache, */ static svn_error_t * membuffer_cache_get_partial(svn_membuffer_t *cache, - entry_key_t key, + const full_key_t *key, void **item, svn_boolean_t *found, svn_cache__partial_getter_func_t deserializer, @@ -1729,7 +2376,7 @@ membuffer_cache_get_partial(svn_membuffer_t *cache, DEBUG_CACHE_MEMBUFFER_TAG_ARG apr_pool_t *result_pool) { - apr_uint32_t group_index = get_group_index(&cache, key); + apr_uint32_t group_index = get_group_index(&cache, &key->entry_key); WITH_READ_LOCK(cache, membuffer_cache_get_partial_internal @@ -1753,7 +2400,7 @@ membuffer_cache_get_partial(svn_membuffer_t *cache, static svn_error_t * membuffer_cache_set_partial_internal(svn_membuffer_t *cache, apr_uint32_t group_index, - entry_key_t to_find, + const full_key_t *to_find, svn_cache__partial_setter_func_t func, void *baton, DEBUG_CACHE_MEMBUFFER_TAG_ARG @@ -1771,12 +2418,12 @@ membuffer_cache_set_partial_internal(svn_membuffer_t *cache, 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; + apr_size_t key_len = entry->key.key_len; + void *item_data = cache->data + entry->offset + key_len; + void *orig_data = item_data; + apr_size_t item_size = entry->size - key_len; - entry->hit_count++; - cache->hit_count++; + increment_hit_counters(cache, entry); cache->total_writes++; #ifdef SVN_DEBUG_CACHE_MEMBUFFER @@ -1784,19 +2431,19 @@ membuffer_cache_set_partial_internal(svn_membuffer_t *cache, /* Check for overlapping entries. */ SVN_ERR_ASSERT(entry->next == NO_INDEX || - entry->offset + size + entry->offset + entry->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(store_content_part(tag, item_data, item_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); + err = func(&item_data, &item_size, baton, scratch_pool); if (err) { @@ -1805,27 +2452,34 @@ membuffer_cache_set_partial_internal(svn_membuffer_t *cache, * We better drop that. */ drop_entry(cache, entry); + + return err; } 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) + if (item_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)) + if ( (cache->max_entry_size >= item_size + key_len) + && ensure_data_insertable_l1(cache, item_size + key_len)) { /* 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); + entry->size = item_size + key_len; + entry->offset = cache->l1.current_data; + + if (key_len) + memcpy(cache->data + entry->offset, + to_find->full_key.data, key_len); + if (item_size) + memcpy(cache->data + entry->offset + key_len, item_data, + item_size); /* Link the entry properly. */ @@ -1837,7 +2491,7 @@ membuffer_cache_set_partial_internal(svn_membuffer_t *cache, /* Remember original content, type and key (hashes) */ - SVN_ERR(store_content_part(tag, data, size, scratch_pool)); + SVN_ERR(store_content_part(tag, item_data, item_size, scratch_pool)); memcpy(&entry->tag, tag, sizeof(*tag)); #endif @@ -1854,7 +2508,7 @@ membuffer_cache_set_partial_internal(svn_membuffer_t *cache, */ static svn_error_t * membuffer_cache_set_partial(svn_membuffer_t *cache, - entry_key_t key, + const full_key_t *key, svn_cache__partial_setter_func_t func, void *baton, DEBUG_CACHE_MEMBUFFER_TAG_ARG @@ -1862,7 +2516,7 @@ membuffer_cache_set_partial(svn_membuffer_t *cache, { /* cache item lookup */ - apr_uint32_t group_index = get_group_index(&cache, key); + apr_uint32_t group_index = get_group_index(&cache, &key->entry_key); WITH_WRITE_LOCK(cache, membuffer_cache_set_partial_internal (cache, group_index, key, func, baton, @@ -1907,44 +2561,26 @@ typedef struct svn_membuffer_cache_t 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. + * This makes our keys different from all keys used by svn_membuffer_cache_t + * instances that we don't want to share cached data with. */ - const char* full_prefix; + full_key_t 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; + /* priority class for all items written through this interface */ + apr_uint32_t priority; - /* an internal counter that is used to clear the pool from time to time - * but not too frequently. + /* Temporary buffer containing the hash key for the current access */ - int alloc_counter; + full_key_t combined_key; /* 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 @@ -1952,46 +2588,89 @@ typedef struct svn_membuffer_cache_t */ #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. + * This could replace combine_key() entirely but we actually use it only + * when the quick path failed. */ static void -combine_key(svn_membuffer_cache_t *cache, - const void *key, - apr_ssize_t key_len) +combine_long_key(svn_membuffer_cache_t *cache, + const void *key, + apr_ssize_t key_len) { + apr_uint32_t *digest_buffer; + char *key_copy; + apr_size_t prefix_len = cache->prefix.entry_key.key_len; + apr_size_t aligned_key_len; + + /* handle variable-length keys */ 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); + aligned_key_len = ALIGN_VALUE(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); + /* Combine keys. */ + svn_membuf__ensure(&cache->combined_key.full_key, + aligned_key_len + prefix_len); - svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key, data); - } - else if (key_len < 64) + key_copy = (char *)cache->combined_key.full_key.data + prefix_len; + cache->combined_key.entry_key.key_len = aligned_key_len + prefix_len; + memcpy(key_copy, key, key_len); + memset(key_copy + key_len, 0, aligned_key_len - key_len); + + /* Hash key into 16 bytes. */ + digest_buffer = (apr_uint32_t *)cache->combined_key.entry_key.fingerprint; + svn__fnv1a_32x4_raw(digest_buffer, key, key_len); + + /* Combine with prefix. */ + cache->combined_key.entry_key.fingerprint[0] + ^= cache->prefix.entry_key.fingerprint[0]; + cache->combined_key.entry_key.fingerprint[1] + ^= cache->prefix.entry_key.fingerprint[1]; +} + +/* 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) +{ + /* short, fixed-size keys are the most common case */ + if (key_len != APR_HASH_KEY_STRING && key_len <= 16) { - apr_uint32_t data[16] = { 0 }; + const apr_size_t prefix_len = cache->prefix.entry_key.key_len; + + /* Copy of *key, padded with 0. + * We put it just behind the prefix already copied into the COMBINED_KEY. + * The buffer space has been allocated when the cache was created. */ + apr_uint64_t *data = (void *)((char *)cache->combined_key.full_key.data + + prefix_len); + assert(prefix_len <= cache->combined_key.full_key.size - 16); + cache->combined_key.entry_key.key_len = prefix_len + 16; + + data[0] = 0; + data[1] = 0; memcpy(data, key, key_len); - svn__pseudo_md5_63((apr_uint32_t *)cache->combined_key, data); + /* scramble key DATA. All of this must be reversible to prevent key + * collisions. So, we limit ourselves to xor and permutations. */ + data[1] = (data[1] << 27) | (data[1] >> 37); + data[1] ^= data[0] & 0xffff; + data[0] ^= data[1] & APR_UINT64_C(0xffffffffffff0000); + + /* combine with this cache's namespace */ + cache->combined_key.entry_key.fingerprint[0] + = data[0] ^ cache->prefix.entry_key.fingerprint[0]; + cache->combined_key.entry_key.fingerprint[1] + = data[1] ^ cache->prefix.entry_key.fingerprint[1]; } else { - apr_md5((unsigned char*)cache->combined_key, key, key_len); + /* longer or variably sized keys */ + combine_long_key(cache, 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) @@ -2005,7 +2684,7 @@ svn_membuffer_cache_get(void **value_p, { svn_membuffer_cache_t *cache = cache_void; - DEBUG_CACHE_MEMBUFFER_INIT_TAG + DEBUG_CACHE_MEMBUFFER_INIT_TAG(result_pool) /* special case */ if (key == NULL) @@ -2023,7 +2702,7 @@ svn_membuffer_cache_get(void **value_p, /* Look the item up. */ SVN_ERR(membuffer_cache_get(cache->membuffer, - cache->combined_key, + &cache->combined_key, value_p, cache->deserializer, DEBUG_CACHE_MEMBUFFER_TAG @@ -2031,6 +2710,39 @@ svn_membuffer_cache_get(void **value_p, /* return result */ *found = *value_p != NULL; + + return SVN_NO_ERROR; +} + +/* Implement svn_cache__vtable_t.has_key (not thread-safe) + */ +static svn_error_t * +svn_membuffer_cache_has_key(svn_boolean_t *found, + void *cache_void, + const void *key, + apr_pool_t *scratch_pool) +{ + svn_membuffer_cache_t *cache = cache_void; + + /* special case */ + if (key == 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_has_key(cache->membuffer, + &cache->combined_key, + found)); + + /* return result */ return SVN_NO_ERROR; } @@ -2044,22 +2756,12 @@ svn_membuffer_cache_set(void *cache_void, { svn_membuffer_cache_t *cache = cache_void; - DEBUG_CACHE_MEMBUFFER_INIT_TAG + DEBUG_CACHE_MEMBUFFER_INIT_TAG(scratch_pool) /* 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 */ @@ -2069,11 +2771,12 @@ svn_membuffer_cache_set(void *cache_void, * that the item will actually be cached afterwards. */ return membuffer_cache_set(cache->membuffer, - cache->combined_key, + &cache->combined_key, value, cache->serializer, + cache->priority, DEBUG_CACHE_MEMBUFFER_TAG - cache->pool); + scratch_pool); } /* Implement svn_cache__vtable_t.iter as "not implemented" @@ -2102,7 +2805,7 @@ svn_membuffer_cache_get_partial(void **value_p, { svn_membuffer_cache_t *cache = cache_void; - DEBUG_CACHE_MEMBUFFER_INIT_TAG + DEBUG_CACHE_MEMBUFFER_INIT_TAG(result_pool) if (key == NULL) { @@ -2114,7 +2817,7 @@ svn_membuffer_cache_get_partial(void **value_p, combine_key(cache, key, cache->key_len); SVN_ERR(membuffer_cache_get_partial(cache->membuffer, - cache->combined_key, + &cache->combined_key, value_p, found, func, @@ -2136,13 +2839,13 @@ svn_membuffer_cache_set_partial(void *cache_void, { svn_membuffer_cache_t *cache = cache_void; - DEBUG_CACHE_MEMBUFFER_INIT_TAG + DEBUG_CACHE_MEMBUFFER_INIT_TAG(scratch_pool) if (key != NULL) { combine_key(cache, key, cache->key_len); SVN_ERR(membuffer_cache_set_partial(cache->membuffer, - cache->combined_key, + &cache->combined_key, func, baton, DEBUG_CACHE_MEMBUFFER_TAG @@ -2162,23 +2865,41 @@ svn_membuffer_cache_is_cachable(void *cache_void, apr_size_t 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; + return cache->priority > SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY + ? cache->membuffer->l2.size >= size && MAX_ITEM_SIZE >= size + : size <= cache->membuffer->max_entry_size; } -/* Add statistics of SEGMENT to INFO. +/* Add statistics of SEGMENT to INFO. If INCLUDE_HISTOGRAM is TRUE, + * accumulate index bucket fill levels in INFO->HISTOGRAM. */ static svn_error_t * svn_membuffer_get_segment_info(svn_membuffer_t *segment, - svn_cache__info_t *info) + svn_cache__info_t *info, + svn_boolean_t include_histogram) { - info->data_size += segment->data_size; + apr_uint32_t i; + + info->data_size += segment->l1.size + segment->l2.size; info->used_size += segment->data_used; - info->total_size += segment->data_size + + info->total_size += segment->l1.size + segment->l2.size + segment->group_count * GROUP_SIZE * sizeof(entry_t); info->used_entries += segment->used_entries; info->total_entries += segment->group_count * GROUP_SIZE; + if (include_histogram) + for (i = 0; i < segment->group_count; ++i) + if (is_group_initialized(segment, i)) + { + entry_group_t *chain_end + = last_group_in_chain(segment, &segment->directory[i]); + apr_size_t use + = MIN(chain_end->header.used, + sizeof(info->histogram) / sizeof(info->histogram[0]) - 1); + info->histogram[use]++; + } + return SVN_NO_ERROR; } @@ -2196,22 +2917,15 @@ svn_membuffer_cache_get_info(void *cache_void, /* cache front-end specific data */ - info->id = apr_pstrdup(result_pool, cache->full_prefix); + info->id = apr_pstrdup(result_pool, cache->prefix.full_key.data); /* 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)); + svn_membuffer_get_segment_info(segment, info, FALSE)); } return SVN_NO_ERROR; @@ -2222,6 +2936,7 @@ svn_membuffer_cache_get_info(void *cache_void, */ static svn_cache__vtable_t membuffer_cache_vtable = { svn_membuffer_cache_get, + svn_membuffer_cache_has_key, svn_membuffer_cache_set, svn_membuffer_cache_iter, svn_membuffer_cache_is_cachable, @@ -2250,6 +2965,24 @@ svn_membuffer_cache_get_synced(void **value_p, return SVN_NO_ERROR; } +/* Implement svn_cache__vtable_t.has_key and serialize all cache access. + */ +static svn_error_t * +svn_membuffer_cache_has_key_synced(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_has_key(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 * @@ -2316,6 +3049,7 @@ svn_membuffer_cache_set_partial_synced(void *cache_void, */ static svn_cache__vtable_t membuffer_cache_synced_vtable = { svn_membuffer_cache_get_synced, + svn_membuffer_cache_has_key_synced, svn_membuffer_cache_set_synced, svn_membuffer_cache_iter, /* no sync required */ svn_membuffer_cache_is_cachable, /* no sync required */ @@ -2370,15 +3104,18 @@ svn_cache__create_membuffer_cache(svn_cache__t **cache_p, svn_cache__deserialize_func_t deserializer, apr_ssize_t klen, const char *prefix, + apr_uint32_t priority, svn_boolean_t thread_safe, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_checksum_t *checksum; + apr_size_t prefix_len, prefix_orig_len; /* allocate the cache header structures */ - svn_cache__t *wrapper = apr_pcalloc(pool, sizeof(*wrapper)); - svn_membuffer_cache_t *cache = apr_palloc(pool, sizeof(*cache)); + svn_cache__t *wrapper = apr_pcalloc(result_pool, sizeof(*wrapper)); + svn_membuffer_cache_t *cache = apr_pcalloc(result_pool, sizeof(*cache)); /* initialize our internal cache header */ @@ -2389,30 +3126,38 @@ svn_cache__create_membuffer_cache(svn_cache__t **cache_p, cache->deserializer = deserializer ? deserializer : deserialize_svn_stringbuf; - cache->full_prefix = apr_pstrdup(pool, prefix); + cache->priority = priority; cache->key_len = klen; - cache->pool = svn_pool_create(pool); - cache->alloc_counter = 0; - SVN_ERR(svn_mutex__init(&cache->mutex, thread_safe, pool)); + SVN_ERR(svn_mutex__init(&cache->mutex, thread_safe, result_pool)); - /* for performance reasons, we don't actually store the full prefix but a - * hash value of it - */ + /* Copy the prefix into the prefix full key. Align it to ITEM_ALIGMENT. + * Don't forget to include the terminating NUL. */ + prefix_orig_len = strlen(prefix) + 1; + prefix_len = ALIGN_VALUE(prefix_orig_len); + + svn_membuf__create(&cache->prefix.full_key, prefix_len, result_pool); + memcpy((char *)cache->prefix.full_key.data, prefix, prefix_orig_len); + memset((char *)cache->prefix.full_key.data + prefix_orig_len, 0, + prefix_len - prefix_orig_len); + + /* Construct the folded prefix key. */ 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 + scratch_pool)); + memcpy(cache->prefix.entry_key.fingerprint, checksum->digest, + sizeof(cache->prefix.entry_key.fingerprint)); + cache->prefix.entry_key.key_len = prefix_len; + + /* Initialize the combined key. Pre-allocate some extra room in the full + * key such that we probably don't need to re-alloc. */ + cache->combined_key.entry_key = cache->prefix.entry_key; + svn_membuf__create(&cache->combined_key.full_key, prefix_len + 200, + result_pool); + memcpy(cache->combined_key.full_key.data, cache->prefix.full_key.data, + prefix_len); /* initialize the generic cache wrapper */ @@ -2421,8 +3166,43 @@ svn_cache__create_membuffer_cache(svn_cache__t **cache_p, wrapper->cache_internal = cache; wrapper->error_handler = 0; wrapper->error_baton = 0; + wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT"); *cache_p = wrapper; return SVN_NO_ERROR; } +static svn_error_t * +svn_membuffer_get_global_segment_info(svn_membuffer_t *segment, + svn_cache__info_t *info) +{ + info->gets += segment->total_reads; + info->sets += segment->total_writes; + info->hits += segment->total_hits; + + WITH_READ_LOCK(segment, + svn_membuffer_get_segment_info(segment, info, TRUE)); + + return SVN_NO_ERROR; +} + +svn_cache__info_t * +svn_cache__membuffer_get_global_info(apr_pool_t *pool) +{ + apr_uint32_t i; + + svn_membuffer_t *membuffer = svn_cache__get_global_membuffer_cache(); + svn_cache__info_t *info = apr_pcalloc(pool, sizeof(*info)); + + /* cache front-end specific data */ + + info->id = "membuffer globals"; + + /* collect info from shared cache back-end */ + + for (i = 0; i < membuffer->segment_count; ++i) + svn_error_clear(svn_membuffer_get_global_segment_info(membuffer + i, + info)); + + return info; +} diff --git a/contrib/subversion/subversion/libsvn_subr/cache-memcache.c b/contrib/subversion/subversion/libsvn_subr/cache-memcache.c index 500426d0f..333eb44d6 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache-memcache.c +++ b/contrib/subversion/subversion/libsvn_subr/cache-memcache.c @@ -99,7 +99,7 @@ build_key(const char **mc_key, } long_key = apr_pstrcat(pool, "SVN:", cache->prefix, ":", encoded_suffix, - (char *)NULL); + SVN_VA_NULL); long_key_len = strlen(long_key); /* We don't want to have a key that's too big. If it was going to @@ -120,7 +120,7 @@ build_key(const char **mc_key, apr_pstrmemdup(pool, long_key, MEMCACHED_KEY_UNHASHED_LEN), svn_checksum_to_cstring_display(checksum, pool), - (char *)NULL); + SVN_VA_NULL); } *mc_key = long_key; @@ -214,6 +214,26 @@ memcache_get(void **value_p, return SVN_NO_ERROR; } +/* Implement vtable.has_key in terms of the getter. + */ +static svn_error_t * +memcache_has_key(svn_boolean_t *found, + void *cache_void, + const void *key, + apr_pool_t *scratch_pool) +{ + char *data; + apr_size_t data_len; + SVN_ERR(memcache_internal_get(&data, + &data_len, + found, + cache_void, + key, + scratch_pool)); + + return SVN_NO_ERROR; +} + /* Core functionality of our setter functions: store LENGH bytes of DATA * to be identified by KEY in the memcached given by CACHE_VOID. Use POOL * for temporary allocations. @@ -347,7 +367,7 @@ memcache_iter(svn_boolean_t *completed, static svn_boolean_t memcache_is_cachable(void *unused, apr_size_t size) { - (void)unused; /* silence gcc warning. */ + SVN_UNUSED(unused); /* The memcached cutoff seems to be a bit (header length?) under a megabyte. * We round down a little to be safe. @@ -367,17 +387,12 @@ memcache_get_info(void *cache_void, /* we don't have any memory allocation info */ - info->used_size = 0; - info->total_size = 0; - info->data_size = 0; - info->used_entries = 0; - info->total_entries = 0; - return SVN_NO_ERROR; } static svn_cache__vtable_t memcache_vtable = { memcache_get, + memcache_has_key, memcache_set, memcache_iter, memcache_is_cachable, @@ -408,6 +423,7 @@ svn_cache__create_memcache(svn_cache__t **cache_p, wrapper->cache_internal = cache; wrapper->error_handler = 0; wrapper->error_baton = 0; + wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT"); *cache_p = wrapper; return SVN_NO_ERROR; @@ -528,20 +544,17 @@ nop_enumerator(const char *name, svn_error_t * svn_cache__make_memcache_from_config(svn_memcache_t **memcache_p, svn_config_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - int server_count; - apr_pool_t *subpool = svn_pool_create(pool); - - server_count = + int server_count = svn_config_enumerate2(config, SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS, - nop_enumerator, NULL, subpool); + nop_enumerator, NULL, scratch_pool); if (server_count == 0) { *memcache_p = NULL; - svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -551,8 +564,8 @@ svn_cache__make_memcache_from_config(svn_memcache_t **memcache_p, #ifdef SVN_HAVE_MEMCACHE { struct ams_baton b; - svn_memcache_t *memcache = apr_pcalloc(pool, sizeof(*memcache)); - apr_status_t apr_err = apr_memcache_create(pool, + svn_memcache_t *memcache = apr_pcalloc(result_pool, sizeof(*memcache)); + apr_status_t apr_err = apr_memcache_create(result_pool, (apr_uint16_t)server_count, 0, /* flags */ &(memcache->c)); @@ -561,19 +574,18 @@ svn_cache__make_memcache_from_config(svn_memcache_t **memcache_p, _("Unknown error creating apr_memcache_t")); b.memcache = memcache->c; - b.memcache_pool = pool; + b.memcache_pool = result_pool; b.err = SVN_NO_ERROR; svn_config_enumerate2(config, SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS, add_memcache_server, &b, - subpool); + scratch_pool); if (b.err) return b.err; *memcache_p = memcache; - svn_pool_destroy(subpool); return SVN_NO_ERROR; } #else /* ! SVN_HAVE_MEMCACHE */ diff --git a/contrib/subversion/subversion/libsvn_subr/cache.c b/contrib/subversion/subversion/libsvn_subr/cache.c index 70e189f16..06bb45412 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache.c +++ b/contrib/subversion/subversion/libsvn_subr/cache.c @@ -76,7 +76,7 @@ svn_cache__get(void **value_p, out with FOUND set to false. */ *found = FALSE; #ifdef SVN_DEBUG - if (getenv("SVN_X_DOES_NOT_MARK_THE_SPOT")) + if (cache->pretend_empty) return SVN_NO_ERROR; #endif @@ -95,6 +95,26 @@ svn_cache__get(void **value_p, return err; } +svn_error_t * +svn_cache__has_key(svn_boolean_t *found, + svn_cache__t *cache, + const void *key, + apr_pool_t *scratch_pool) +{ + *found = FALSE; +#ifdef SVN_DEBUG + if (cache->pretend_empty) + return SVN_NO_ERROR; +#endif + + return handle_error(cache, + (cache->vtable->has_key)(found, + cache->cache_internal, + key, + scratch_pool), + scratch_pool); +} + svn_error_t * svn_cache__set(svn_cache__t *cache, const void *key, @@ -119,7 +139,7 @@ svn_cache__iter(svn_boolean_t *completed, apr_pool_t *scratch_pool) { #ifdef SVN_DEBUG - if (getenv("SVN_X_DOES_NOT_MARK_THE_SPOT")) + if (cache->pretend_empty) /* Pretend CACHE is empty. */ return SVN_NO_ERROR; #endif @@ -146,7 +166,7 @@ svn_cache__get_partial(void **value, out with FOUND set to false. */ *found = FALSE; #ifdef SVN_DEBUG - if (getenv("SVN_X_DOES_NOT_MARK_THE_SPOT")) + if (cache->pretend_empty) return SVN_NO_ERROR; #endif @@ -192,6 +212,7 @@ svn_cache__get_info(svn_cache__t *cache, { /* write general statistics */ + memset(info, 0, sizeof(*info)); info->gets = cache->reads; info->hits = cache->hits; info->sets = cache->writes; @@ -221,6 +242,7 @@ svn_cache__get_info(svn_cache__t *cache, svn_string_t * svn_cache__format_info(const svn_cache__info_t *info, + svn_boolean_t access_only, apr_pool_t *result_pool) { enum { _1MB = 1024 * 1024 }; @@ -235,9 +257,40 @@ svn_cache__format_info(const svn_cache__info_t *info, double data_entry_rate = (100.0 * (double)info->used_entries) / (double)(info->total_entries ? info->total_entries : 1); - return svn_string_createf(result_pool, + const char *histogram = ""; + if (!access_only) + { + svn_stringbuf_t *text = svn_stringbuf_create_empty(result_pool); + + int i; + int count = sizeof(info->histogram) / sizeof(info->histogram[0]); + for (i = count - 1; i >= 0; --i) + if (info->histogram[i] > 0 || text->len > 0) + text = svn_stringbuf_createf(result_pool, + i == count - 1 + ? "%s%12" APR_UINT64_T_FMT + " buckets with >%d entries\n" + : "%s%12" APR_UINT64_T_FMT + " buckets with %d entries\n", + text->data, info->histogram[i], i); + + histogram = text->data; + } + + return access_only + ? svn_string_createf(result_pool, + "%s\n" + "gets : %" APR_UINT64_T_FMT + ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n" + "sets : %" APR_UINT64_T_FMT + " (%5.2f%% of misses)\n", + info->id, + info->gets, + info->hits, hit_rate, + info->sets, write_rate) + : svn_string_createf(result_pool, - "prefix : %s\n" + "%s\n" "gets : %" APR_UINT64_T_FMT ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n" "sets : %" APR_UINT64_T_FMT @@ -247,7 +300,7 @@ svn_cache__format_info(const svn_cache__info_t *info, " of %" APR_UINT64_T_FMT " MB data cache" " / %" APR_UINT64_T_FMT " MB total cache memory\n" " %" APR_UINT64_T_FMT " entries (%5.2f%%)" - " of %" APR_UINT64_T_FMT " total\n", + " of %" APR_UINT64_T_FMT " total\n%s", info->id, @@ -261,5 +314,6 @@ svn_cache__format_info(const svn_cache__info_t *info, info->total_size / _1MB, info->used_entries, data_entry_rate, - info->total_entries); + info->total_entries, + histogram); } diff --git a/contrib/subversion/subversion/libsvn_subr/cache.h b/contrib/subversion/subversion/libsvn_subr/cache.h index 5029cefe1..62a1f8e4a 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache.h +++ b/contrib/subversion/subversion/libsvn_subr/cache.h @@ -38,6 +38,12 @@ typedef struct svn_cache__vtable_t { const void *key, apr_pool_t *result_pool); + /* See svn_cache__has_key(). */ + svn_error_t *(*has_key)(svn_boolean_t *found, + void *cache_implementation, + const void *key, + apr_pool_t *scratch_pool); + /* See svn_cache__set(). */ svn_error_t *(*set)(void *cache_implementation, const void *key, @@ -99,6 +105,10 @@ struct svn_cache__t { /* Total number of function calls that returned an error. */ apr_uint64_t failures; + + /* Cause all getters to act as though the cache contains no data. + (Currently this never becomes set except in maintainer builds.) */ + svn_boolean_t pretend_empty; }; diff --git a/contrib/subversion/subversion/libsvn_subr/cache_config.c b/contrib/subversion/subversion/libsvn_subr/cache_config.c index 17659f8c8..639124e79 100644 --- a/contrib/subversion/subversion/libsvn_subr/cache_config.c +++ b/contrib/subversion/subversion/libsvn_subr/cache_config.c @@ -27,6 +27,7 @@ #include "private/svn_cache.h" #include "svn_pools.h" +#include "svn_sorts.h" /* The cache settings as a process-wide singleton. */ @@ -80,7 +81,13 @@ initialize_cache(void *baton, apr_pool_t *unused_pool) svn_membuffer_t **cache_p = baton; svn_membuffer_t *cache = NULL; - apr_uint64_t cache_size = cache_settings.cache_size; + /* Limit the cache size to about half the available address space + * (typ. 1G under 32 bits). + */ + apr_uint64_t cache_size = MIN(cache_settings.cache_size, + (apr_uint64_t)SVN_MAX_OBJECT_SIZE / 2); + + /* Create caches at all? */ if (cache_size) { svn_error_t *err; @@ -116,7 +123,7 @@ initialize_cache(void *baton, apr_pool_t *unused_pool) err = svn_cache__membuffer_cache_create( &cache, (apr_size_t)cache_size, - (apr_size_t)(cache_size / 10), + (apr_size_t)(cache_size / 5), 0, ! svn_cache_config_get()->single_threaded, FALSE, diff --git a/contrib/subversion/subversion/libsvn_subr/checksum.c b/contrib/subversion/subversion/libsvn_subr/checksum.c index e5d6a620e..6b195f8a5 100644 --- a/contrib/subversion/subversion/libsvn_subr/checksum.c +++ b/contrib/subversion/subversion/libsvn_subr/checksum.c @@ -21,6 +21,7 @@ * ==================================================================== */ +#define APR_WANT_BYTEFUNC #include @@ -30,9 +31,10 @@ #include "svn_checksum.h" #include "svn_error.h" #include "svn_ctype.h" +#include "svn_sorts.h" -#include "sha1.h" -#include "md5.h" +#include "checksum.h" +#include "fnv1a.h" #include "private/svn_subr_private.h" @@ -40,17 +42,117 @@ +/* The MD5 digest for the empty string. */ +static const unsigned char md5_empty_string_digest_array[] = { + 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e +}; + +/* The SHA1 digest for the empty string. */ +static const unsigned char sha1_empty_string_digest_array[] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 +}; + +/* The FNV-1a digest for the empty string. */ +static const unsigned char fnv1a_32_empty_string_digest_array[] = { + 0x81, 0x1c, 0x9d, 0xc5 +}; + +/* The FNV-1a digest for the empty string. */ +static const unsigned char fnv1a_32x4_empty_string_digest_array[] = { + 0xcd, 0x6d, 0x9a, 0x85 +}; + +/* Digests for an empty string, indexed by checksum type */ +static const unsigned char * empty_string_digests[] = { + md5_empty_string_digest_array, + sha1_empty_string_digest_array, + fnv1a_32_empty_string_digest_array, + fnv1a_32x4_empty_string_digest_array +}; + +/* Digest sizes in bytes, indexed by checksum type */ +static const apr_size_t digest_sizes[] = { + APR_MD5_DIGESTSIZE, + APR_SHA1_DIGESTSIZE, + sizeof(apr_uint32_t), + sizeof(apr_uint32_t) +}; + +/* Checksum type prefixes used in serialized checksums. */ +static const char *ckind_str[] = { + "$md5 $", + "$sha1$", + "$fnv1$", + "$fnvm$", +}; + /* Returns the digest size of it's argument. */ -#define DIGESTSIZE(k) ((k) == svn_checksum_md5 ? APR_MD5_DIGESTSIZE : \ - (k) == svn_checksum_sha1 ? APR_SHA1_DIGESTSIZE : 0) +#define DIGESTSIZE(k) \ + (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k]) + +/* Largest supported digest size */ +#define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE)) + +const unsigned char * +svn__empty_string_digest(svn_checksum_kind_t kind) +{ + return empty_string_digests[kind]; +} + +const char * +svn__digest_to_cstring_display(const unsigned char digest[], + apr_size_t digest_size, + apr_pool_t *pool) +{ + static const char *hex = "0123456789abcdef"; + char *str = apr_palloc(pool, (digest_size * 2) + 1); + apr_size_t i; + + for (i = 0; i < digest_size; i++) + { + str[i*2] = hex[digest[i] >> 4]; + str[i*2+1] = hex[digest[i] & 0x0f]; + } + str[i*2] = '\0'; + + return str; +} + + +const char * +svn__digest_to_cstring(const unsigned char digest[], + apr_size_t digest_size, + apr_pool_t *pool) +{ + static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 }; + + if (memcmp(digest, zeros_digest, digest_size) != 0) + return svn__digest_to_cstring_display(digest, digest_size, pool); + else + return NULL; +} +svn_boolean_t +svn__digests_match(const unsigned char d1[], + const unsigned char d2[], + apr_size_t digest_size) +{ + static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 }; + + return ((memcmp(d1, d2, digest_size) == 0) + || (memcmp(d2, zeros, digest_size) == 0) + || (memcmp(d1, zeros, digest_size) == 0)); +} + /* Check to see if KIND is something we recognize. If not, return * SVN_ERR_BAD_CHECKSUM_KIND */ static svn_error_t * validate_kind(svn_checksum_kind_t kind) { - if (kind == svn_checksum_md5 || kind == svn_checksum_sha1) + if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4) return SVN_NO_ERROR; else return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); @@ -71,12 +173,15 @@ checksum_create_without_digest(svn_checksum_kind_t kind, return checksum; } +/* Return a checksum object, allocated in POOL. The checksum will be of + * type KIND and contain the given DIGEST. + */ static svn_checksum_t * checksum_create(svn_checksum_kind_t kind, - apr_size_t digest_size, const unsigned char *digest, apr_pool_t *pool) { + apr_size_t digest_size = DIGESTSIZE(kind); svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size, pool); memcpy((unsigned char *)checksum->digest, digest, digest_size); @@ -93,11 +198,12 @@ svn_checksum_create(svn_checksum_kind_t kind, switch (kind) { case svn_checksum_md5: - digest_size = APR_MD5_DIGESTSIZE; - break; case svn_checksum_sha1: - digest_size = APR_SHA1_DIGESTSIZE; + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + digest_size = digest_sizes[kind]; break; + default: return NULL; } @@ -111,16 +217,28 @@ svn_checksum_t * svn_checksum__from_digest_md5(const unsigned char *digest, apr_pool_t *result_pool) { - return checksum_create(svn_checksum_md5, APR_MD5_DIGESTSIZE, digest, - result_pool); + return checksum_create(svn_checksum_md5, digest, result_pool); } svn_checksum_t * svn_checksum__from_digest_sha1(const unsigned char *digest, apr_pool_t *result_pool) { - return checksum_create(svn_checksum_sha1, APR_SHA1_DIGESTSIZE, digest, - result_pool); + return checksum_create(svn_checksum_sha1, digest, result_pool); +} + +svn_checksum_t * +svn_checksum__from_digest_fnv1a_32(const unsigned char *digest, + apr_pool_t *result_pool) +{ + return checksum_create(svn_checksum_fnv1a_32, digest, result_pool); +} + +svn_checksum_t * +svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest, + apr_pool_t *result_pool) +{ + return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool); } svn_error_t * @@ -145,9 +263,13 @@ svn_checksum_match(const svn_checksum_t *checksum1, switch (checksum1->kind) { case svn_checksum_md5: - return svn_md5__digests_match(checksum1->digest, checksum2->digest); case svn_checksum_sha1: - return svn_sha1__digests_match(checksum1->digest, checksum2->digest); + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return svn__digests_match(checksum1->digest, + checksum2->digest, + digest_sizes[checksum1->kind]); + default: /* We really shouldn't get here, but if we do... */ return FALSE; @@ -161,9 +283,13 @@ svn_checksum_to_cstring_display(const svn_checksum_t *checksum, switch (checksum->kind) { case svn_checksum_md5: - return svn_md5__digest_to_cstring_display(checksum->digest, pool); case svn_checksum_sha1: - return svn_sha1__digest_to_cstring_display(checksum->digest, pool); + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return svn__digest_to_cstring_display(checksum->digest, + digest_sizes[checksum->kind], + pool); + default: /* We really shouldn't get here, but if we do... */ return NULL; @@ -180,9 +306,13 @@ svn_checksum_to_cstring(const svn_checksum_t *checksum, switch (checksum->kind) { case svn_checksum_md5: - return svn_md5__digest_to_cstring(checksum->digest, pool); case svn_checksum_sha1: - return svn_sha1__digest_to_cstring(checksum->digest, pool); + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return svn__digest_to_cstring(checksum->digest, + digest_sizes[checksum->kind], + pool); + default: /* We really shouldn't get here, but if we do... */ return NULL; @@ -195,15 +325,12 @@ svn_checksum_serialize(const svn_checksum_t *checksum, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const char *ckind_str; - - SVN_ERR_ASSERT_NO_RETURN(checksum->kind == svn_checksum_md5 - || checksum->kind == svn_checksum_sha1); - ckind_str = (checksum->kind == svn_checksum_md5 ? "$md5 $" : "$sha1$"); + SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5 + || checksum->kind <= svn_checksum_fnv1a_32x4); return apr_pstrcat(result_pool, - ckind_str, + ckind_str[checksum->kind], svn_checksum_to_cstring(checksum, scratch_pool), - (char *)NULL); + SVN_VA_NULL); } @@ -213,18 +340,29 @@ svn_checksum_deserialize(const svn_checksum_t **checksum, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_checksum_kind_t ckind; + svn_checksum_kind_t kind; svn_checksum_t *parsed_checksum; - /* "$md5 $..." or "$sha1$..." */ - SVN_ERR_ASSERT(strlen(data) > 6); - - ckind = (data[1] == 'm' ? svn_checksum_md5 : svn_checksum_sha1); - SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, ckind, - data + 6, result_pool)); - *checksum = parsed_checksum; - - return SVN_NO_ERROR; + /* All prefixes have the same length. */ + apr_size_t prefix_len = strlen(ckind_str[0]); + + /* "$md5 $...", "$sha1$..." or ... */ + if (strlen(data) <= prefix_len) + return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, + _("Invalid prefix in checksum '%s'"), + data); + + for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind) + if (strncmp(ckind_str[kind], data, prefix_len) == 0) + { + SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind, + data + prefix_len, result_pool)); + *checksum = parsed_checksum; + return SVN_NO_ERROR; + } + + return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL, + "Unknown checksum kind in '%s'", data); } @@ -234,7 +372,7 @@ svn_checksum_parse_hex(svn_checksum_t **checksum, const char *hex, apr_pool_t *pool) { - int i, len; + apr_size_t i, len; char is_nonzero = '\0'; char *digest; static const char xdigitval[256] = @@ -302,11 +440,11 @@ svn_checksum_dup(const svn_checksum_t *checksum, switch (checksum->kind) { case svn_checksum_md5: - return svn_checksum__from_digest_md5(checksum->digest, pool); - break; case svn_checksum_sha1: - return svn_checksum__from_digest_sha1(checksum->digest, pool); - break; + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return checksum_create(checksum->kind, checksum->digest, pool); + default: SVN_ERR_MALFUNCTION_NO_RETURN(); break; @@ -337,6 +475,16 @@ svn_checksum(svn_checksum_t **checksum, apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx); break; + case svn_checksum_fnv1a_32: + *(apr_uint32_t *)(*checksum)->digest + = htonl(svn__fnv1a_32(data, len)); + break; + + case svn_checksum_fnv1a_32x4: + *(apr_uint32_t *)(*checksum)->digest + = htonl(svn__fnv1a_32x4(data, len)); + break; + default: /* We really shouldn't get here, but if we do... */ return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); @@ -353,12 +501,10 @@ svn_checksum_empty_checksum(svn_checksum_kind_t kind, switch (kind) { case svn_checksum_md5: - return svn_checksum__from_digest_md5(svn_md5__empty_string_digest(), - pool); - case svn_checksum_sha1: - return svn_checksum__from_digest_sha1(svn_sha1__empty_string_digest(), - pool); + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return checksum_create(kind, empty_string_digests[kind], pool); default: /* We really shouldn't get here, but if we do... */ @@ -391,6 +537,14 @@ svn_checksum_ctx_create(svn_checksum_kind_t kind, apr_sha1_init(ctx->apr_ctx); break; + case svn_checksum_fnv1a_32: + ctx->apr_ctx = svn_fnv1a_32__context_create(pool); + break; + + case svn_checksum_fnv1a_32x4: + ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool); + break; + default: SVN_ERR_MALFUNCTION_NO_RETURN(); } @@ -413,6 +567,14 @@ svn_checksum_update(svn_checksum_ctx_t *ctx, apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len); break; + case svn_checksum_fnv1a_32: + svn_fnv1a_32__update(ctx->apr_ctx, data, len); + break; + + case svn_checksum_fnv1a_32x4: + svn_fnv1a_32x4__update(ctx->apr_ctx, data, len); + break; + default: /* We really shouldn't get here, but if we do... */ return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); @@ -438,6 +600,16 @@ svn_checksum_final(svn_checksum_t **checksum, apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx); break; + case svn_checksum_fnv1a_32: + *(apr_uint32_t *)(*checksum)->digest + = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx)); + break; + + case svn_checksum_fnv1a_32x4: + *(apr_uint32_t *)(*checksum)->digest + = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx)); + break; + default: /* We really shouldn't get here, but if we do... */ return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); @@ -486,15 +658,152 @@ svn_checksum_is_empty_checksum(svn_checksum_t *checksum) switch (checksum->kind) { case svn_checksum_md5: - return svn_md5__digests_match(checksum->digest, - svn_md5__empty_string_digest()); - case svn_checksum_sha1: - return svn_sha1__digests_match(checksum->digest, - svn_sha1__empty_string_digest()); + case svn_checksum_fnv1a_32: + case svn_checksum_fnv1a_32x4: + return svn__digests_match(checksum->digest, + svn__empty_string_digest(checksum->kind), + digest_sizes[checksum->kind]); default: /* We really shouldn't get here, but if we do... */ SVN_ERR_MALFUNCTION_NO_RETURN(); } } + +/* Checksum calculating stream wrappers. + */ + +/* Baton used by write_handler and close_handler to calculate the checksum + * and return the result to the stream creator. It accommodates the data + * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as + * svn_checksum__wrap_write_stream. + */ +typedef struct stream_baton_t +{ + /* Stream we are wrapping. Forward write() and close() operations to it. */ + svn_stream_t *inner_stream; + + /* Build the checksum data in here. */ + svn_checksum_ctx_t *context; + + /* Write the final checksum here. May be NULL. */ + svn_checksum_t **checksum; + + /* Copy the digest of the final checksum. May be NULL. */ + unsigned char *digest; + + /* Allocate the resulting checksum here. */ + apr_pool_t *pool; +} stream_baton_t; + +/* Implement svn_write_fn_t. + * Update checksum and pass data on to inner stream. + */ +static svn_error_t * +write_handler(void *baton, + const char *data, + apr_size_t *len) +{ + stream_baton_t *b = baton; + + SVN_ERR(svn_checksum_update(b->context, data, *len)); + SVN_ERR(svn_stream_write(b->inner_stream, data, len)); + + return SVN_NO_ERROR; +} + +/* Implement svn_close_fn_t. + * Finalize checksum calculation and write results. Close inner stream. + */ +static svn_error_t * +close_handler(void *baton) +{ + stream_baton_t *b = baton; + svn_checksum_t *local_checksum; + + /* Ensure we can always write to *B->CHECKSUM. */ + if (!b->checksum) + b->checksum = &local_checksum; + + /* Get the final checksum. */ + SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool)); + + /* Extract digest, if wanted. */ + if (b->digest) + { + apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind); + memcpy(b->digest, (*b->checksum)->digest, digest_size); + } + + /* Done here. Now, close the underlying stream as well. */ + return svn_error_trace(svn_stream_close(b->inner_stream)); +} + +/* Common constructor function for svn_checksum__wrap_write_stream and + * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their + * respecting parameters. + * + * In the current usage, either CHECKSUM or DIGEST will be NULL but this + * function does not enforce any such restriction. Also, the caller must + * make sure that DIGEST refers to a buffer of sufficient length. + */ +static svn_stream_t * +wrap_write_stream(svn_checksum_t **checksum, + unsigned char *digest, + svn_stream_t *inner_stream, + svn_checksum_kind_t kind, + apr_pool_t *pool) +{ + svn_stream_t *outer_stream; + + stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton)); + baton->inner_stream = inner_stream; + baton->context = svn_checksum_ctx_create(kind, pool); + baton->checksum = checksum; + baton->digest = digest; + baton->pool = pool; + + outer_stream = svn_stream_create(baton, pool); + svn_stream_set_write(outer_stream, write_handler); + svn_stream_set_close(outer_stream, close_handler); + + return outer_stream; +} + +svn_stream_t * +svn_checksum__wrap_write_stream(svn_checksum_t **checksum, + svn_stream_t *inner_stream, + svn_checksum_kind_t kind, + apr_pool_t *pool) +{ + return wrap_write_stream(checksum, NULL, inner_stream, kind, pool); +} + +/* Implement svn_close_fn_t. + * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead + * of a big endian 4 byte sequence. This simply wraps close_handler adding + * the digest conversion. + */ +static svn_error_t * +close_handler_fnv1a_32x4(void *baton) +{ + stream_baton_t *b = baton; + SVN_ERR(close_handler(baton)); + + *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest); + return SVN_NO_ERROR; +} + +svn_stream_t * +svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest, + svn_stream_t *inner_stream, + apr_pool_t *pool) +{ + svn_stream_t *result + = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream, + svn_checksum_fnv1a_32x4, pool); + svn_stream_set_close(result, close_handler_fnv1a_32x4); + + return result; +} diff --git a/contrib/subversion/subversion/libsvn_subr/md5.h b/contrib/subversion/subversion/libsvn_subr/checksum.h similarity index 61% rename from contrib/subversion/subversion/libsvn_subr/md5.h rename to contrib/subversion/subversion/libsvn_subr/checksum.h index 0d83539a0..2a03cdf58 100644 --- a/contrib/subversion/subversion/libsvn_subr/md5.h +++ b/contrib/subversion/subversion/libsvn_subr/checksum.h @@ -1,5 +1,5 @@ /* - * md5.h: Converting and comparing MD5 checksums + * checksum.h: Converting and comparing checksums * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -21,12 +21,12 @@ * ==================================================================== */ -#ifndef SVN_LIBSVN_SUBR_MD5_H -#define SVN_LIBSVN_SUBR_MD5_H +#ifndef SVN_LIBSVN_SUBR_CHECKSUM_H +#define SVN_LIBSVN_SUBR_CHECKSUM_H #include -#include "svn_types.h" +#include "svn_checksum.h" #ifdef __cplusplus extern "C" { @@ -34,38 +34,41 @@ extern "C" { -/* The MD5 digest for the empty string. */ +/* The digest of the given KIND for the empty string. */ const unsigned char * -svn_md5__empty_string_digest(void); +svn__empty_string_digest(svn_checksum_kind_t kind); /* Return the hex representation of DIGEST, which must be - * APR_MD5_DIGESTSIZE bytes long, allocating the string in POOL. + * DIGEST_SIZE bytes long, allocating the string in POOL. */ const char * -svn_md5__digest_to_cstring_display(const unsigned char digest[], - apr_pool_t *pool); +svn__digest_to_cstring_display(const unsigned char digest[], + apr_size_t digest_size, + apr_pool_t *pool); /* Return the hex representation of DIGEST, which must be - * APR_MD5_DIGESTSIZE bytes long, allocating the string in POOL. + * DIGEST_SIZE bytes long, allocating the string in POOL. * If DIGEST is all zeros, then return NULL. */ const char * -svn_md5__digest_to_cstring(const unsigned char digest[], - apr_pool_t *pool); +svn__digest_to_cstring(const unsigned char digest[], + apr_size_t digest_size, + apr_pool_t *pool); -/** Compare digests D1 and D2, each APR_MD5_DIGESTSIZE bytes long. +/* Compare digests D1 and D2, each DIGEST_SIZE bytes long. * If neither is all zeros, and they do not match, then return FALSE; * else return TRUE. */ svn_boolean_t -svn_md5__digests_match(const unsigned char d1[], - const unsigned char d2[]); +svn__digests_match(const unsigned char d1[], + const unsigned char d2[], + apr_size_t digest_size); #ifdef __cplusplus } #endif /* __cplusplus */ -#endif /* SVN_LIBSVN_SUBR_MD5_H */ +#endif /* SVN_LIBSVN_SUBR_CHECKSUM_H */ diff --git a/contrib/subversion/subversion/libsvn_subr/cmdline.c b/contrib/subversion/subversion/libsvn_subr/cmdline.c index 89d1ff3a3..9e97bf71f 100644 --- a/contrib/subversion/subversion/libsvn_subr/cmdline.c +++ b/contrib/subversion/subversion/libsvn_subr/cmdline.c @@ -34,6 +34,7 @@ #else #include #include +#include #endif #include /* for STDIN_FILENO */ @@ -62,17 +63,32 @@ #include "private/svn_cmdline_private.h" #include "private/svn_utf_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_string_private.h" #include "svn_private_config.h" #include "win32_crashrpt.h" +#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400) +/* Before Visual Studio 2005, the C runtime didn't handle encodings for the + for the stdio output handling. */ +#define CMDLINE_USE_CUSTOM_ENCODING + /* The stdin encoding. If null, it's the same as the native encoding. */ static const char *input_encoding = NULL; /* The stdout encoding. If null, it's the same as the native encoding. */ static const char *output_encoding = NULL; +#elif defined(WIN32) && defined(_MSC_VER) +/* For now limit this code to Visual C++, as the result is highly dependent + on the CRT implementation */ +#define USE_WIN32_CONSOLE_SHORTCUT + +/* When TRUE, stdout/stderr is directly connected to a console */ +static svn_boolean_t shortcut_stdout_to_console = FALSE; +static svn_boolean_t shortcut_stderr_to_console = FALSE; +#endif int @@ -114,7 +130,7 @@ svn_cmdline_init(const char *progname, FILE *error_stream) #endif #ifdef WIN32 -#if _MSC_VER < 1400 +#ifdef CMDLINE_USE_CUSTOM_ENCODING /* Initialize the input and output encodings. */ { static char input_encoding_buffer[16]; @@ -128,33 +144,35 @@ svn_cmdline_init(const char *progname, FILE *error_stream) "CP%u", (unsigned) GetConsoleOutputCP()); output_encoding = output_encoding_buffer; } -#endif /* _MSC_VER < 1400 */ +#endif /* CMDLINE_USE_CUSTOM_ENCODING */ #ifdef SVN_USE_WIN32_CRASHHANDLER - /* Attach (but don't load) the crash handler */ - SetUnhandledExceptionFilter(svn__unhandled_exception_filter); + if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER")) + { + /* Attach (but don't load) the crash handler */ + SetUnhandledExceptionFilter(svn__unhandled_exception_filter); #if _MSC_VER >= 1400 - /* ### This should work for VC++ 2002 (=1300) and later */ - /* Show the abort message on STDERR instead of a dialog to allow - scripts (e.g. our testsuite) to continue after an abort without - user intervention. Allow overriding for easier debugging. */ - if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) - { - /* In release mode: Redirect abort() errors to stderr */ - _set_error_mode(_OUT_TO_STDERR); - - /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. - (Ignored in release builds) */ - _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - } + /* ### This should work for VC++ 2002 (=1300) and later */ + /* Show the abort message on STDERR instead of a dialog to allow + scripts (e.g. our testsuite) to continue after an abort without + user intervention. Allow overriding for easier debugging. */ + if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) + { + /* In release mode: Redirect abort() errors to stderr */ + _set_error_mode(_OUT_TO_STDERR); + + /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. + (Ignored in release builds) */ + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + } #endif /* _MSC_VER >= 1400 */ - + } #endif /* SVN_USE_WIN32_CRASHHANDLER */ #endif /* WIN32 */ @@ -247,6 +265,31 @@ svn_cmdline_init(const char *progname, FILE *error_stream) return EXIT_FAILURE; } +#ifdef USE_WIN32_CONSOLE_SHORTCUT + if (_isatty(STDOUT_FILENO)) + { + DWORD ignored; + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + /* stdout is a char device handle, but is it the console? */ + if (GetConsoleMode(stdout_handle, &ignored)) + shortcut_stdout_to_console = TRUE; + + /* Don't close stdout_handle */ + } + if (_isatty(STDERR_FILENO)) + { + DWORD ignored; + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + /* stderr is a char device handle, but is it the console? */ + if (GetConsoleMode(stderr_handle, &ignored)) + shortcut_stderr_to_console = TRUE; + + /* Don't close stderr_handle */ + } +#endif + return EXIT_SUCCESS; } @@ -256,10 +299,12 @@ svn_cmdline_cstring_from_utf8(const char **dest, const char *src, apr_pool_t *pool) { - if (output_encoding == NULL) - return svn_utf_cstring_from_utf8(dest, src, pool); - else +#ifdef CMDLINE_USE_CUSTOM_ENCODING + if (output_encoding != NULL) return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool); +#endif + + return svn_utf_cstring_from_utf8(dest, src, pool); } @@ -277,10 +322,12 @@ svn_cmdline_cstring_to_utf8(const char **dest, const char *src, apr_pool_t *pool) { - if (input_encoding == NULL) - return svn_utf_cstring_to_utf8(dest, src, pool); - else +#ifdef CMDLINE_USE_CUSTOM_ENCODING + if (input_encoding != NULL) return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool); +#endif + + return svn_utf_cstring_to_utf8(dest, src, pool); } @@ -336,6 +383,45 @@ svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool) svn_error_t *err; const char *out; +#ifdef USE_WIN32_CONSOLE_SHORTCUT + /* For legacy reasons the Visual C++ runtime converts output to the console + from the native 'ansi' encoding, to unicode, then back to 'ansi' and then + onwards to the console which is implemented as unicode. + + For operations like 'svn status -v' this may cause about 70% of the total + processing time, with absolutely no gain. + + For this specific scenario this shortcut exists. It has the nice side + effect of allowing full unicode output to the console. + + Note that this shortcut is not used when the output is redirected, as in + that case the data is put on the pipe/file after the first conversion to + ansi. In this case the most expensive conversion is already avoided. + */ + if ((stream == stdout && shortcut_stdout_to_console) + || (stream == stderr && shortcut_stderr_to_console)) + { + WCHAR *result; + + if (string[0] == '\0') + return SVN_NO_ERROR; + + SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */ + + SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool)); + + if (_cputws(result)) + { + if (apr_get_os_error()) + { + return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); + } + } + + return SVN_NO_ERROR; + } +#endif + err = svn_cmdline_cstring_from_utf8(&out, string, pool); if (err) @@ -393,10 +479,12 @@ svn_cmdline_fflush(FILE *stream) const char *svn_cmdline_output_encoding(apr_pool_t *pool) { +#ifdef CMDLINE_USE_CUSTOM_ENCODING if (output_encoding) return apr_pstrdup(pool, output_encoding); - else - return SVN_APR_LOCALE_CHARSET; +#endif + + return SVN_APR_LOCALE_CHARSET; } int @@ -419,31 +507,51 @@ svn_cmdline_handle_exit_error(svn_error_t *err, return EXIT_FAILURE; } +struct trust_server_cert_non_interactive_baton { + svn_boolean_t trust_server_cert_unknown_ca; + svn_boolean_t trust_server_cert_cn_mismatch; + svn_boolean_t trust_server_cert_expired; + svn_boolean_t trust_server_cert_not_yet_valid; + svn_boolean_t trust_server_cert_other_failure; +}; + /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. Don't actually prompt. Instead, set *CRED_P to valid credentials - iff FAILURES is empty or is exactly SVN_AUTH_SSL_UNKNOWNCA. If - there are any other failure bits, then set *CRED_P to null (that - is, reject the cert). + iff FAILURES is empty or may be accepted according to the flags + in BATON. If there are any other failure bits, then set *CRED_P + to null (that is, reject the cert). Ignore MAY_SAVE; we don't save certs we never prompted for. - Ignore BATON, REALM, and CERT_INFO, + Ignore REALM and CERT_INFO, Ignore any further films by George Lucas. */ static svn_error_t * -ssl_trust_unknown_server_cert - (svn_auth_cred_ssl_server_trust_t **cred_p, - void *baton, - const char *realm, - apr_uint32_t failures, - const svn_auth_ssl_server_cert_info_t *cert_info, - svn_boolean_t may_save, - apr_pool_t *pool) +trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p, + void *baton, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t + *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) { + struct trust_server_cert_non_interactive_baton *b = baton; + apr_uint32_t non_ignored_failures; *cred_p = NULL; - if (failures == 0 || failures == SVN_AUTH_SSL_UNKNOWNCA) + /* Mask away bits we are instructed to ignore. */ + non_ignored_failures = failures & ~( + (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0) + | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0) + | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0) + | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0) + | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0) + ); + + /* If no failures remain, accept the certificate. */ + if (non_ignored_failures == 0) { *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); (*cred_p)->may_save = FALSE; @@ -454,17 +562,22 @@ ssl_trust_unknown_server_cert } svn_error_t * -svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, - svn_boolean_t non_interactive, - const char *auth_username, - const char *auth_password, - const char *config_dir, - svn_boolean_t no_auth_cache, - svn_boolean_t trust_server_cert, - svn_config_t *cfg, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *pool) +svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, + svn_boolean_t non_interactive, + const char *auth_username, + const char *auth_password, + const char *config_dir, + svn_boolean_t no_auth_cache, + svn_boolean_t trust_server_cert_unknown_ca, + svn_boolean_t trust_server_cert_cn_mismatch, + svn_boolean_t trust_server_cert_expired, + svn_boolean_t trust_server_cert_not_yet_valid, + svn_boolean_t trust_server_cert_other_failure, + svn_config_t *cfg, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) + { svn_boolean_t store_password_val = TRUE; svn_boolean_t store_auth_creds_val = TRUE; @@ -505,24 +618,6 @@ svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, svn_auth_get_username_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - /* The windows ssl server certificate CRYPTOAPI provider. */ - SVN_ERR(svn_auth_get_platform_specific_provider(&provider, - "windows", - "ssl_server_trust", - pool)); - - if (provider) - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* The windows ssl authority certificate CRYPTOAPI provider. */ - SVN_ERR(svn_auth_get_platform_specific_provider(&provider, - "windows", - "ssl_server_authority", - pool)); - - if (provider) - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_ssl_server_trust_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_file_provider(&provider, pool); @@ -583,11 +678,22 @@ svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } } - else if (trust_server_cert) + else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch || + trust_server_cert_expired || trust_server_cert_not_yet_valid || + trust_server_cert_other_failure) { + struct trust_server_cert_non_interactive_baton *b; + + b = apr_palloc(pool, sizeof(*b)); + b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca; + b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch; + b->trust_server_cert_expired = trust_server_cert_expired; + b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid; + b->trust_server_cert_other_failure = trust_server_cert_other_failure; + /* Remember, only register this provider if non_interactive. */ svn_auth_get_ssl_server_trust_prompt_provider - (&provider, ssl_trust_unknown_server_cert, NULL, pool); + (&provider, trust_server_cert_non_interactive, b, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } @@ -689,12 +795,12 @@ svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, outstr, pool, svn_xml_protect_pcdata, inherited_prop ? "inherited_property" : "property", "name", propname, - "encoding", encoding, NULL); + "encoding", encoding, SVN_VA_NULL); else svn_xml_make_open_tag( outstr, pool, svn_xml_protect_pcdata, inherited_prop ? "inherited_property" : "property", - "name", propname, NULL); + "name", propname, SVN_VA_NULL); svn_stringbuf_appendcstr(*outstr, xml_safe); @@ -705,9 +811,124 @@ svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, return; } +/* Return the most similar string to NEEDLE in HAYSTACK, which contains + * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar. + */ +/* See svn_cl__similarity_check() for a more general solution. */ +static const char * +most_similar(const char *needle_cstr, + const char **haystack, + apr_size_t haystack_len, + apr_pool_t *scratch_pool) +{ + const char *max_similar; + apr_size_t max_score = 0; + apr_size_t i; + svn_membuf_t membuf; + svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool); + + svn_membuf__create(&membuf, 64, scratch_pool); + + for (i = 0; i < haystack_len; i++) + { + apr_size_t score; + svn_string_t *hay = svn_string_create(haystack[i], scratch_pool); + + score = svn_string__similarity(needle_str, hay, &membuf, NULL); + + /* If you update this factor, consider updating + * svn_cl__similarity_check(). */ + if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3 + && score > max_score) + { + max_score = score; + max_similar = haystack[i]; + } + } + + if (max_score) + return max_similar; + else + return NULL; +} + +/* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */ +static svn_error_t * +string_in_array(const char *needle, + const char **haystack, + apr_size_t haystack_len, + apr_pool_t *scratch_pool) +{ + const char *next_of_kin; + apr_size_t i; + for (i = 0; i < haystack_len; i++) + { + if (!strcmp(needle, haystack[i])) + return SVN_NO_ERROR; + } + + /* Error. */ + next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool); + if (next_of_kin) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Ignoring unknown value '%s'; " + "did you mean '%s'?"), + needle, next_of_kin); + else + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Ignoring unknown value '%s'"), + needle); +} + +#include "config_keys.inc" + +/* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are + * known. Warn to stderr if not. (An unknown value may be either a typo + * or added in a newer minor version of Subversion.) */ +static svn_error_t * +validate_config_option(svn_cmdline__config_argument_t *config_option, + apr_pool_t *scratch_pool) +{ + svn_boolean_t arbitrary_keys = FALSE; + + /* TODO: some day, we could also verify that OPTION is valid for SECTION; + i.e., forbid invalid combinations such as config:auth:diff-extensions. */ + +#define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) ) + + SVN_ERR(string_in_array(config_option->file, svn__valid_config_files, + ARRAYLEN(svn__valid_config_files), + scratch_pool)); + SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections, + ARRAYLEN(svn__valid_config_sections), + scratch_pool)); + + /* Don't validate option names for sections such as servers[group], + * config[tunnels], and config[auto-props] that permit arbitrary options. */ + { + int i; + + for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++) + { + if (!strcmp(config_option->section, svn__empty_config_sections[i])) + arbitrary_keys = TRUE; + } + } + + if (! arbitrary_keys) + SVN_ERR(string_in_array(config_option->option, svn__valid_config_options, + ARRAYLEN(svn__valid_config_options), + scratch_pool)); + +#undef ARRAYLEN + + return SVN_NO_ERROR; +} + svn_error_t * svn_cmdline__parse_config_option(apr_array_header_t *config_options, const char *opt_arg, + const char *prefix, apr_pool_t *pool) { svn_cmdline__config_argument_t *config_option; @@ -721,6 +942,8 @@ svn_cmdline__parse_config_option(apr_array_header_t *config_options, if ((equals_sign = strchr(second_colon + 1, '=')) && (equals_sign != second_colon + 1)) { + svn_error_t *warning; + config_option = apr_pcalloc(pool, sizeof(*config_option)); config_option->file = apr_pstrndup(pool, opt_arg, first_colon - opt_arg); @@ -729,6 +952,13 @@ svn_cmdline__parse_config_option(apr_array_header_t *config_options, config_option->option = apr_pstrndup(pool, second_colon + 1, equals_sign - second_colon - 1); + warning = validate_config_option(config_option, pool); + if (warning) + { + svn_handle_warning2(stderr, warning, prefix); + svn_error_clear(warning); + } + if (! (strchr(config_option->option, ':'))) { config_option->value = apr_pstrndup(pool, equals_sign + 1, @@ -920,7 +1150,7 @@ svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr, svn_xml_make_open_tag( outstr, pool, svn_xml_self_closing, inherited_props ? "inherited_property" : "property", - "name", pname, NULL); + "name", pname, SVN_VA_NULL); } else { @@ -1275,7 +1505,7 @@ svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, /* Translate back to UTF8/LF if desired. */ if (as_text) { - err = svn_subst_translate_string2(edited_contents, FALSE, FALSE, + err = svn_subst_translate_string2(edited_contents, NULL, NULL, *edited_contents, encoding, FALSE, pool, pool); if (err) @@ -1319,3 +1549,50 @@ svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */, return svn_error_trace(err); } + +svn_error_t * +svn_cmdline__parse_trust_options( + svn_boolean_t *trust_server_cert_unknown_ca, + svn_boolean_t *trust_server_cert_cn_mismatch, + svn_boolean_t *trust_server_cert_expired, + svn_boolean_t *trust_server_cert_not_yet_valid, + svn_boolean_t *trust_server_cert_other_failure, + const char *opt_arg, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *failures; + int i; + + *trust_server_cert_unknown_ca = FALSE; + *trust_server_cert_cn_mismatch = FALSE; + *trust_server_cert_expired = FALSE; + *trust_server_cert_not_yet_valid = FALSE; + *trust_server_cert_other_failure = FALSE; + + failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool); + + for (i = 0; i < failures->nelts; i++) + { + const char *value = APR_ARRAY_IDX(failures, i, const char *); + if (!strcmp(value, "unknown-ca")) + *trust_server_cert_unknown_ca = TRUE; + else if (!strcmp(value, "cn-mismatch")) + *trust_server_cert_cn_mismatch = TRUE; + else if (!strcmp(value, "expired")) + *trust_server_cert_expired = TRUE; + else if (!strcmp(value, "not-yet-valid")) + *trust_server_cert_not_yet_valid = TRUE; + else if (!strcmp(value, "other")) + *trust_server_cert_other_failure = TRUE; + else + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Unknown value '%s' for %s.\n" + "Supported values: %s"), + value, + "--trust-server-cert-failures", + "unknown-ca, cn-mismatch, expired, " + "not-yet-valid, other"); + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_subr/compat.c b/contrib/subversion/subversion/libsvn_subr/compat.c index 2089828d8..964361e82 100644 --- a/contrib/subversion/subversion/libsvn_subr/compat.c +++ b/contrib/subversion/subversion/libsvn_subr/compat.c @@ -95,23 +95,33 @@ svn_compat_log_revprops_in(apr_pool_t *pool) } void -svn_compat_log_revprops_out(const char **author, const char **date, - const char **message, apr_hash_t *revprops) +svn_compat_log_revprops_out_string(const svn_string_t **author, + const svn_string_t **date, + const svn_string_t **message, + apr_hash_t *revprops) { - svn_string_t *author_s, *date_s, *message_s; - *author = *date = *message = NULL; if (revprops) { - if ((author_s = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR))) - *author = author_s->data; - if ((date_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE))) - *date = date_s->data; - if ((message_s = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG))) - *message = message_s->data; + *author = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR); + *date = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE); + *message = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG); } } +void +svn_compat_log_revprops_out(const char **author, const char **date, + const char **message, apr_hash_t *revprops) +{ + const svn_string_t *author_s, *date_s, *message_s; + svn_compat_log_revprops_out_string(&author_s, &date_s, &message_s, + revprops); + + *author = author_s ? author_s->data : NULL; + *date = date_s ? date_s->data : NULL; + *message = message_s ? message_s->data : NULL; +} + /* Baton for use with svn_compat_wrap_log_receiver */ struct log_wrapper_baton { void *baton; diff --git a/contrib/subversion/subversion/libsvn_subr/compress.c b/contrib/subversion/subversion/libsvn_subr/compress.c new file mode 100644 index 000000000..004e44362 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/compress.c @@ -0,0 +1,257 @@ +/* + * compress.c: various data compression routines + * + * ==================================================================== + * 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 +#include +#include + +#include "private/svn_subr_private.h" +#include "private/svn_error_private.h" + +#include "svn_private_config.h" + +const char * +svn_zlib__compiled_version(void) +{ + static const char zlib_version_str[] = ZLIB_VERSION; + + return zlib_version_str; +} + +const char * +svn_zlib__runtime_version(void) +{ + return zlibVersion(); +} + + +/* 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 + +unsigned char * +svn__encode_uint(unsigned char *p, apr_uint64_t val) +{ + int n; + apr_uint64_t v; + + /* Figure out how many bytes we'll need. */ + v = val >> 7; + n = 1; + while (v > 0) + { + v = v >> 7; + n++; + } + + /* Encode the remaining bytes; n is always the number of bytes + coming after the one we're encoding. */ + while (--n >= 1) + *p++ = (unsigned char)(((val >> (n * 7)) | 0x80) & 0xff); + + *p++ = (unsigned char)(val & 0x7f); + + return p; +} + +const unsigned char * +svn__decode_uint(apr_uint64_t *val, + const unsigned char *p, + const unsigned char *end) +{ + apr_uint64_t temp = 0; + + if (p + SVN__MAX_ENCODED_UINT_LEN < end) + end = p + SVN__MAX_ENCODED_UINT_LEN; + + /* Decode bytes until we're done. */ + while (SVN__PREDICT_TRUE(p < end)) + { + unsigned int c = *p++; + + if (c < 0x80) + { + *val = (temp << 7) | c; + return p; + } + else + { + temp = (temp << 7) | (c & 0x7f); + } + } + + return NULL; +} + +/* 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; + unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p; + + svn_stringbuf_setempty(out); + p = svn__encode_uint(buf, (apr_uint64_t)len); + svn_stringbuf_appendbytes(out, (const char *)buf, p - buf); + + 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__COMPRESSION_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; +} + +/* 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; + apr_uint64_t size; + const unsigned char *oldplace = in; + + /* First thing in the string is the original length. */ + in = svn__decode_uint(&size, in, in + inLen); + len = (apr_size_t)size; + if (in == NULL || len != size) + return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL, + _("Decompression of zlib compressed data failed: no size")); + if (len > limit) + return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL, + _("Decompression of zlib compressed 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; +} + +svn_error_t * +svn__compress(svn_stringbuf_t *in, + svn_stringbuf_t *out, + int compression_method) +{ + if ( compression_method < SVN__COMPRESSION_NONE + || compression_method > SVN__COMPRESSION_ZLIB_MAX) + return svn_error_createf(SVN_ERR_BAD_COMPRESSION_METHOD, NULL, + _("Unsupported compression method %d"), + compression_method); + + return zlib_encode(in->data, in->len, out, compression_method); +} + +svn_error_t * +svn__decompress(svn_stringbuf_t *in, + svn_stringbuf_t *out, + apr_size_t limit) +{ + return zlib_decode((const unsigned char*)in->data, in->len, out, limit); +} diff --git a/contrib/subversion/subversion/libsvn_subr/config.c b/contrib/subversion/subversion/libsvn_subr/config.c index cf97f0dbe..770d699ef 100644 --- a/contrib/subversion/subversion/libsvn_subr/config.c +++ b/contrib/subversion/subversion/libsvn_subr/config.c @@ -23,6 +23,8 @@ +#include + #define APR_WANT_STRFUNC #define APR_WANT_MEMFUNC #include @@ -36,6 +38,7 @@ #include "svn_private_config.h" #include "private/svn_dep_compat.h" +#include "private/svn_subr_private.h" @@ -92,6 +95,7 @@ svn_config_create2(svn_config_t **cfgp, 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; + cfg->read_only = FALSE; *cfgp = cfg; return SVN_NO_ERROR; @@ -188,7 +192,8 @@ read_all(svn_config_t **cfgp, #ifdef WIN32 if (sys_registry_path) { - SVN_ERR(svn_config_read2(cfgp, sys_registry_path, FALSE, FALSE, pool)); + SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE, + pool)); red_config = TRUE; } #endif /* WIN32 */ @@ -214,8 +219,8 @@ read_all(svn_config_t **cfgp, SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE)); else { - SVN_ERR(svn_config_read2(cfgp, usr_registry_path, - FALSE, FALSE, pool)); + SVN_ERR(svn_config_read3(cfgp, usr_registry_path, + FALSE, FALSE, FALSE, pool)); red_config = TRUE; } } @@ -242,7 +247,7 @@ read_all(svn_config_t **cfgp, /* CONFIG_DIR provides an override for the default behavior of reading the default set of overlay files described by read_all()'s doc - string. */ + string. Returns non-NULL *CFG or an error. */ static svn_error_t * get_category_config(svn_config_t **cfg, const char *config_dir, @@ -259,9 +264,9 @@ get_category_config(svn_config_t **cfg, { #ifdef WIN32 sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH, - category, NULL); + category, SVN_VA_NULL); usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH, - category, NULL); + category, SVN_VA_NULL); #endif /* WIN32 */ err = svn_config__sys_config_path(&sys_cfg_path, category, pool); @@ -291,19 +296,29 @@ svn_config_get_config(apr_hash_t **cfg_hash, 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 + svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, cfg); -#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 + svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, cfg); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_config__get_default_config(apr_hash_t **cfg_hash, + apr_pool_t *pool) +{ + svn_config_t *empty_cfg; + *cfg_hash = apr_hash_make(pool); + + SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool)); + svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg); + + SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool)); + svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, empty_cfg); return SVN_NO_ERROR; } @@ -415,6 +430,26 @@ make_hash_key(char *key) return key; } +/* Return the value for KEY in HASH. If CASE_SENSITIVE is FALSE, + BUFFER will be used to construct the normalized hash key. */ +static void * +get_hash_value(apr_hash_t *hash, + svn_stringbuf_t *buffer, + const char *key, + svn_boolean_t case_sensitive) +{ + apr_size_t i; + apr_size_t len = strlen(key); + + if (case_sensitive) + return apr_hash_get(hash, key, len); + + svn_stringbuf_ensure(buffer, len); + for (i = 0; i < len; ++i) + buffer->data[i] = (char)apr_tolower(key[i]); + + return apr_hash_get(hash, buffer->data, len); +} /* 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. @@ -423,30 +458,16 @@ 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); + void *sec_ptr = get_hash_value(cfg->sections, cfg->tmp_key, section, + cfg->section_names_case_sensitive); 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); + cfg_option_t *opt = get_hash_value(sec->options, cfg->tmp_key, option, + cfg->option_names_case_sensitive); /* NOTE: ConfigParser's sections are case sensitive. */ if (opt == NULL && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0) @@ -482,7 +503,13 @@ make_string_from_option(const char **valuep, svn_config_t *cfg, */ if (opt->value && strchr(opt->value, '%')) { - apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool)); + apr_pool_t *tmp_pool; + + /* setting read-only mode should have expanded all values + * automatically. */ + assert(!cfg->read_only); + + 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; @@ -642,6 +669,33 @@ svn_config_create_option(cfg_option_t **opt, *opt = o; } +svn_boolean_t +svn_config__is_expanded(svn_config_t *cfg, + const char *section, + const char *option) +{ + cfg_option_t *opt; + + if (cfg == NULL) + return FALSE; + + /* does the option even exist? */ + opt = find_option(cfg, section, option, NULL); + if (opt == NULL) + return FALSE; + + /* already expanded? */ + if (opt->expanded) + return TRUE; + + /* needs expansion? */ + if (opt->value && strchr(opt->value, '%')) + return FALSE; + + /* no expansion necessary */ + return TRUE; +} + void svn_config_get(svn_config_t *cfg, const char **valuep, @@ -659,11 +713,11 @@ svn_config_get(svn_config_t *cfg, const char **valuep, } 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 there is none, 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); + apr_pool_t *tmp_pool = svn_pool_create(cfg->pool); const char *x_default; expand_option_value(cfg, sec, default_value, &x_default, tmp_pool); if (x_default) @@ -686,6 +740,17 @@ svn_config_set(svn_config_t *cfg, cfg_section_t *sec; cfg_option_t *opt; + /* Ignore write attempts to r/o configurations. + * + * Since we should never try to modify r/o data, trigger an assertion + * in debug mode. + */ +#ifdef SVN_DEBUG + SVN_ERR_ASSERT_NO_RETURN(!cfg->read_only); +#endif + if (cfg->read_only) + return; + remove_expansions(cfg); opt = find_option(cfg, section, option, &sec); @@ -930,7 +995,7 @@ svn_config_enumerate(svn_config_t *cfg, const char *section, if (sec == NULL) return 0; - subpool = svn_pool_create(cfg->x_pool); + subpool = svn_pool_create(cfg->pool); count = 0; for (opt_ndx = apr_hash_first(subpool, sec->options); opt_ndx != NULL; @@ -1059,7 +1124,7 @@ svn_config_get_server_setting(svn_config_t *cfg, svn_error_t * svn_config_dup(svn_config_t **cfgp, - svn_config_t *src, + const svn_config_t *src, apr_pool_t *pool) { apr_hash_index_t *sectidx; @@ -1197,13 +1262,6 @@ svn_config_get_server_setting_bool(svn_config_t *cfg, 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; + return NULL != get_hash_value(cfg->sections, cfg->tmp_key, section, + cfg->section_names_case_sensitive); } diff --git a/contrib/subversion/subversion/libsvn_subr/config_auth.c b/contrib/subversion/subversion/libsvn_subr/config_auth.c index ed26a58cb..90670f9d7 100644 --- a/contrib/subversion/subversion/libsvn_subr/config_auth.c +++ b/contrib/subversion/subversion/libsvn_subr/config_auth.c @@ -35,10 +35,6 @@ #include "private/svn_auth_private.h" -/* Helper for svn_config_{read|write}_auth_data. Return a path to a - file within ~/.subversion/auth/ that holds CRED_KIND credentials - within REALMSTRING. If no path is available *PATH will be set to - NULL. */ svn_error_t * svn_auth__file_path(const char **path, const char *cred_kind, @@ -124,9 +120,8 @@ svn_config_write_auth_data(apr_hash_t *hash, const char *config_dir, apr_pool_t *pool) { - apr_file_t *authfile = NULL; svn_stream_t *stream; - const char *auth_path; + const char *auth_path, *tmp_path; SVN_ERR(svn_auth__file_path(&auth_path, cred_kind, realmstring, config_dir, pool)); @@ -135,25 +130,25 @@ svn_config_write_auth_data(apr_hash_t *hash, _("Unable to locate auth file")); /* Add the realmstring to the hash, so programs (or users) can - verify exactly which set of credentials this file holds. */ + verify exactly which set of credentials this file holds. + ### What if realmstring key is already in the hash? */ svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY, svn_string_create(realmstring, pool)); - SVN_ERR_W(svn_io_file_open(&authfile, auth_path, - (APR_WRITE | APR_CREATE | APR_TRUNCATE - | APR_BUFFERED), - APR_OS_DEFAULT, pool), + SVN_ERR_W(svn_stream_open_unique(&stream, &tmp_path, + svn_dirent_dirname(auth_path, pool), + svn_io_file_del_on_pool_cleanup, + pool, pool), _("Unable to open auth file for writing")); - - stream = svn_stream_from_aprfile2(authfile, FALSE, pool); SVN_ERR_W(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool), apr_psprintf(pool, _("Error writing hash to '%s'"), svn_dirent_local_style(auth_path, pool))); - SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_io_file_rename(tmp_path, auth_path, pool)); /* To be nice, remove the realmstring from the hash again, just in - case the caller wants their hash unchanged. */ + case the caller wants their hash unchanged. + ### Should we also do this when a write error occurs? */ svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY, NULL); return SVN_NO_ERROR; @@ -213,7 +208,7 @@ svn_config_walk_auth_data(const char *config_dir, itempool = svn_pool_create(iterpool); for (hi = apr_hash_first(iterpool, nodes); hi; hi = apr_hash_next(hi)) { - svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); + svn_io_dirent2_t *dirent = apr_hash_this_val(hi); svn_stream_t *stream; apr_hash_t *creds_hash; const svn_string_t *realm; @@ -227,7 +222,7 @@ svn_config_walk_auth_data(const char *config_dir, svn_pool_clear(itempool); - item_path = svn_dirent_join(dir_path, svn__apr_hash_index_key(hi), + item_path = svn_dirent_join(dir_path, apr_hash_this_key(hi), itempool); err = svn_stream_open_readonly(&stream, item_path, diff --git a/contrib/subversion/subversion/libsvn_subr/config_file.c b/contrib/subversion/subversion/libsvn_subr/config_file.c index 9969b8eb9..eede71757 100644 --- a/contrib/subversion/subversion/libsvn_subr/config_file.c +++ b/contrib/subversion/subversion/libsvn_subr/config_file.c @@ -30,6 +30,7 @@ #include "svn_types.h" #include "svn_dirent_uri.h" #include "svn_auth.h" +#include "svn_hash.h" #include "svn_subst.h" #include "svn_utf.h" #include "svn_pools.h" @@ -37,6 +38,7 @@ #include "svn_ctype.h" #include "svn_private_config.h" +#include "private/svn_subr_private.h" #ifdef __HAIKU__ # include @@ -102,8 +104,8 @@ parser_getc(parse_context_t *ctx, int *c) ctx->buffer_pos = 0; ctx->buffer_size = sizeof(ctx->parser_buffer); - SVN_ERR(svn_stream_read(ctx->stream, ctx->parser_buffer, - &(ctx->buffer_size))); + SVN_ERR(svn_stream_read_full(ctx->stream, ctx->parser_buffer, + &(ctx->buffer_size))); if (ctx->buffer_pos < ctx->buffer_size) { @@ -183,7 +185,25 @@ skip_to_eoln(parse_context_t *ctx, int *c) SVN_ERR(parser_getc(ctx, &ch)); while (ch != '\n' && ch != EOF) - SVN_ERR(parser_getc_plain(ctx, &ch)); + { + /* This is much faster than checking individual bytes. + * We use this function a lot when skipping comment lines. + * + * This assumes that the ungetc buffer is empty, but that is a + * safe assumption right after reading a character (which would + * clear the buffer. */ + const char *newline = memchr(ctx->parser_buffer + ctx->buffer_pos, '\n', + ctx->buffer_size - ctx->buffer_pos); + if (newline) + { + ch = '\n'; + ctx->buffer_pos = newline - ctx->parser_buffer + 1; + break; + } + + /* refill buffer, check for EOF */ + SVN_ERR(parser_getc_plain(ctx, &ch)); + } *c = ch; return SVN_NO_ERROR; @@ -321,7 +341,7 @@ parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool) { ch = EOF; err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Option must end with ':' or '='", + _("line %d: Option must end with ':' or '='"), ctx->line); } else @@ -364,7 +384,7 @@ parse_section_name(int *pch, parse_context_t *ctx, { ch = EOF; err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Section header must end with ']'", + _("line %d: Section header must end with ']'"), ctx->line); } else @@ -392,9 +412,10 @@ svn_config__sys_config_path(const char **path_p, #ifdef WIN32 { const char *folder; - SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool)); + SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool, pool)); *path_p = svn_dirent_join_many(pool, folder, - SVN_CONFIG__SUBDIRECTORY, fname, NULL); + SVN_CONFIG__SUBDIRECTORY, fname, + SVN_VA_NULL); } #elif defined(__HAIKU__) @@ -407,30 +428,112 @@ svn_config__sys_config_path(const char **path_p, return SVN_NO_ERROR; *path_p = svn_dirent_join_many(pool, folder, - SVN_CONFIG__SYS_DIRECTORY, fname, NULL); + SVN_CONFIG__SYS_DIRECTORY, fname, + SVN_VA_NULL); } #else /* ! WIN32 && !__HAIKU__ */ - *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, NULL); + *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, + SVN_VA_NULL); #endif /* WIN32 */ return SVN_NO_ERROR; } +/* Callback for svn_config_enumerate2: Continue to next value. */ +static svn_boolean_t +expand_value(const char *name, + const char *value, + void *baton, + apr_pool_t *pool) +{ + return TRUE; +} + +/* Callback for svn_config_enumerate_sections2: + * Enumerate and implicitly expand all values in this section. + */ +static svn_boolean_t +expand_values_in_section(const char *name, + void *baton, + apr_pool_t *pool) +{ + svn_config_t *cfg = baton; + svn_config_enumerate2(cfg, name, expand_value, NULL, pool); + + return TRUE; +} + /*** Exported interfaces. ***/ +void +svn_config__set_read_only(svn_config_t *cfg, + apr_pool_t *scratch_pool) +{ + /* expand all items such that later calls to getters won't need to + * change internal state */ + svn_config_enumerate_sections2(cfg, expand_values_in_section, + cfg, scratch_pool); + + /* now, any modification attempt will be ignored / trigger an assertion + * in debug mode */ + cfg->read_only = TRUE; +} + +svn_boolean_t +svn_config__is_read_only(svn_config_t *cfg) +{ + return cfg->read_only; +} + +svn_config_t * +svn_config__shallow_copy(svn_config_t *src, + apr_pool_t *pool) +{ + svn_config_t *cfg = apr_palloc(pool, sizeof(*cfg)); + + cfg->sections = src->sections; + cfg->pool = pool; + + /* r/o configs are fully expanded and don't need the x_pool anymore */ + cfg->x_pool = src->read_only ? NULL : svn_pool_create(pool); + cfg->x_values = src->x_values; + cfg->tmp_key = svn_stringbuf_create_empty(pool); + cfg->tmp_value = svn_stringbuf_create_empty(pool); + cfg->section_names_case_sensitive = src->section_names_case_sensitive; + cfg->option_names_case_sensitive = src->option_names_case_sensitive; + cfg->read_only = src->read_only; + + return cfg; +} + +void +svn_config__shallow_replace_section(svn_config_t *target, + svn_config_t *source, + const char *section) +{ + if (target->read_only) + target->sections = apr_hash_copy(target->pool, target->sections); + + svn_hash_sets(target->sections, section, + svn_hash_gets(source->sections, section)); +} + svn_error_t * svn_config__parse_file(svn_config_t *cfg, const char *file, svn_boolean_t must_exist, apr_pool_t *result_pool) { svn_error_t *err = SVN_NO_ERROR; + apr_file_t *apr_file; svn_stream_t *stream; apr_pool_t *scratch_pool = svn_pool_create(result_pool); - err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool); + /* Use unbuffered IO since we use our own buffering. */ + err = svn_io_file_open(&apr_file, file, APR_READ, APR_OS_DEFAULT, + scratch_pool); if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err)) { @@ -441,13 +544,14 @@ svn_config__parse_file(svn_config_t *cfg, const char *file, else SVN_ERR(err); + stream = svn_stream_from_aprfile2(apr_file, FALSE, scratch_pool); err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool); if (err != SVN_NO_ERROR) { /* Add the filename to the error stack. */ err = svn_error_createf(err->apr_err, err, - "Error while parsing config file: %s:", + _("Error while parsing config file: %s:"), svn_dirent_local_style(file, scratch_pool)); } @@ -489,8 +593,8 @@ svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream, SVN_ERR(parse_section_name(&ch, ctx, scratch_pool)); else return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Section header" - " must start in the first column", + _("line %d: Section header" + " must start in the first column"), ctx->line); break; @@ -502,8 +606,8 @@ svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream, } else return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Comment" - " must start in the first column", + _("line %d: Comment" + " must start in the first column"), ctx->line); break; @@ -517,11 +621,11 @@ svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream, default: if (svn_stringbuf_isempty(ctx->section)) return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Section header expected", + _("line %d: Section header expected"), ctx->line); else if (count != 0) return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - "line %d: Option expected", + _("line %d: Option expected"), ctx->line); else SVN_ERR(parse_option(&ch, ctx, scratch_pool)); @@ -1174,6 +1278,12 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool) "### for 'svn add' and 'svn import', it defaults to 'no'." NL "### Automatic properties are defined in the section 'auto-props'." NL "# enable-auto-props = yes" NL +#ifdef SVN_HAVE_LIBMAGIC + "### Set enable-magic-file to 'no' to disable magic file detection" NL + "### of the file type when automatically setting svn:mime-type. It" NL + "### defaults to 'yes' if magic file support is possible." NL + "# enable-magic-file = yes" NL +#endif "### Set interactive-conflicts to 'no' to disable interactive" NL "### conflict resolution prompting. It defaults to 'yes'." NL "# interactive-conflicts = no" NL @@ -1182,6 +1292,16 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool) "### ra_local (the file:// scheme). The value represents the number" NL "### of MB used by the cache." NL "# memory-cache-size = 16" NL + "### Set diff-ignore-content-type to 'yes' to cause 'svn diff' to" NL + "### attempt to show differences of all modified files regardless" NL + "### of their MIME content type. By default, Subversion will only" NL + "### attempt to show differences for files believed to have human-" NL + "### readable (non-binary) content. This option is especially" NL + "### useful when Subversion is configured (via the 'diff-cmd'" NL + "### option) to employ an external differencing tool which is able" NL + "### to show meaningful differences for binary file formats. [New" NL + "### in 1.9]" NL + "# diff-ignore-content-type = no" NL "" NL "### Section for configuring automatic properties." NL "[auto-props]" NL @@ -1217,7 +1337,13 @@ svn_config_ensure(const char *config_dir, apr_pool_t *pool) "### copies by all clients using the 1.8 APIs. Enabling this may" NL "### cause some clients to fail to work properly. This does not have"NL "### to be set for exclusive-locking-clients to work." NL - "# exclusive-locking = false" NL; + "# exclusive-locking = false" NL + "### Set the SQLite busy timeout in milliseconds: the maximum time" NL + "### the client waits to get access to the SQLite database before" NL + "### returning an error. The default is 10000, i.e. 10 seconds." NL + "### Longer values may be useful when exclusive locking is enabled." NL + "# busy-timeout = 10000" NL + ; err = svn_io_file_open(&f, path, (APR_WRITE | APR_CREATE | APR_EXCL), @@ -1249,16 +1375,20 @@ svn_config_get_user_config_path(const char **path, if (config_dir) { - *path = svn_dirent_join_many(pool, config_dir, fname, NULL); + *path = svn_dirent_join_many(pool, config_dir, fname, SVN_VA_NULL); return SVN_NO_ERROR; } #ifdef WIN32 { const char *folder; - SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool)); + SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool, pool)); + + if (! folder) + return SVN_NO_ERROR; + *path = svn_dirent_join_many(pool, folder, - SVN_CONFIG__SUBDIRECTORY, fname, NULL); + SVN_CONFIG__SUBDIRECTORY, fname, SVN_VA_NULL); } #elif defined(__HAIKU__) @@ -1271,7 +1401,8 @@ svn_config_get_user_config_path(const char **path, return SVN_NO_ERROR; *path = svn_dirent_join_many(pool, folder, - SVN_CONFIG__USR_DIRECTORY, fname, NULL); + SVN_CONFIG__USR_DIRECTORY, fname, + SVN_VA_NULL); } #else /* ! WIN32 && !__HAIKU__ */ @@ -1281,7 +1412,7 @@ svn_config_get_user_config_path(const char **path, return SVN_NO_ERROR; *path = svn_dirent_join_many(pool, svn_dirent_canonicalize(homedir, pool), - SVN_CONFIG__USR_DIRECTORY, fname, NULL); + SVN_CONFIG__USR_DIRECTORY, fname, SVN_VA_NULL); } #endif /* WIN32 */ diff --git a/contrib/subversion/subversion/libsvn_subr/config_impl.h b/contrib/subversion/subversion/libsvn_subr/config_impl.h index a3ab8fa1a..529d7d5d1 100644 --- a/contrib/subversion/subversion/libsvn_subr/config_impl.h +++ b/contrib/subversion/subversion/libsvn_subr/config_impl.h @@ -34,7 +34,6 @@ #include "svn_string.h" #include "svn_io.h" #include "svn_config.h" -#include "svn_private_config.h" #ifdef __cplusplus extern "C" { @@ -47,7 +46,8 @@ struct svn_config_t /* Table of cfg_section_t's. */ apr_hash_t *sections; - /* Pool for hash tables, table entries and unexpanded values */ + /* Pool for hash tables, table entries and unexpanded values. + Also, parent pool for temporary pools. */ apr_pool_t *pool; /* Pool for expanded values -- this is separate, so that we can @@ -70,8 +70,11 @@ struct svn_config_t /* Specifies whether option names are populated case sensitively. */ svn_boolean_t option_names_case_sensitive; -}; + /* When set, all modification attempts will be ignored. + * In debug mode, we will trigger an assertion. */ + svn_boolean_t read_only; +}; /* Read sections and options from a file. */ svn_error_t *svn_config__parse_file(svn_config_t *cfg, @@ -92,8 +95,9 @@ svn_error_t *svn_config__parse_stream(svn_config_t *cfg, #ifdef WIN32 /* Get the common or user-specific AppData folder */ svn_error_t *svn_config__win_config_path(const char **folder, - int system_path, - apr_pool_t *pool); + svn_boolean_t system_path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Read sections and options from the Windows Registry. */ svn_error_t *svn_config__parse_registry(svn_config_t *cfg, diff --git a/contrib/subversion/subversion/libsvn_subr/config_keys.inc b/contrib/subversion/subversion/libsvn_subr/config_keys.inc new file mode 100644 index 000000000..d9f448447 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/config_keys.inc @@ -0,0 +1,77 @@ +/* Automatically generated by build/generator/gen_base.pyc:write_config_keys() */ + +static const char *svn__valid_config_files[] = { + SVN_CONFIG_CATEGORY_SERVERS, + SVN_CONFIG_CATEGORY_CONFIG +}; + +static const char *svn__valid_config_sections[] = { + SVN_CONFIG_SECTION_GROUPS, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_SECTION_AUTH, + SVN_CONFIG_SECTION_HELPERS, + SVN_CONFIG_SECTION_MISCELLANY, + SVN_CONFIG_SECTION_TUNNELS, + SVN_CONFIG_SECTION_AUTO_PROPS, + SVN_CONFIG_SECTION_WORKING_COPY +}; + +static const char *svn__valid_config_options[] = { + SVN_CONFIG_OPTION_HTTP_PROXY_HOST, + SVN_CONFIG_OPTION_HTTP_PROXY_PORT, + SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, + SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, + SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, + SVN_CONFIG_OPTION_HTTP_TIMEOUT, + SVN_CONFIG_OPTION_HTTP_COMPRESSION, + SVN_CONFIG_OPTION_NEON_DEBUG_MASK, + SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, + SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, + SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, + SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE, + SVN_CONFIG_OPTION_SSL_CLIENT_CERT_PASSWORD, + SVN_CONFIG_OPTION_SSL_PKCS11_PROVIDER, + SVN_CONFIG_OPTION_HTTP_LIBRARY, + SVN_CONFIG_OPTION_STORE_PASSWORDS, + SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, + SVN_CONFIG_OPTION_STORE_AUTH_CREDS, + SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, + SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, + SVN_CONFIG_OPTION_USERNAME, + SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, + SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, + SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, + SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, + SVN_CONFIG_OPTION_SERF_LOG_LEVEL, + SVN_CONFIG_OPTION_PASSWORD_STORES, + SVN_CONFIG_OPTION_KWALLET_WALLET, + SVN_CONFIG_OPTION_KWALLET_SVN_APPLICATION_NAME_WITH_PID, + SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT, + SVN_CONFIG_OPTION_EDITOR_CMD, + SVN_CONFIG_OPTION_DIFF_CMD, + SVN_CONFIG_OPTION_DIFF_EXTENSIONS, + SVN_CONFIG_OPTION_DIFF3_CMD, + SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG, + SVN_CONFIG_OPTION_MERGE_TOOL_CMD, + SVN_CONFIG_OPTION_GLOBAL_IGNORES, + SVN_CONFIG_OPTION_LOG_ENCODING, + SVN_CONFIG_OPTION_USE_COMMIT_TIMES, + SVN_CONFIG_OPTION_TEMPLATE_ROOT, + SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, + SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE, + SVN_CONFIG_OPTION_NO_UNLOCK, + SVN_CONFIG_OPTION_MIMETYPES_FILE, + SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, + SVN_CONFIG_OPTION_INTERACTIVE_CONFLICTS, + SVN_CONFIG_OPTION_MEMORY_CACHE_SIZE, + SVN_CONFIG_OPTION_DIFF_IGNORE_CONTENT_TYPE, + SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, + SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE_CLIENTS, + SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT +}; + +static const char *svn__empty_config_sections[] = { + SVN_CONFIG_SECTION_GROUPS, + SVN_CONFIG_SECTION_TUNNELS, + SVN_CONFIG_SECTION_AUTO_PROPS +}; diff --git a/contrib/subversion/subversion/libsvn_subr/config_win.c b/contrib/subversion/subversion/libsvn_subr/config_win.c index 0a1512916..77f7ce439 100644 --- a/contrib/subversion/subversion/libsvn_subr/config_win.c +++ b/contrib/subversion/subversion/libsvn_subr/config_win.c @@ -44,10 +44,15 @@ #include "svn_path.h" #include "svn_pools.h" #include "svn_utf.h" +#include "private/svn_utf_private.h" + +#include "config_impl.h" svn_error_t * -svn_config__win_config_path(const char **folder, int system_path, - apr_pool_t *pool) +svn_config__win_config_path(const char **folder, + svn_boolean_t system_path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { /* ### Adding CSIDL_FLAG_CREATE here, because those folders really must exist. I'm not too sure about the SHGFP_TYPE_CURRENT @@ -56,47 +61,54 @@ svn_config__win_config_path(const char **folder, int system_path, | CSIDL_FLAG_CREATE); WCHAR folder_ucs2[MAX_PATH]; - int inwords, outbytes, outlength; - char *folder_utf8; + const char *folder_utf8; - if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, - folder_ucs2)) - return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, - (system_path - ? "Can't determine the system config path" - : "Can't determine the user's config path")); + if (! system_path) + { + HKEY hkey_tmp; - /* ### When mapping from UCS-2 to UTF-8, we need at most 3 bytes - per wide char, plus extra space for the nul terminator. */ - inwords = lstrlenW(folder_ucs2); - outbytes = outlength = 3 * (inwords + 1); + /* Verify if we actually have a *per user* profile to read from */ + if (ERROR_SUCCESS == RegOpenCurrentUser(KEY_SET_VALUE, &hkey_tmp)) + RegCloseKey(hkey_tmp); /* We have a profile */ + else + { + /* The user is not properly logged in. (Most likely we are running + in a service process). In this case Windows will return a default + read only 'roaming profile' directory, which we assume to be + writable. We will then spend many seconds trying to create a + configuration and then fail, because we are not allowed to write + there, but the retry loop in io.c doesn't know that. - folder_utf8 = apr_palloc(pool, outlength); + We just answer that there is no user configuration directory. */ - outbytes = WideCharToMultiByte(CP_UTF8, 0, folder_ucs2, inwords, - folder_utf8, outbytes, NULL, NULL); + *folder = NULL; + return SVN_NO_ERROR; + } + } - if (outbytes == 0) - return svn_error_wrap_apr(apr_get_os_error(), - "Can't convert config path to UTF-8"); + if (S_OK != SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, + folder_ucs2)) + return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, + (system_path + ? _("Can't determine the system config path") + : _("Can't determine the user's config path"))); - /* Note that WideCharToMultiByte does _not_ terminate the - outgoing buffer. */ - folder_utf8[outbytes] = '\0'; - *folder = folder_utf8; + SVN_ERR(svn_utf__win32_utf16_to_utf8(&folder_utf8, folder_ucs2, + NULL, scratch_pool)); + *folder = svn_dirent_internal_style(folder_utf8, result_pool); return SVN_NO_ERROR; } -#include "config_impl.h" -/* ### These constants are insanely large, but (a) we want to avoid - reallocating strings if possible, and (b) the realloc logic might - not actually work -- you never know with Win32 ... */ +/* ### These constants are insanely large, but we want to avoid + reallocating strings if possible. */ #define SVN_REG_DEFAULT_NAME_SIZE 2048 #define SVN_REG_DEFAULT_VALUE_SIZE 8192 +/* ### This function should be converted to use the unicode functions + ### instead of the ansi functions */ static svn_error_t * parse_section(svn_config_t *cfg, HKEY hkey, const char *section, svn_stringbuf_t *option, svn_stringbuf_t *value) @@ -122,7 +134,7 @@ parse_section(svn_config_t *cfg, HKEY hkey, const char *section, } if (err != ERROR_SUCCESS) return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - "Can't enumerate registry values"); + _("Can't enumerate registry values")); /* Ignore option names that start with '#', see http://subversion.tigris.org/issues/show_bug.cgi?id=671 */ @@ -139,7 +151,7 @@ parse_section(svn_config_t *cfg, HKEY hkey, const char *section, } if (err != ERROR_SUCCESS) return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - "Can't read registry value data"); + _("Can't read registry value data")); svn_config_set(cfg, section, option->data, value->data); } @@ -176,7 +188,7 @@ svn_config__parse_registry(svn_config_t *cfg, const char *file, else { return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, - "Unrecognised registry path '%s'", + _("Unrecognised registry path '%s'"), svn_dirent_local_style(file, pool)); } @@ -185,14 +197,15 @@ svn_config__parse_registry(svn_config_t *cfg, const char *file, &hkey); if (err != ERROR_SUCCESS) { - const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err)); - if (!is_enoent) - return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, - "Can't open registry key '%s'", - svn_dirent_local_style(file, pool)); - else if (must_exist && is_enoent) - return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, - "Can't find registry key '%s'", + apr_status_t apr_err = APR_FROM_OS_ERROR(err); + svn_boolean_t is_enoent = APR_STATUS_IS_ENOENT(apr_err) + || (err == ERROR_INVALID_HANDLE); + + if (must_exist || !is_enoent) + return svn_error_createf(SVN_ERR_BAD_FILENAME, + is_enoent ? NULL + : svn_error_wrap_apr(apr_err, NULL), + _("Can't open registry key '%s'"), svn_dirent_local_style(file, pool)); else return SVN_NO_ERROR; @@ -230,7 +243,7 @@ svn_config__parse_registry(svn_config_t *cfg, const char *file, if (err != ERROR_SUCCESS) { svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - "Can't enumerate registry keys"); + _("Can't enumerate registry keys")); goto cleanup; } @@ -240,7 +253,7 @@ svn_config__parse_registry(svn_config_t *cfg, const char *file, if (err != ERROR_SUCCESS) { svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - "Can't open existing subkey"); + _("Can't open existing subkey")); goto cleanup; } diff --git a/contrib/subversion/subversion/libsvn_subr/ctype.c b/contrib/subversion/subversion/libsvn_subr/ctype.c index 0dd5d5b15..1d4c30b7f 100644 --- a/contrib/subversion/subversion/libsvn_subr/ctype.c +++ b/contrib/subversion/subversion/libsvn_subr/ctype.c @@ -25,6 +25,23 @@ #include "svn_ctype.h" +#ifndef WIN32 +static +#else +/* This variable is exported as 'CONSTANT' in our .def file for libsvn_subr, + with the name svn_ctype_table. + + This long deprecated construct will export *a pointer to* the + variable exported. + + See http://support.microsoft.com/kb/90530/en-us for the ugly details on + this system that was already deprecated when we started Subversion and + on why we should have used __declspec(dllexport) when initially exporting + this variable. (It would allow avoiding the pointer transformation). + + But to keep backwards compatibility this symbol will have to stay public + on Windows until Subversion 2.0. */ +#endif const apr_uint32_t svn_ctype_table_internal[256] = { /* **** DO NOT EDIT! **** diff --git a/contrib/subversion/subversion/libsvn_subr/debug.c b/contrib/subversion/subversion/libsvn_subr/debug.c index be331ed77..4681fa465 100644 --- a/contrib/subversion/subversion/libsvn_subr/debug.c +++ b/contrib/subversion/subversion/libsvn_subr/debug.c @@ -80,7 +80,7 @@ static void debug_vprintf(const char *fmt, va_list ap) { FILE *output = debug_output; - char prefix[80], buffer[1000]; + char prefix[80], buffer[4096]; char *s = buffer; int n; @@ -145,8 +145,8 @@ svn_dbg__print_props(apr_hash_t *props, for (hi = apr_hash_first(apr_hash_pool_get(props), props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - svn_string_t *val = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_string_t *val = apr_hash_this_val(hi); svn_dbg__printf(" '%s' -> '%s'\n", name, val->data); } diff --git a/contrib/subversion/subversion/libsvn_subr/deprecated.c b/contrib/subversion/subversion/libsvn_subr/deprecated.c index 93bd89db5..a0bf813ea 100644 --- a/contrib/subversion/subversion/libsvn_subr/deprecated.c +++ b/contrib/subversion/subversion/libsvn_subr/deprecated.c @@ -28,6 +28,8 @@ #include +#include + /* We define this here to remove any further warnings about the usage of deprecated functions in this file. */ #define SVN_DEPRECATED @@ -43,11 +45,12 @@ #include "svn_mergeinfo.h" #include "svn_utf.h" #include "svn_xml.h" +#include "svn_auth.h" #include "opt.h" +#include "auth.h" #include "private/svn_opt_private.h" #include "private/svn_mergeinfo_private.h" -#include "private/svn_subr_private.h" #include "svn_private_config.h" @@ -1067,6 +1070,119 @@ svn_stream_contents_same(svn_boolean_t *same, pool)); } +void +svn_stream_set_read(svn_stream_t *stream, + svn_read_fn_t read_fn) +{ + svn_stream_set_read2(stream, NULL /* only full read support */, + read_fn); +} + +svn_error_t * +svn_stream_read(svn_stream_t *stream, + char *buffer, + apr_size_t *len) +{ + return svn_error_trace(svn_stream_read_full(stream, buffer, len)); +} + +struct md5_stream_baton +{ + const unsigned char **read_digest; + const unsigned char **write_digest; + svn_checksum_t *read_checksum; + svn_checksum_t *write_checksum; + svn_stream_t *proxy; + apr_pool_t *pool; +}; + +static svn_error_t * +read_handler_md5(void *baton, char *buffer, apr_size_t *len) +{ + struct md5_stream_baton *btn = baton; + return svn_error_trace(svn_stream_read2(btn->proxy, buffer, len)); +} + +static svn_error_t * +read_full_handler_md5(void *baton, char *buffer, apr_size_t *len) +{ + struct md5_stream_baton *btn = baton; + return svn_error_trace(svn_stream_read_full(btn->proxy, buffer, len)); +} + +static svn_error_t * +skip_handler_md5(void *baton, apr_size_t len) +{ + struct md5_stream_baton *btn = baton; + return svn_error_trace(svn_stream_skip(btn->proxy, len)); +} + +static svn_error_t * +write_handler_md5(void *baton, const char *buffer, apr_size_t *len) +{ + struct md5_stream_baton *btn = baton; + return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); +} + +static svn_error_t * +close_handler_md5(void *baton) +{ + struct md5_stream_baton *btn = baton; + + SVN_ERR(svn_stream_close(btn->proxy)); + + if (btn->read_digest) + *btn->read_digest + = apr_pmemdup(btn->pool, btn->read_checksum->digest, + APR_MD5_DIGESTSIZE); + + if (btn->write_digest) + *btn->write_digest + = apr_pmemdup(btn->pool, btn->write_checksum->digest, + APR_MD5_DIGESTSIZE); + + return SVN_NO_ERROR; +} + + +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) +{ + svn_stream_t *s; + struct md5_stream_baton *baton; + + if (! read_digest && ! write_digest) + return stream; + + baton = apr_palloc(pool, sizeof(*baton)); + baton->read_digest = read_digest; + baton->write_digest = write_digest; + baton->pool = pool; + + /* Set BATON->proxy to a stream that will fill in BATON->read_checksum + * and BATON->write_checksum (if we want them) when it is closed. */ + baton->proxy + = svn_stream_checksummed2(stream, + read_digest ? &baton->read_checksum : NULL, + write_digest ? &baton->write_checksum : NULL, + svn_checksum_md5, + read_all, pool); + + /* Create a stream that will forward its read/write/close operations to + * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we + * want them) after it closes BATON->proxy. */ + s = svn_stream_create(baton, pool); + svn_stream_set_read2(s, read_handler_md5, read_full_handler_md5); + svn_stream_set_skip(s, skip_handler_md5); + svn_stream_set_write(s, write_handler_md5); + svn_stream_set_close(s, close_handler_md5); + return s; +} + /*** From path.c ***/ const char * @@ -1253,12 +1369,39 @@ svn_xml_make_header(svn_stringbuf_t **str, apr_pool_t *pool) svn_xml_make_header2(str, NULL, pool); } + +/*** From utf.c ***/ void svn_utf_initialize(apr_pool_t *pool) { svn_utf_initialize2(FALSE, pool); } +svn_error_t * +svn_utf_cstring_from_utf8_ex(const char **dest, + const char *src, + const char *topage, + const char *convset_key, + apr_pool_t *pool) +{ + return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool); +} + +/*** From error.c ***/ +void +svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal) +{ + svn_handle_error2(err, stream, fatal, "svn: "); +} + +void +svn_handle_warning(FILE *stream, svn_error_t *err) +{ + svn_handle_warning2(stream, err, "svn: "); +} + + +/*** From subst.c ***/ svn_error_t * svn_subst_build_keywords(svn_subst_keywords_t *kw, const char *keywords_val, @@ -1309,3 +1452,89 @@ svn_ver_check_list(const svn_version_t *my_version, { return svn_ver_check_list2(my_version, checklist, svn_ver_compatible); } + +/*** From win32_crypto.c ***/ +#if defined(WIN32) && !defined(__MINGW32__) +void +svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth__get_windows_simple_provider(provider, pool); +} + +void +svn_auth_get_windows_ssl_client_cert_pw_provider + (svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth__get_windows_ssl_client_cert_pw_provider(provider, pool); +} + +void +svn_auth_get_windows_ssl_server_trust_provider + (svn_auth_provider_object_t **provider, apr_pool_t *pool) +{ + svn_auth__get_windows_ssl_server_trust_provider(provider, pool); +} +#endif /* WIN32 && !__MINGW32__ */ + +/*** From macos_keychain.c ***/ +#if defined(DARWIN) +void +svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth__get_keychain_simple_provider(provider, pool); +} + +void +svn_auth_get_keychain_ssl_client_cert_pw_provider + (svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ + svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool); +} +#endif /* DARWIN */ + +#if !defined(WIN32) +void +svn_auth_get_gpg_agent_simple_provider(svn_auth_provider_object_t **provider, + apr_pool_t *pool) +{ +#ifdef SVN_HAVE_GPG_AGENT + svn_auth__get_gpg_agent_simple_provider(provider, pool); +#else + svn_auth__get_dummmy_simple_provider(provider, pool); +#endif /* SVN_HAVE_GPG_AGENT */ +} +#endif /* !WIN32 */ + +svn_error_t * +svn_cmdline_create_auth_baton(svn_auth_baton_t **ab, + svn_boolean_t non_interactive, + const char *auth_username, + const char *auth_password, + const char *config_dir, + svn_boolean_t no_auth_cache, + svn_boolean_t trust_server_cert, + svn_config_t *cfg, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + return svn_error_trace(svn_cmdline_create_auth_baton2(ab, + non_interactive, + auth_username, + auth_password, + config_dir, + no_auth_cache, + trust_server_cert, + FALSE, + FALSE, + FALSE, + FALSE, + cfg, + cancel_func, + cancel_baton, + pool)); +} diff --git a/contrib/subversion/subversion/libsvn_subr/dirent_uri.c b/contrib/subversion/subversion/libsvn_subr/dirent_uri.c index 6886a3e75..a009145eb 100644 --- a/contrib/subversion/subversion/libsvn_subr/dirent_uri.c +++ b/contrib/subversion/subversion/libsvn_subr/dirent_uri.c @@ -1294,6 +1294,29 @@ svn_relpath_split(const char **dirpath, *base_name = svn_relpath_basename(relpath, pool); } +const char * +svn_relpath_prefix(const char *relpath, + int max_components, + apr_pool_t *result_pool) +{ + const char *end; + assert(relpath_is_canonical(relpath)); + + if (max_components <= 0) + return ""; + + for (end = relpath; *end; end++) + { + if (*end == '/') + { + if (!--max_components) + break; + } + } + + return apr_pstrmemdup(result_pool, relpath, end-relpath); +} + char * svn_uri_dirname(const char *uri, apr_pool_t *pool) { @@ -1689,7 +1712,9 @@ svn_dirent_is_canonical(const char *dirent, apr_pool_t *scratch_pool) static svn_boolean_t relpath_is_canonical(const char *relpath) { - const char *ptr = relpath, *seg = relpath; + const char *dot_pos, *ptr = relpath; + apr_size_t i, len; + unsigned pattern = 0; /* RELPATH is canonical if it has: * - no '.' segments @@ -1697,35 +1722,38 @@ relpath_is_canonical(const char *relpath) * - no '//' */ - if (*relpath == '\0') - return TRUE; - + /* invalid beginnings */ if (*ptr == '/') return FALSE; - /* Now validate the rest of the path. */ - while(1) - { - apr_size_t seglen = ptr - seg; - - if (seglen == 1 && *seg == '.') - return FALSE; /* /./ */ - - if (*ptr == '/' && *(ptr+1) == '/') - return FALSE; /* // */ + if (ptr[0] == '.' && (ptr[1] == '/' || ptr[1] == '\0')) + return FALSE; - if (! *ptr && *(ptr - 1) == '/') - return FALSE; /* foo/ */ + /* valid special cases */ + len = strlen(ptr); + if (len < 2) + return TRUE; - if (! *ptr) - break; + /* invalid endings */ + if (ptr[len-1] == '/' || (ptr[len-1] == '.' && ptr[len-2] == '/')) + return FALSE; - if (*ptr == '/') - ptr++; - seg = ptr; + /* '.' are rare. So, search for them globally. There will often be no + * more than one hit. Also note that we already checked for invalid + * starts and endings, i.e. we only need to check for "/./" + */ + for (dot_pos = memchr(ptr, '.', len); + dot_pos; + dot_pos = strchr(dot_pos+1, '.')) + if (dot_pos > ptr && dot_pos[-1] == '/' && dot_pos[1] == '/') + return FALSE; - while (*ptr && (*ptr != '/')) - ptr++; + /* Now validate the rest of the path. */ + for (i = 0; i < len - 1; ++i) + { + pattern = ((pattern & 0xff) << 8) + (unsigned char)ptr[i]; + if (pattern == 0x101 * (unsigned char)('/')) + return FALSE; } return TRUE; @@ -2315,7 +2343,7 @@ svn_uri_get_dirent_from_file_url(const char **dirent, "prefix"), url); /* Find the HOSTNAME portion and the PATH portion of the URL. The host - name is between the "file://" prefix and the next occurence of '/'. We + name is between the "file://" prefix and the next occurrence of '/'. We are considering everything from that '/' until the end of the URL to be the absolute path portion of the URL. If we got just "file://", treat it the same as "file:///". */ @@ -2394,7 +2422,7 @@ svn_uri_get_dirent_from_file_url(const char **dirent, "no path"), url); /* We still know that the path starts with a slash. */ - *dirent = apr_pstrcat(pool, "//", hostname, dup_path, NULL); + *dirent = apr_pstrcat(pool, "//", hostname, dup_path, SVN_VA_NULL); } else *dirent = dup_path; @@ -2427,18 +2455,18 @@ svn_uri_get_file_url_from_dirent(const char **url, if (dirent[0] == '/' && dirent[1] == '\0') dirent = NULL; /* "file://" is the canonical form of "file:///" */ - *url = apr_pstrcat(pool, "file://", dirent, (char *)NULL); + *url = apr_pstrcat(pool, "file://", dirent, SVN_VA_NULL); #else if (dirent[0] == '/') { /* Handle UNC paths //server/share -> file://server/share */ assert(dirent[1] == '/'); /* Expect UNC, not non-absolute */ - *url = apr_pstrcat(pool, "file:", dirent, NULL); + *url = apr_pstrcat(pool, "file:", dirent, SVN_VA_NULL); } else { - char *uri = apr_pstrcat(pool, "file:///", dirent, NULL); + char *uri = apr_pstrcat(pool, "file:///", dirent, SVN_VA_NULL); apr_size_t len = 8 /* strlen("file:///") */ + strlen(dirent); /* "C:/" is a canonical dirent on Windows, @@ -2472,7 +2500,7 @@ svn_fspath__canonicalize(const char *fspath, return "/"; return apr_pstrcat(pool, "/", svn_relpath_canonicalize(fspath, pool), - (char *)NULL); + SVN_VA_NULL); } @@ -2505,7 +2533,7 @@ svn_fspath__dirname(const char *fspath, return apr_pstrdup(pool, fspath); else return apr_pstrcat(pool, "/", svn_relpath_dirname(fspath + 1, pool), - (char *)NULL); + SVN_VA_NULL); } @@ -2549,9 +2577,9 @@ svn_fspath__join(const char *fspath, if (relpath[0] == '\0') result = apr_pstrdup(result_pool, fspath); else if (fspath[1] == '\0') - result = apr_pstrcat(result_pool, "/", relpath, (char *)NULL); + result = apr_pstrcat(result_pool, "/", relpath, SVN_VA_NULL); else - result = apr_pstrcat(result_pool, fspath, "/", relpath, (char *)NULL); + result = apr_pstrcat(result_pool, fspath, "/", relpath, SVN_VA_NULL); assert(svn_fspath__is_canonical(result)); return result; @@ -2570,7 +2598,7 @@ svn_fspath__get_longest_ancestor(const char *fspath1, svn_relpath_get_longest_ancestor(fspath1 + 1, fspath2 + 1, result_pool), - (char *)NULL); + SVN_VA_NULL); assert(svn_fspath__is_canonical(result)); return result; diff --git a/contrib/subversion/subversion/libsvn_subr/dso.c b/contrib/subversion/subversion/libsvn_subr/dso.c index 7cce2fd34..46af5499f 100644 --- a/contrib/subversion/subversion/libsvn_subr/dso.c +++ b/contrib/subversion/subversion/libsvn_subr/dso.c @@ -29,6 +29,7 @@ #include "private/svn_mutex.h" #include "private/svn_atomic.h" +#include "private/svn_subr_private.h" /* A mutex to protect our global pool and cache. */ static svn_mutex__t *dso_mutex = NULL; @@ -123,4 +124,11 @@ svn_dso_load(apr_dso_handle_t **dso, const char *fname) return SVN_NO_ERROR; } + +apr_pool_t * +svn_dso__pool(void) +{ + return dso_pool; +} + #endif /* APR_HAS_DSO */ diff --git a/contrib/subversion/subversion/libsvn_subr/eol.c b/contrib/subversion/subversion/libsvn_subr/eol.c index 88a6a376b..e63cf1113 100644 --- a/contrib/subversion/subversion/libsvn_subr/eol.c +++ b/contrib/subversion/subversion/libsvn_subr/eol.c @@ -30,25 +30,10 @@ #include "private/svn_eol_private.h" #include "private/svn_dep_compat.h" -/* Machine-word-sized masks used in svn_eol__find_eol_start. - */ char * svn_eol__find_eol_start(char *buf, apr_size_t len) { -#if !SVN_UNALIGNED_ACCESS_IS_OK - - /* On some systems, we need to make sure that buf is properly aligned - * for chunky data access. This overhead is still justified because - * only lines tend to be tens of chars long. - */ - for (; (len > 0) && ((apr_uintptr_t)buf) & (sizeof(apr_uintptr_t)-1) - ; ++buf, --len) - { - if (*buf == '\n' || *buf == '\r') - return buf; - } - -#endif +#if SVN_UNALIGNED_ACCESS_IS_OK /* Scan the input one machine word at a time. */ for (; len > sizeof(apr_uintptr_t) @@ -62,8 +47,8 @@ svn_eol__find_eol_start(char *buf, apr_size_t len) apr_uintptr_t r_test = chunk ^ SVN__R_MASK; apr_uintptr_t n_test = chunk ^ SVN__N_MASK; - /* A byte in SVN__R_TEST can by < 0x80, iff it has been \0 before - * (i.e. \r in *BUF). Dito for SVN__N_TEST. */ + /* A byte in SVN__R_TEST can only be < 0x80, iff it has been \0 before + * (i.e. \r in *BUF). Ditto for SVN__N_TEST. */ r_test |= (r_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; n_test |= (n_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; @@ -73,6 +58,8 @@ svn_eol__find_eol_start(char *buf, apr_size_t len) break; } +#endif + /* The remaining odd bytes will be examined the naive way: */ for (; len > 0; ++buf, --len) { diff --git a/contrib/subversion/subversion/libsvn_subr/error.c b/contrib/subversion/subversion/libsvn_subr/error.c index 48ac90627..ffb2495f4 100644 --- a/contrib/subversion/subversion/libsvn_subr/error.c +++ b/contrib/subversion/subversion/libsvn_subr/error.c @@ -67,6 +67,7 @@ static const char SVN_FILE_LINE_UNDEFINED[] = "svn:"; #undef svn_error_create #undef svn_error_createf #undef svn_error_quick_wrap +#undef svn_error_quick_wrapf #undef svn_error_wrap_apr /* Note: Although this is a "__" function, it was historically in the @@ -85,17 +86,15 @@ svn_error__locate(const char *file, long line) /* 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 */ + SVN_UNUSED(err); if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK")) abort(); return APR_SUCCESS; } -#endif static svn_error_t * @@ -110,7 +109,8 @@ make_error_internal(apr_status_t apr_err, pool = child->pool; else { - if (apr_pool_create(&pool, NULL)) + pool = svn_pool_create(NULL); + if (!pool) abort(); } @@ -201,7 +201,8 @@ svn_error_wrap_apr(apr_status_t status, va_end(ap); if (msg_apr) { - err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL); + err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, + SVN_VA_NULL); } else { @@ -224,6 +225,26 @@ svn_error_quick_wrap(svn_error_t *child, const char *new_msg) new_msg); } +svn_error_t * +svn_error_quick_wrapf(svn_error_t *child, + const char *fmt, + ...) +{ + svn_error_t *err; + va_list ap; + + if (child == SVN_NO_ERROR) + return SVN_NO_ERROR; + + err = make_error_internal(child->apr_err, child); + + va_start(ap, fmt); + err->message = apr_pvsprintf(err->pool, fmt, ap); + va_end(ap); + + return err; +} + /* Messages in tracing errors all point to this static string. */ static const char error_tracing_link[] = "traced call"; @@ -259,8 +280,7 @@ svn_error_compose_create(svn_error_t *err1, if (err1 && err2) { svn_error_compose(err1, - svn_error_quick_wrap(err2, - _("Additional errors:"))); + svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL)); return err1; } return err1 ? err1 : err2; @@ -314,7 +334,10 @@ svn_error_root_cause(svn_error_t *err) { while (err) { - if (err->child) + /* I don't think we can change the behavior here, but the additional + error chain doesn't define the root cause. Perhaps we should rev + this function. */ + if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/) err = err->child; else break; @@ -336,12 +359,16 @@ svn_error_find_cause(svn_error_t *err, apr_status_t apr_err) } svn_error_t * -svn_error_dup(svn_error_t *err) +svn_error_dup(const svn_error_t *err) { apr_pool_t *pool; svn_error_t *new_err = NULL, *tmp_err = NULL; - if (apr_pool_create(&pool, NULL)) + if (!err) + return SVN_NO_ERROR; + + pool = svn_pool_create(NULL); + if (!pool) abort(); for (; err; err = err->child) @@ -388,7 +415,7 @@ svn_error_clear(svn_error_t *err) } svn_boolean_t -svn_error__is_tracing_link(svn_error_t *err) +svn_error__is_tracing_link(const svn_error_t *err) { #ifdef SVN_ERR__TRACING /* ### A strcmp()? Really? I think it's the best we can do unless @@ -423,10 +450,8 @@ svn_error_purge_tracing(svn_error_t *err) 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), + svn_error__malfunction(TRUE, __FILE__, __LINE__, + NULL /* ### say something? */), NULL); /* Copy the current error except for its child error pointer @@ -534,12 +559,6 @@ print_error(svn_error_t *err, FILE *stream, const char *prefix) } } -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, @@ -559,11 +578,7 @@ svn_handle_error2(svn_error_t *err, 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); + subpool = svn_pool_create(err->pool); empties = apr_array_make(subpool, 0, sizeof(apr_status_t)); tmp_err = err; @@ -611,17 +626,20 @@ svn_handle_error2(svn_error_t *err, } } - -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) +svn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix) { char buf[256]; +#ifdef SVN_DEBUG + const char *symbolic_name = svn_error_symbolic_name(err->apr_err); +#endif + +#ifdef SVN_DEBUG + if (symbolic_name) + svn_error_clear( + svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n", + prefix, symbolic_name)); +#endif svn_error_clear(svn_cmdline_fprintf (stream, err->pool, @@ -632,7 +650,7 @@ svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix) } const char * -svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize) +svn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize) { /* Skip over any trace records. */ while (svn_error__is_tracing_link(err)) @@ -672,19 +690,43 @@ svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize) return apr_strerror(statcode, buf, bufsize); } +#ifdef SVN_DEBUG +/* Defines svn__errno and svn__apr_errno */ +#include "errorcode.inc" +#endif + const char * svn_error_symbolic_name(apr_status_t statcode) { const err_defn *defn; +#ifdef SVN_DEBUG + int i; +#endif /* SVN_DEBUG */ 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) + if (statcode == APR_SUCCESS) return "SVN_NO_ERROR"; +#ifdef SVN_DEBUG + /* Try errno.h symbols. */ + /* Linear search through a sorted array */ + for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++) + if (svn__errno[i].errcode == (int)statcode) + return svn__errno[i].errname; + + /* Try APR errors. */ + /* Linear search through a sorted array */ + for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++) + if (svn__apr_errno[i].errcode == (int)statcode) + return svn__apr_errno[i].errname; +#endif /* SVN_DEBUG */ + + /* ### TODO: do we need APR_* error macros? What about APR_TO_OS_ERROR()? */ + return NULL; } @@ -741,6 +783,12 @@ svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func) return old_malfunction_handler; } +svn_error_malfunction_handler_t +svn_error_get_malfunction_handler(void) +{ + return 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 * diff --git a/contrib/subversion/subversion/libsvn_subr/errorcode.inc b/contrib/subversion/subversion/libsvn_subr/errorcode.inc new file mode 100644 index 000000000..a02cd4ed0 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/errorcode.inc @@ -0,0 +1,271 @@ +/* This file was generated by build/generator/gen_base.py */ + +static struct { + int errcode; + const char *errname; +} svn__errno[] = { + { 1, "EPERM" }, + { 2, "ENOENT" }, + { 3, "ESRCH" }, + { 4, "EINTR" }, + { 5, "EIO" }, + { 6, "ENXIO" }, + { 7, "E2BIG" }, + { 8, "ENOEXEC" }, + { 9, "EBADF" }, + { 10, "ECHILD" }, + { 11, "EAGAIN" }, + { 12, "ENOMEM" }, + { 13, "EACCES" }, + { 14, "EFAULT" }, + { 15, "ENOTBLK" }, + { 16, "EBUSY" }, + { 17, "EEXIST" }, + { 18, "EXDEV" }, + { 19, "ENODEV" }, + { 20, "ENOTDIR" }, + { 21, "EISDIR" }, + { 22, "EINVAL" }, + { 23, "ENFILE" }, + { 24, "EMFILE" }, + { 25, "ENOTTY" }, + { 26, "ETXTBSY" }, + { 27, "EFBIG" }, + { 28, "ENOSPC" }, + { 29, "ESPIPE" }, + { 30, "EROFS" }, + { 31, "EMLINK" }, + { 32, "EPIPE" }, + { 33, "EDOM" }, + { 34, "ERANGE" }, + { 35, "EDEADLOCK" }, + { 36, "ENAMETOOLONG" }, + { 37, "ENOLCK" }, + { 38, "ENOSYS" }, + { 39, "ENOTEMPTY" }, + { 40, "ELOOP" }, + { 42, "ENOMSG" }, + { 43, "EIDRM" }, + { 44, "ECHRNG" }, + { 45, "EL2NSYNC" }, + { 46, "EL3HLT" }, + { 47, "EL3RST" }, + { 48, "ELNRNG" }, + { 49, "EUNATCH" }, + { 50, "ENOCSI" }, + { 51, "EL2HLT" }, + { 52, "EBADE" }, + { 53, "EBADR" }, + { 54, "EXFULL" }, + { 55, "ENOANO" }, + { 56, "EBADRQC" }, + { 57, "EBADSLT" }, + { 59, "EBFONT" }, + { 60, "ENOSTR" }, + { 61, "ENODATA" }, + { 62, "ETIME" }, + { 63, "ENOSR" }, + { 64, "ENONET" }, + { 65, "ENOPKG" }, + { 66, "EREMOTE" }, + { 67, "ENOLINK" }, + { 68, "EADV" }, + { 69, "ESRMNT" }, + { 70, "ECOMM" }, + { 71, "EPROTO" }, + { 72, "EMULTIHOP" }, + { 73, "EDOTDOT" }, + { 74, "EBADMSG" }, + { 75, "EOVERFLOW" }, + { 76, "ENOTUNIQ" }, + { 77, "EBADFD" }, + { 78, "EREMCHG" }, + { 79, "ELIBACC" }, + { 80, "ELIBBAD" }, + { 81, "ELIBSCN" }, + { 82, "ELIBMAX" }, + { 83, "ELIBEXEC" }, + { 84, "EILSEQ" }, + { 85, "ERESTART" }, + { 86, "ESTRPIPE" }, + { 87, "EUSERS" }, + { 88, "ENOTSOCK" }, + { 89, "EDESTADDRREQ" }, + { 90, "EMSGSIZE" }, + { 91, "EPROTOTYPE" }, + { 92, "ENOPROTOOPT" }, + { 93, "EPROTONOSUPPORT" }, + { 94, "ESOCKTNOSUPPORT" }, + { 95, "ENOTSUP" }, + { 96, "EPFNOSUPPORT" }, + { 97, "EAFNOSUPPORT" }, + { 98, "EADDRINUSE" }, + { 99, "EADDRNOTAVAIL" }, + { 100, "ENETDOWN" }, + { 101, "ENETUNREACH" }, + { 102, "ENETRESET" }, + { 103, "ECONNABORTED" }, + { 104, "ECONNRESET" }, + { 105, "ENOBUFS" }, + { 106, "EISCONN" }, + { 107, "ENOTCONN" }, + { 108, "ESHUTDOWN" }, + { 109, "ETOOMANYREFS" }, + { 110, "ETIMEDOUT" }, + { 111, "ECONNREFUSED" }, + { 112, "EHOSTDOWN" }, + { 113, "EHOSTUNREACH" }, + { 114, "EALREADY" }, + { 115, "EINPROGRESS" }, + { 116, "ESTALE" }, + { 117, "EUCLEAN" }, + { 118, "ENOTNAM" }, + { 119, "ENAVAIL" }, + { 120, "EISNAM" }, + { 121, "EREMOTEIO" }, + { 122, "EDQUOT" } +}; + +static struct { + int errcode; + const char *errname; +} svn__apr_errno[] = { + { 0, "APR_SUCCESS" }, + { 10000, "SOCBASEERR" }, + { 10001, "SOCEPERM" }, + { 10003, "SOCESRCH" }, + { 10004, "SOCEINTR" }, + { 10006, "SOCENXIO" }, + { 10009, "SOCEBADF" }, + { 10013, "SOCEACCES" }, + { 10014, "SOCEFAULT" }, + { 10022, "SOCEINVAL" }, + { 10024, "SOCEMFILE" }, + { 10032, "SOCEPIPE" }, + { 10035, "SOCEWOULDBLOCK" }, + { 10036, "SOCEINPROGRESS" }, + { 10037, "SOCEALREADY" }, + { 10038, "SOCENOTSOCK" }, + { 10039, "SOCEDESTADDRREQ" }, + { 10040, "SOCEMSGSIZE" }, + { 10041, "SOCEPROTOTYPE" }, + { 10042, "SOCENOPROTOOPT" }, + { 10043, "SOCEPROTONOSUPPORT" }, + { 10044, "SOCESOCKTNOSUPPORT" }, + { 10045, "SOCEOPNOTSUPP" }, + { 10046, "SOCEPFNOSUPPORT" }, + { 10047, "SOCEAFNOSUPPORT" }, + { 10048, "SOCEADDRINUSE" }, + { 10049, "SOCEADDRNOTAVAIL" }, + { 10050, "SOCENETDOWN" }, + { 10051, "SOCENETUNREACH" }, + { 10052, "SOCENETRESET" }, + { 10053, "SOCECONNABORTED" }, + { 10054, "SOCECONNRESET" }, + { 10055, "SOCENOBUFS" }, + { 10056, "SOCEISCONN" }, + { 10057, "SOCENOTCONN" }, + { 10058, "SOCESHUTDOWN" }, + { 10059, "SOCETOOMANYREFS" }, + { 10060, "SOCETIMEDOUT" }, + { 10061, "SOCECONNREFUSED" }, + { 10062, "SOCELOOP" }, + { 10063, "SOCENAMETOOLONG" }, + { 10064, "SOCEHOSTDOWN" }, + { 10065, "SOCEHOSTUNREACH" }, + { 10066, "SOCENOTEMPTY" }, + { 20000, "APR_OS_START_ERROR" }, + { 20000, "APR_UTIL_ERRSPACE_SIZE" }, + { 20001, "APR_ENOSTAT" }, + { 20002, "APR_ENOPOOL" }, + { 20004, "APR_EBADDATE" }, + { 20005, "APR_EINVALSOCK" }, + { 20006, "APR_ENOPROC" }, + { 20007, "APR_ENOTIME" }, + { 20008, "APR_ENODIR" }, + { 20009, "APR_ENOLOCK" }, + { 20010, "APR_ENOPOLL" }, + { 20011, "APR_ENOSOCKET" }, + { 20012, "APR_ENOTHREAD" }, + { 20013, "APR_ENOTHDKEY" }, + { 20014, "APR_EGENERAL" }, + { 20015, "APR_ENOSHMAVAIL" }, + { 20016, "APR_EBADIP" }, + { 20017, "APR_EBADMASK" }, + { 20019, "APR_EDSOOPEN" }, + { 20020, "APR_EABSOLUTE" }, + { 20021, "APR_ERELATIVE" }, + { 20022, "APR_EINCOMPLETE" }, + { 20023, "APR_EABOVEROOT" }, + { 20024, "APR_EBADPATH" }, + { 20025, "APR_EPATHWILD" }, + { 20026, "APR_ESYMNOTFOUND" }, + { 20027, "APR_EPROC_UNKNOWN" }, + { 20028, "APR_ENOTENOUGHENTROPY" }, + { 50000, "APR_OS_ERRSPACE_SIZE" }, + { 70000, "APR_OS_START_STATUS" }, + { 70001, "APR_INCHILD" }, + { 70002, "APR_INPARENT" }, + { 70003, "APR_DETACH" }, + { 70004, "APR_NOTDETACH" }, + { 70005, "APR_CHILD_DONE" }, + { 70006, "APR_CHILD_NOTDONE" }, + { 70007, "APR_TIMEUP" }, + { 70008, "APR_INCOMPLETE" }, + { 70012, "APR_BADCH" }, + { 70013, "APR_BADARG" }, + { 70014, "APR_EOF" }, + { 70015, "APR_NOTFOUND" }, + { 70019, "APR_ANONYMOUS" }, + { 70020, "APR_FILEBASED" }, + { 70021, "APR_KEYBASED" }, + { 70022, "APR_EINIT" }, + { 70023, "APR_ENOTIMPL" }, + { 70024, "APR_EMISMATCH" }, + { 70025, "APR_EBUSY" }, + { 100000, "APR_UTIL_START_STATUS" }, + { 100001, "APR_ENOKEY" }, + { 100002, "APR_ENOIV" }, + { 100003, "APR_EKEYTYPE" }, + { 100004, "APR_ENOSPACE" }, + { 100005, "APR_ECRYPT" }, + { 100006, "APR_EPADDING" }, + { 100007, "APR_EKEYLENGTH" }, + { 100008, "APR_ENOCIPHER" }, + { 100009, "APR_ENODIGEST" }, + { 100010, "APR_ENOENGINE" }, + { 100011, "APR_EINITENGINE" }, + { 100012, "APR_EREINIT" }, + { 120000, "APR_OS_START_USEERR" }, + { 120000, "APR_OS_START_USERERR" }, + { 620000, "APR_OS_START_CANONERR" }, + { 620001, "APR_EACCES" }, + { 620002, "APR_EEXIST" }, + { 620003, "APR_ENAMETOOLONG" }, + { 620004, "APR_ENOENT" }, + { 620005, "APR_ENOTDIR" }, + { 620006, "APR_ENOSPC" }, + { 620007, "APR_ENOMEM" }, + { 620008, "APR_EMFILE" }, + { 620009, "APR_ENFILE" }, + { 620010, "APR_EBADF" }, + { 620011, "APR_EINVAL" }, + { 620012, "APR_ESPIPE" }, + { 620013, "APR_EAGAIN" }, + { 620014, "APR_EINTR" }, + { 620015, "APR_ENOTSOCK" }, + { 620016, "APR_ECONNREFUSED" }, + { 620017, "APR_EINPROGRESS" }, + { 620018, "APR_ECONNABORTED" }, + { 620019, "APR_ECONNRESET" }, + { 620020, "APR_ETIMEDOUT" }, + { 620021, "APR_EHOSTUNREACH" }, + { 620022, "APR_ENETUNREACH" }, + { 620023, "APR_EFTYPE" }, + { 620024, "APR_EPIPE" }, + { 620025, "APR_EXDEV" }, + { 620026, "APR_ENOTEMPTY" }, + { 620027, "APR_EAFNOSUPPORT" }, + { 670000, "APR_OS_START_EAIERR" }, + { 720000, "APR_OS_START_SYSERR" } +}; diff --git a/contrib/subversion/subversion/libsvn_subr/fnv1a.c b/contrib/subversion/subversion/libsvn_subr/fnv1a.c new file mode 100644 index 000000000..458bdd25e --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/fnv1a.c @@ -0,0 +1,246 @@ +/* + * fnv1a.c : routines to create checksums derived from FNV-1a + * + * ==================================================================== + * 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_BYTEFUNC + +#include +#include + +#include "private/svn_subr_private.h" +#include "fnv1a.h" + +/** + * See http://www.isthe.com/chongo/tech/comp/fnv/ for more info on FNV-1 + */ + +/* FNV-1 32 bit constants taken from + * http://www.isthe.com/chongo/tech/comp/fnv/ + */ +#define FNV1_PRIME_32 0x01000193 +#define FNV1_BASE_32 2166136261U + +/* FNV-1a core implementation returning a 32 bit checksum over the first + * LEN bytes in INPUT. HASH is the checksum over preceding data (if any). + */ +static apr_uint32_t +fnv1a_32(apr_uint32_t hash, const void *input, apr_size_t len) +{ + const unsigned char *data = input; + const unsigned char *end = data + len; + + for (; data != end; ++data) + { + hash ^= *data; + hash *= FNV1_PRIME_32; + } + + return hash; +} + +/* Number of interleaved FVN-1a checksums we calculate for the modified + * checksum algorithm. + */ +enum { SCALING = 4 }; + +/* FNV-1a core implementation updating 4 interleaved checksums in HASHES + * over the first LEN bytes in INPUT. This will only process multiples + * of 4 and return the number of bytes processed. LEN - ReturnValue < 4. + */ +static apr_size_t +fnv1a_32x4(apr_uint32_t hashes[SCALING], const void *input, apr_size_t len) +{ + /* calculate SCALING interleaved FNV-1a hashes while the input + is large enough */ + const unsigned char *data = input; + const unsigned char *end = data + len; + for (; data + SCALING <= end; data += SCALING) + { + hashes[0] ^= data[0]; + hashes[0] *= FNV1_PRIME_32; + hashes[1] ^= data[1]; + hashes[1] *= FNV1_PRIME_32; + hashes[2] ^= data[2]; + hashes[2] *= FNV1_PRIME_32; + hashes[3] ^= data[3]; + hashes[3] *= FNV1_PRIME_32; + } + + return data - (const unsigned char *)input; +} + +/* Combine interleaved HASHES plus LEN bytes from INPUT into a single + * 32 bit hash value and return that. LEN must be < 4. + */ +static apr_uint32_t +finalize_fnv1a_32x4(apr_uint32_t hashes[SCALING], + const void *input, + apr_size_t len) +{ + char final_data[sizeof(apr_uint32_t) * SCALING + SCALING - 1]; + apr_size_t i; + assert(len < SCALING); + + for (i = 0; i < SCALING; ++i) + hashes[i] = htonl(hashes[i]); + + /* run FNV-1a over the interleaved checksums plus the remaining + (odd-lotted) input data */ + memcpy(final_data, hashes, sizeof(apr_uint32_t) * SCALING); + if (len) + memcpy(final_data + sizeof(apr_uint32_t) * SCALING, input, len); + + return fnv1a_32(FNV1_BASE_32, + final_data, + sizeof(apr_uint32_t) * SCALING + len); +} + +apr_uint32_t +svn__fnv1a_32(const void *input, apr_size_t len) +{ + return fnv1a_32(FNV1_BASE_32, input, len); +} + +apr_uint32_t +svn__fnv1a_32x4(const void *input, apr_size_t len) +{ + apr_uint32_t hashes[SCALING] + = { FNV1_BASE_32, FNV1_BASE_32, FNV1_BASE_32, FNV1_BASE_32 }; + apr_size_t processed = fnv1a_32x4(hashes, input, len); + + return finalize_fnv1a_32x4(hashes, + (const char *)input + processed, + len - processed); +} + +void +svn__fnv1a_32x4_raw(apr_uint32_t hashes[4], + const void *input, + apr_size_t len) +{ + apr_size_t processed; + + apr_size_t i; + for (i = 0; i < SCALING; ++i) + hashes[i] = FNV1_BASE_32; + + /* Process full 16 byte chunks. */ + processed = fnv1a_32x4(hashes, input, len); + + /* Fold the remainder (if any) into the first hash. */ + hashes[0] = fnv1a_32(hashes[0], (const char *)input + processed, + len - processed); +} + +struct svn_fnv1a_32__context_t +{ + apr_uint32_t hash; +}; + +svn_fnv1a_32__context_t * +svn_fnv1a_32__context_create(apr_pool_t *pool) +{ + svn_fnv1a_32__context_t *context = apr_palloc(pool, sizeof(*context)); + context->hash = FNV1_BASE_32; + + return context; +} + +void +svn_fnv1a_32__update(svn_fnv1a_32__context_t *context, + const void *data, + apr_size_t len) +{ + context->hash = fnv1a_32(context->hash, data, len); +} + +apr_uint32_t +svn_fnv1a_32__finalize(svn_fnv1a_32__context_t *context) +{ + return context->hash; +} + + +struct svn_fnv1a_32x4__context_t +{ + apr_uint32_t hashes[SCALING]; + apr_size_t buffered; + char buffer[SCALING]; +}; + +svn_fnv1a_32x4__context_t * +svn_fnv1a_32x4__context_create(apr_pool_t *pool) +{ + svn_fnv1a_32x4__context_t *context = apr_palloc(pool, sizeof(*context)); + + context->hashes[0] = FNV1_BASE_32; + context->hashes[1] = FNV1_BASE_32; + context->hashes[2] = FNV1_BASE_32; + context->hashes[3] = FNV1_BASE_32; + + context->buffered = 0; + + return context; +} + +void +svn_fnv1a_32x4__update(svn_fnv1a_32x4__context_t *context, + const void *data, + apr_size_t len) +{ + apr_size_t processed; + + if (context->buffered) + { + apr_size_t to_copy = SCALING - context->buffered; + if (to_copy > len) + { + memcpy(context->buffer + context->buffered, data, len); + context->buffered += len; + return; + } + + memcpy(context->buffer + context->buffered, data, to_copy); + data = (const char *)data + to_copy; + len -= to_copy; + + fnv1a_32x4(context->hashes, context->buffer, SCALING); + context->buffered = 0; + } + + processed = fnv1a_32x4(context->hashes, data, len); + if (processed != len) + { + context->buffered = len - processed; + memcpy(context->buffer, + (const char*)data + processed, + len - processed); + } +} + +apr_uint32_t +svn_fnv1a_32x4__finalize(svn_fnv1a_32x4__context_t *context) +{ + return finalize_fnv1a_32x4(context->hashes, + context->buffer, + context->buffered); +} diff --git a/contrib/subversion/subversion/libsvn_subr/fnv1a.h b/contrib/subversion/subversion/libsvn_subr/fnv1a.h new file mode 100644 index 000000000..4bb0b1275 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/fnv1a.h @@ -0,0 +1,91 @@ +/* + * fnv1a.h : routines to create checksums derived from FNV-1a + * + * ==================================================================== + * 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_SUBR_FNV1A_H +#define SVN_LIBSVN_SUBR_FNV1A_H + +#include + +#include "svn_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Opaque FNV-1a checksum creation context type. + */ +typedef struct svn_fnv1a_32__context_t svn_fnv1a_32__context_t; + +/* Return a new FNV-1a checksum creation context allocated in POOL. + */ +svn_fnv1a_32__context_t * +svn_fnv1a_32__context_create(apr_pool_t *pool); + +/* Feed LEN bytes from DATA into the FNV-1a checksum creation CONTEXT. + */ +void +svn_fnv1a_32__update(svn_fnv1a_32__context_t *context, + const void *data, + apr_size_t len); + +/* Return the FNV-1a checksum over all data fed into CONTEXT. + */ +apr_uint32_t +svn_fnv1a_32__finalize(svn_fnv1a_32__context_t *context); + + +/* Opaque modified FNV-1a checksum creation context type. + */ +typedef struct svn_fnv1a_32x4__context_t svn_fnv1a_32x4__context_t; + +/* Return a new modified FNV-1a checksum creation context allocated in POOL. + */ +svn_fnv1a_32x4__context_t * +svn_fnv1a_32x4__context_create(apr_pool_t *pool); + +/* Feed LEN bytes from DATA into the modified FNV-1a checksum creation + * CONTEXT. + */ +void +svn_fnv1a_32x4__update(svn_fnv1a_32x4__context_t *context, + const void *data, + apr_size_t len); + +/* Return the modified FNV-1a checksum over all data fed into CONTEXT. + */ +apr_uint32_t +svn_fnv1a_32x4__finalize(svn_fnv1a_32x4__context_t *context); + +/* Set HASHES to the 4 partial hash sums produced by the modified FVN-1a + * over INPUT of LEN bytes. + */ +void +svn__fnv1a_32x4_raw(apr_uint32_t hashes[4], + const void *input, + apr_size_t len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_SUBR_FNV1A_H */ diff --git a/contrib/subversion/subversion/libsvn_subr/gpg_agent.c b/contrib/subversion/subversion/libsvn_subr/gpg_agent.c index 4dbf1184e..d53eec44c 100644 --- a/contrib/subversion/subversion/libsvn_subr/gpg_agent.c +++ b/contrib/subversion/subversion/libsvn_subr/gpg_agent.c @@ -76,6 +76,7 @@ #include "svn_user.h" #include "svn_dirent_uri.h" +#include "auth.h" #include "private/svn_auth_private.h" #include "svn_private_config.h" @@ -102,12 +103,46 @@ escape_blanks(char *str) return str; } +#define is_hex(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F')) +#define hex_to_int(c) ((c) < '9' ? (c) - '0' : (c) - 'A' + 10) + +/* Modify STR in-place. '%', CR and LF are always percent escaped, + other characters may be percent escaped, always using uppercase + hex, see https://www.gnupg.org/documentation/manuals/assuan.pdf */ +static char * +unescape_assuan(char *str) +{ + char *s = str; + + while (s[0]) + { + if (s[0] == '%' && is_hex(s[1]) && is_hex(s[2])) + { + char *s2 = s; + char val = hex_to_int(s[1]) * 16 + hex_to_int(s[2]); + + s2[0] = val; + ++s2; + + while (s2[2]) + { + s2[0] = s2[2]; + ++s2; + } + s2[0] = '\0'; + } + ++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) + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *cache_id = NULL; svn_checksum_t *digest = NULL; @@ -208,7 +243,7 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) /* 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 + * 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 */ @@ -218,7 +253,7 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) 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 + * 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); @@ -231,8 +266,9 @@ find_running_gpg_agent(int *new_sd, apr_pool_t *pool) if (!homedir) return SVN_NO_ERROR; + homedir = svn_dirent_canonicalize(homedir, pool); socket_name = svn_dirent_join_many(pool, homedir, ".gnupg", - "S.gpg-agent", NULL); + "S.gpg-agent", SVN_VA_NULL); } if (socket_name != NULL) @@ -377,7 +413,7 @@ password_get_gpg_agent(svn_boolean_t *done, apr_pool_t *pool) { int sd; - const char *p = NULL; + char *p = NULL; char *ep = NULL; char *buffer; const char *request = NULL; @@ -450,7 +486,7 @@ password_get_gpg_agent(svn_boolean_t *done, if (ep != NULL) *ep = '\0'; - *password = p; + *password = unescape_assuan(p); *done = TRUE; return SVN_NO_ERROR; @@ -629,7 +665,7 @@ static const svn_auth_provider_t gpg_agent_simple_provider = { /* Public API */ void -svn_auth_get_gpg_agent_simple_provider(svn_auth_provider_object_t **provider, +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)); diff --git a/contrib/subversion/subversion/libsvn_subr/hash.c b/contrib/subversion/subversion/libsvn_subr/hash.c index 7868cac8f..f58c43c40 100644 --- a/contrib/subversion/subversion/libsvn_subr/hash.c +++ b/contrib/subversion/subversion/libsvn_subr/hash.c @@ -40,6 +40,7 @@ #include "svn_pools.h" #include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" #include "private/svn_subr_private.h" #include "svn_private_config.h" @@ -89,119 +90,156 @@ /*** Dumping and loading hash files. */ /* Implements svn_hash_read2 and svn_hash_read_incremental. */ -static svn_error_t * -hash_read(apr_hash_t *hash, svn_stream_t *stream, const char *terminator, - svn_boolean_t incremental, apr_pool_t *pool) +svn_error_t * +svn_hash__read_entry(svn_hash__entry_t *entry, + svn_stream_t *stream, + const char *terminator, + svn_boolean_t incremental, + apr_pool_t *pool) { svn_stringbuf_t *buf; svn_boolean_t eof; - apr_size_t len, keylen, vallen; - char c, *keybuf, *valbuf; - apr_pool_t *iterpool = svn_pool_create(pool); + apr_size_t len; + char c; - while (1) - { - svn_error_t *err; - apr_uint64_t ui64; + svn_error_t *err; + apr_uint64_t ui64; - svn_pool_clear(iterpool); + /* Read a key length line. Might be END, though. */ + SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, pool)); - /* Read a key length line. Might be END, though. */ - SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool)); + /* Check for the end of the hash. */ + if ((!terminator && eof && buf->len == 0) + || (terminator && (strcmp(buf->data, terminator) == 0))) + { + entry->key = NULL; + entry->keylen = 0; + entry->val = NULL; + entry->vallen = 0; - /* Check for the end of the hash. */ - if ((!terminator && eof && buf->len == 0) - || (terminator && (strcmp(buf->data, terminator) == 0))) - break; + return SVN_NO_ERROR; + } - /* Check for unexpected end of stream */ - if (eof) + /* Check for unexpected end of stream */ + if (eof) + return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, + _("Serialized hash missing terminator")); + + if ((buf->len >= 3) && (buf->data[0] == 'K') && (buf->data[1] == ' ')) + { + /* Get the length of the key */ + err = svn_cstring_strtoui64(&ui64, buf->data + 2, + 0, APR_SIZE_MAX, 10); + if (err) + return svn_error_create(SVN_ERR_MALFORMED_FILE, err, + _("Serialized hash malformed key length")); + entry->keylen = (apr_size_t)ui64; + + /* Now read that much into a buffer. */ + entry->key = apr_palloc(pool, entry->keylen + 1); + SVN_ERR(svn_stream_read_full(stream, entry->key, &entry->keylen)); + entry->key[entry->keylen] = '\0'; + + /* Suck up extra newline after key data */ + len = 1; + SVN_ERR(svn_stream_read_full(stream, &c, &len)); + if (c != '\n') return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash missing terminator")); + _("Serialized hash malformed key data")); - if ((buf->len >= 3) && (buf->data[0] == 'K') && (buf->data[1] == ' ')) + /* Read a val length line */ + SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, pool)); + + if ((buf->data[0] == 'V') && (buf->data[1] == ' ')) { - /* Get the length of the key */ + /* Get the length of the val */ err = svn_cstring_strtoui64(&ui64, buf->data + 2, 0, APR_SIZE_MAX, 10); if (err) return svn_error_create(SVN_ERR_MALFORMED_FILE, err, - _("Serialized hash malformed")); - keylen = (apr_size_t)ui64; + _("Serialized hash malformed value length")); + entry->vallen = (apr_size_t)ui64; - /* Now read that much into a buffer. */ - keybuf = apr_palloc(pool, keylen + 1); - SVN_ERR(svn_stream_read(stream, keybuf, &keylen)); - keybuf[keylen] = '\0'; + entry->val = apr_palloc(pool, entry->vallen + 1); + SVN_ERR(svn_stream_read_full(stream, entry->val, &entry->vallen)); + entry->val[entry->vallen] = '\0'; - /* Suck up extra newline after key data */ + /* Suck up extra newline after val data */ len = 1; - SVN_ERR(svn_stream_read(stream, &c, &len)); + SVN_ERR(svn_stream_read_full(stream, &c, &len)); if (c != '\n') return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash malformed")); - - /* Read a val length line */ - SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool)); + _("Serialized hash malformed value data")); + } + else + return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, + _("Serialized hash malformed")); + } + else if (incremental && (buf->len >= 3) + && (buf->data[0] == 'D') && (buf->data[1] == ' ')) + { + /* Get the length of the key */ + err = svn_cstring_strtoui64(&ui64, buf->data + 2, + 0, APR_SIZE_MAX, 10); + if (err) + return svn_error_create(SVN_ERR_MALFORMED_FILE, err, + _("Serialized hash malformed key length")); + entry->keylen = (apr_size_t)ui64; + + /* Now read that much into a buffer. */ + entry->key = apr_palloc(pool, entry->keylen + 1); + SVN_ERR(svn_stream_read_full(stream, entry->key, &entry->keylen)); + entry->key[entry->keylen] = '\0'; + + /* Suck up extra newline after key data */ + len = 1; + SVN_ERR(svn_stream_read_full(stream, &c, &len)); + if (c != '\n') + return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, + _("Serialized hash malformed key data")); - if ((buf->data[0] == 'V') && (buf->data[1] == ' ')) - { - err = svn_cstring_strtoui64(&ui64, buf->data + 2, - 0, APR_SIZE_MAX, 10); - if (err) - return svn_error_create(SVN_ERR_MALFORMED_FILE, err, - _("Serialized hash malformed")); - vallen = (apr_size_t)ui64; + /* Remove this hash entry. */ + entry->vallen = 0; + entry->val = NULL; + } + else + { + return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, + _("Serialized hash malformed")); + } - valbuf = apr_palloc(iterpool, vallen + 1); - SVN_ERR(svn_stream_read(stream, valbuf, &vallen)); - valbuf[vallen] = '\0'; + return SVN_NO_ERROR; +} - /* Suck up extra newline after val data */ - len = 1; - SVN_ERR(svn_stream_read(stream, &c, &len)); - if (c != '\n') - return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash malformed")); +static svn_error_t * +hash_read(apr_hash_t *hash, svn_stream_t *stream, const char *terminator, + svn_boolean_t incremental, apr_pool_t *pool) +{ + apr_pool_t *iterpool = svn_pool_create(pool); - /* Add a new hash entry. */ - apr_hash_set(hash, keybuf, keylen, - svn_string_ncreate(valbuf, vallen, pool)); - } - else - return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash malformed")); - } - else if (incremental && (buf->len >= 3) - && (buf->data[0] == 'D') && (buf->data[1] == ' ')) - { - /* Get the length of the key */ - err = svn_cstring_strtoui64(&ui64, buf->data + 2, - 0, APR_SIZE_MAX, 10); - if (err) - return svn_error_create(SVN_ERR_MALFORMED_FILE, err, - _("Serialized hash malformed")); - keylen = (apr_size_t)ui64; + while (1) + { + svn_hash__entry_t entry; - /* Now read that much into a buffer. */ - keybuf = apr_palloc(iterpool, keylen + 1); - SVN_ERR(svn_stream_read(stream, keybuf, &keylen)); - keybuf[keylen] = '\0'; + svn_pool_clear(iterpool); + SVN_ERR(svn_hash__read_entry(&entry, stream, terminator, + incremental, iterpool)); - /* Suck up extra newline after key data */ - len = 1; - SVN_ERR(svn_stream_read(stream, &c, &len)); - if (c != '\n') - return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash malformed")); + /* end of hash? */ + if (entry.key == NULL) + break; - /* Remove this hash entry. */ - apr_hash_set(hash, keybuf, keylen, NULL); + if (entry.val) + { + /* Add a new hash entry. */ + apr_hash_set(hash, apr_pstrmemdup(pool, entry.key, entry.keylen), + entry.keylen, + svn_string_ncreate(entry.val, entry.vallen, pool)); } else { - return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, - _("Serialized hash malformed")); + /* Remove this hash entry. */ + apr_hash_set(hash, entry.key, entry.keylen, NULL); } } @@ -497,7 +535,7 @@ svn_hash_keys(apr_array_header_t **array, for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi)) { - APR_ARRAY_PUSH(*array, const char *) = svn__apr_hash_index_key(hi); + APR_ARRAY_PUSH(*array, const char *) = apr_hash_this_key(hi); } return SVN_NO_ERROR; @@ -522,23 +560,6 @@ svn_hash_from_cstring_keys(apr_hash_t **hash_p, } -#if !APR_VERSION_AT_LEAST(1, 3, 0) -void -svn_hash__clear(apr_hash_t *hash) -{ - apr_hash_index_t *hi; - const void *key; - apr_ssize_t klen; - - for (hi = apr_hash_first(NULL, hash); hi; hi = apr_hash_next(hi)) - { - apr_hash_this(hi, &key, &klen, NULL); - apr_hash_set(hash, key, klen, NULL); - } -} -#endif - - /*** Specialized getter APIs ***/ @@ -576,20 +597,16 @@ svn_hash__get_bool(apr_hash_t *hash, const char *key, /*** Optimized hash function ***/ -/* Optimized version of apr_hashfunc_default in APR 1.4.5 and earlier. - * It assumes that the CPU has 32-bit multiplications with high throughput - * of at least 1 operation every 3 cycles. Latency is not an issue. Another - * optimization is a mildly unrolled main loop and breaking the dependency - * chain within the loop. +/* apr_hashfunc_t optimized for the key that we use in SVN: paths and + * property names. Its primary goal is speed for keys of known length. * - * Note that most CPUs including Intel Atom, VIA Nano, ARM feature the - * assumed pipelined multiplication circuitry. They can do one MUL every - * or every other cycle. - * - * The performance is ultimately limited by the fact that most CPUs can - * do only one LOAD and only one BRANCH operation per cycle. The best we - * can do is to process one character per cycle - provided the processor - * is wide enough to do 1 LOAD, COMPARE, BRANCH, MUL and ADD per cycle. + * Since strings tend to spawn large value spaces (usually differ in many + * bits with differences spanning a larger section of the key), we can be + * quite sloppy extracting a hash value. The more keys there are in a + * hash container, the more bits of the value returned by this function + * will be used. For a small number of string keys, choosing bits from any + * any fix location close to the tail of those keys would usually be good + * enough to prevent high collision rates. */ static unsigned int hashfunc_compatible(const char *char_key, apr_ssize_t *klen) @@ -600,37 +617,29 @@ hashfunc_compatible(const char *char_key, apr_ssize_t *klen) apr_ssize_t i; if (*klen == APR_HASH_KEY_STRING) + *klen = strlen(char_key); + +#if SVN_UNALIGNED_ACCESS_IS_OK + for (p = key, i = *klen; i >= 4; i-=4, p+=4) { - for (p = key; ; p+=4) - { - unsigned int new_hash = hash * 33 * 33 * 33 * 33; - if (!p[0]) break; - new_hash += p[0] * 33 * 33 * 33; - if (!p[1]) break; - new_hash += p[1] * 33 * 33; - if (!p[2]) break; - new_hash += p[2] * 33; - if (!p[3]) break; - hash = new_hash + p[3]; - } - for (; *p; p++) - hash = hash * 33 + *p; - - *klen = p - key; + apr_uint32_t chunk = *(const apr_uint32_t *)p; + + /* the ">> 17" part gives upper bits in the chunk a chance to make + some impact as well */ + hash = hash * 33 * 33 * 33 * 33 + chunk + (chunk >> 17); } - else +#else + for (p = key, i = *klen; i >= 4; i-=4, p+=4) { - for (p = key, i = *klen; i >= 4; i-=4, p+=4) - { - hash = hash * 33 * 33 * 33 * 33 - + p[0] * 33 * 33 * 33 - + p[1] * 33 * 33 - + p[2] * 33 - + p[3]; - } - for (; i; i--, p++) - hash = hash * 33 + *p; + hash = hash * 33 * 33 * 33 * 33 + + p[0] * 33 * 33 * 33 + + p[1] * 33 * 33 + + p[2] * 33 + + p[3]; } +#endif + for (; i; i--, p++) + hash = hash * 33 + *p; return hash; } diff --git a/contrib/subversion/subversion/libsvn_subr/internal_statements.h b/contrib/subversion/subversion/libsvn_subr/internal_statements.h index c606de4c6..93251a47e 100644 --- a/contrib/subversion/subversion/libsvn_subr/internal_statements.h +++ b/contrib/subversion/subversion/libsvn_subr/internal_statements.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_subr/token-map.h. +/* This file is automatically generated from internal_statements.sql and .dist_sandbox/subversion-1.9.4/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 diff --git a/contrib/subversion/subversion/libsvn_subr/io.c b/contrib/subversion/subversion/libsvn_subr/io.c index e27411e13..75e85647d 100644 --- a/contrib/subversion/subversion/libsvn_subr/io.c +++ b/contrib/subversion/subversion/libsvn_subr/io.c @@ -47,8 +47,8 @@ #include #include -#ifdef WIN32 -#include +#if APR_HAVE_FCNTL_H +#include #endif #include "svn_hash.h" @@ -66,6 +66,8 @@ #include "private/svn_atomic.h" #include "private/svn_io_private.h" +#include "private/svn_utf_private.h" +#include "private/svn_dep_compat.h" #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS" @@ -139,6 +141,49 @@ #endif #endif +#ifdef WIN32 + +#if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */ +typedef struct _FILE_RENAME_INFO { + BOOL ReplaceIfExists; + HANDLE RootDirectory; + DWORD FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFO, *PFILE_RENAME_INFO; + +typedef struct _FILE_DISPOSITION_INFO { + BOOL DeleteFile; +} FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO; + +#define FileRenameInfo 3 +#define FileDispositionInfo 4 +#endif /* WIN32 < Vista */ + +/* One-time initialization of the late bound Windows API functions. */ +static volatile svn_atomic_t win_dynamic_imports_state = 0; + +/* Pointer to GetFinalPathNameByHandleW function from kernel32.dll. */ +typedef DWORD (WINAPI *GETFINALPATHNAMEBYHANDLE)( + HANDLE hFile, + WCHAR *lpszFilePath, + DWORD cchFilePath, + DWORD dwFlags); + +typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, + int FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize); + +static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL; +static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL; + +/* Forward declaration. */ +static svn_error_t * io_win_read_link(svn_string_t **dest, + const char *path, + apr_pool_t *pool); + +#endif + /* Forward declaration */ static apr_status_t dir_is_empty(const char *dir, apr_pool_t *pool); @@ -330,6 +375,25 @@ file_open(apr_file_t **f, if (retry_on_failure) { +#ifdef WIN32 + if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) + { + if ((flag & (APR_CREATE | APR_EXCL)) == (APR_CREATE | APR_EXCL)) + return status; /* Can't create if there is something */ + + if (flag & (APR_WRITE | APR_CREATE)) + { + apr_finfo_t finfo; + + if (!apr_stat(&finfo, fname_apr, SVN__APR_FINFO_READONLY, pool)) + { + if (finfo.protection & APR_FREADONLY) + return status; /* Retrying won't fix this */ + } + } + } +#endif + WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool)); } return status; @@ -660,7 +724,7 @@ svn_io_read_link(svn_string_t **dest, const char *path, apr_pool_t *pool) { -#ifdef HAVE_READLINK +#if defined(HAVE_READLINK) svn_string_t dest_apr; const char *path_apr; char buf[1025]; @@ -681,6 +745,8 @@ svn_io_read_link(svn_string_t **dest, /* ### Cast needed, one of these interfaces is wrong */ return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool); +#elif defined(WIN32) + return io_win_read_link(dest, path, pool); #else return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Symbolic links are not supported on this " @@ -959,10 +1025,9 @@ svn_io_copy_perms(const char *src, 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); + return svn_error_quick_wrapf( + err, _("Can't set permissions on '%s'"), + svn_dirent_local_style(dst, pool)); } } } @@ -1130,8 +1195,13 @@ svn_io_make_dir_recursively(const char *path, apr_pool_t *pool) 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)); +#ifdef WIN32 + /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a + permanent error */ + if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) + WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr, + APR_OS_DEFAULT, pool)); +#endif if (apr_err) return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"), @@ -1140,9 +1210,11 @@ svn_io_make_dir_recursively(const char *path, apr_pool_t *pool) return SVN_NO_ERROR; } -svn_error_t *svn_io_file_create(const char *file, - const char *contents, - apr_pool_t *pool) +svn_error_t * +svn_io_file_create_bytes(const char *file, + const void *contents, + apr_size_t length, + apr_pool_t *scratch_pool) { apr_file_t *f; apr_size_t written; @@ -1151,26 +1223,59 @@ svn_error_t *svn_io_file_create(const char *file, 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); + scratch_pool)); + if (length) + err = svn_io_file_write_full(f, contents, length, &written, + scratch_pool); + err = svn_error_compose_create( + err, + svn_io_file_close(f, scratch_pool)); - return svn_error_trace( - svn_error_compose_create(err, - svn_io_file_close(f, pool))); + if (err) + { + /* Our caller doesn't know if we left a file or not if we return + an error. Better to cleanup after ourselves if we created the + file. */ + return svn_error_trace( + svn_error_compose_create( + err, + svn_io_remove_file2(file, TRUE, scratch_pool))); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_io_file_create(const char *file, + const char *contents, + apr_pool_t *pool) +{ + return svn_error_trace(svn_io_file_create_bytes(file, contents, + contents ? strlen(contents) + : 0, + pool)); +} + +svn_error_t * +svn_io_file_create_empty(const char *file, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0, + scratch_pool)); } -svn_error_t *svn_io_dir_file_copy(const char *src_path, - const char *dest_path, - const char *file, - apr_pool_t *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); + return svn_error_trace( + svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool)); } @@ -1456,7 +1561,7 @@ get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool) /* 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 + Explicitly 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. @@ -1528,7 +1633,8 @@ io_set_file_perms(const char *path, status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool); if (status) { - if (ignore_enoent && APR_STATUS_IS_ENOENT(status)) + if (ignore_enoent && (APR_STATUS_IS_ENOENT(status) + || SVN__APR_STATUS_IS_ENOTDIR(status))) return SVN_NO_ERROR; else if (status != APR_ENOTIMPL) return svn_error_wrap_apr(status, @@ -1640,27 +1746,13 @@ io_set_file_perms(const char *path, #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) +/* This is semantically the same as the APR utf8_to_unicode_path + function, but reimplemented here because APR does not export it. */ +svn_error_t* +svn_io__utf8_to_unicode_longpath(const WCHAR **result, + const char *source, + apr_pool_t *result_pool) { - /* 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 @@ -1675,95 +1767,331 @@ static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retl * 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; + const WCHAR *prefix = NULL; + const int srclen = strlen(source); + WCHAR *buffer; + + if (srclen > 248) + { + if (svn_ctype_isalpha(source[0]) && source[1] == ':' + && (source[2] == '/' || source[2] == '\\')) + { + /* This is an ordinary absolute path. */ + prefix = L"\\\\?\\"; } - 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; + else if ((source[0] == '/' || source[0] == '\\') + && (source[1] == '/' || source[1] == '\\') + && source[2] != '?') + { + /* This is a UNC path */ + source += 2; /* Skip the leading slashes */ + prefix = L"\\\\?\\UNC\\"; } } - if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) { - return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv; + SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source, + prefix, result_pool)); + + /* Convert slashes to backslashes because the \\?\ path format + does not allow backslashes as path separators. */ + *result = buffer; + for (; *buffer; ++buffer) + { + if (*buffer == '/') + *buffer = '\\'; } - if (srcremains) { - return APR_ENAMETOOLONG; + return SVN_NO_ERROR; +} + +/* This is semantically the same as the APR unicode_to_utf8_path + function, but reimplemented here because APR does not export it. */ +static svn_error_t * +io_unicode_to_utf8_path(const char **result, + const WCHAR *source, + apr_pool_t *result_pool) +{ + const char *utf8_buffer; + char *buffer; + + SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source, + NULL, result_pool)); + if (!*utf8_buffer) + { + *result = utf8_buffer; + return SVN_NO_ERROR; + } + + /* We know that the non-empty buffer returned from the UTF-16 to + UTF-8 conversion function is in fact writable. */ + buffer = (char*)utf8_buffer; + + /* Skip the leading 4 characters if the path begins \\?\, or substitute + * // for the \\?\UNC\ path prefix, allocating the maximum string + * length based on the remaining string, plus the trailing null. + * then transform \\'s back into /'s since the \\?\ form never + * allows '/' path separators, and APR always uses '/'s. + */ + if (0 == strncmp(buffer, "\\\\?\\", 4)) + { + buffer += 4; + if (0 == strncmp(buffer, "UNC\\", 4)) + { + buffer += 2; + *buffer = '/'; + } } - for (; *t; ++t) - if (*t == L'/') - *t = L'\\'; - return APR_SUCCESS; + + *result = buffer; + for (; *buffer; ++buffer) + { + if (*buffer == '\\') + *buffer = '/'; + } + return SVN_NO_ERROR; } -#endif -static apr_status_t io_win_file_attrs_set(const char *fname, - DWORD attributes, - DWORD attr_mask, - apr_pool_t *pool) +static svn_error_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 + const WCHAR *wfname; + + SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool)); + + flags = GetFileAttributesW(wfname); + if (flags == 0xFFFFFFFF) + return svn_error_wrap_apr(apr_get_os_error(), + _("Can't get attributes of file '%s'"), + svn_dirent_local_style(fname, pool)); + + flags &= ~attr_mask; + flags |= (attributes & attr_mask); + + if (!SetFileAttributesW(wfname, flags)) + return svn_error_wrap_apr(apr_get_os_error(), + _("Can't set attributes of file '%s'"), + svn_dirent_local_style(fname, pool)); + + return SVN_NO_ERROR;; +} + +static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool) +{ + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); -#if APR_HAS_UNICODE_FS - IF_WIN_OS_IS_UNICODE + if (kernel32) { - if (rv = io_utf8_to_unicode_path(wfname, - sizeof(wfname) / sizeof(wfname[0]), - fname)) - return rv; - flags = GetFileAttributesW(wfname); + get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE) + GetProcAddress(kernel32, "GetFinalPathNameByHandleW"); + + set_file_information_by_handle_proc = (SetFileInformationByHandle_t) + GetProcAddress(kernel32, "SetFileInformationByHandle"); } -#endif -#if APR_HAS_ANSI_FS - ELSE_WIN_OS_IS_ANSI + + return SVN_NO_ERROR; +} + +static svn_error_t * io_win_read_link(svn_string_t **dest, + const char *path, + apr_pool_t *pool) +{ + SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state, + win_init_dynamic_imports, NULL, pool)); + + if (get_final_path_name_by_handle_proc) + { + DWORD rv; + apr_status_t status; + apr_file_t *file; + apr_os_file_t filehand; + WCHAR wdest[APR_PATH_MAX]; + const char *data; + + /* reserve one char for terminating zero. */ + DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1; + + status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool); + + if (status) + return svn_error_wrap_apr(status, + _("Can't read contents of link")); + + apr_os_file_get(&filehand, file); + + rv = get_final_path_name_by_handle_proc( + filehand, wdest, wdest_len, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + + /* Save error code. */ + status = apr_get_os_error(); + + /* Close file/directory handle in any case. */ + apr_file_close(file); + + /* GetFinaPathNameByHandleW returns number of characters copied to + * output buffer. Returns zero on error. Returns required buffer size + * if supplied buffer is not enough. */ + if (rv > wdest_len || rv == 0) + { + return svn_error_wrap_apr(status, + _("Can't read contents of link")); + } + + /* GetFinaPathNameByHandleW doesn't add terminating NUL. */ + wdest[rv] = 0; + SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool)); + + /* The result is already in the correct pool, so avoid copying + it to create the string. */ + *dest = svn_string_create_empty(pool); + if (*data) + { + (*dest)->data = data; + (*dest)->len = strlen(data); + } + + return SVN_NO_ERROR; + } + else + { + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Symbolic links are not supported on this " + "platform")); + } +} + +/* Wrapper around Windows API function SetFileInformationByHandle() that + * returns APR status instead of boolean flag. */ +static apr_status_t +win32_set_file_information_by_handle(HANDLE hFile, + int FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize) +{ + svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state, + win_init_dynamic_imports, + NULL, NULL)); + + if (!set_file_information_by_handle_proc) { - flags = GetFileAttributesA(fname); + return SVN_ERR_UNSUPPORTED_FEATURE; } -#endif - if (flags == 0xFFFFFFFF) - return apr_get_os_error(); + if (!set_file_information_by_handle_proc(hFile, FileInformationClass, + lpFileInformation, + dwBufferSize)) + { + return apr_get_os_error(); + } - flags &= ~attr_mask; - flags |= (attributes & attr_mask); + return APR_SUCCESS; +} + +svn_error_t * +svn_io__win_delete_file_on_close(apr_file_t *file, + const char *path, + apr_pool_t *pool) +{ + FILE_DISPOSITION_INFO disposition_info; + HANDLE hFile; + apr_status_t status; + + apr_os_file_get(&hFile, file); + + disposition_info.DeleteFile = TRUE; -#if APR_HAS_UNICODE_FS - IF_WIN_OS_IS_UNICODE + status = win32_set_file_information_by_handle(hFile, FileDispositionInfo, + &disposition_info, + sizeof(disposition_info)); + + if (status) { - rv = SetFileAttributesW(wfname, flags); + return svn_error_wrap_apr(status, _("Can't remove file '%s'"), + svn_dirent_local_style(path, pool)); } -#endif -#if APR_HAS_ANSI_FS - ELSE_WIN_OS_IS_ANSI + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_io__win_rename_open_file(apr_file_t *file, + const char *from_path, + const char *to_path, + apr_pool_t *pool) +{ + WCHAR *w_final_abspath; + size_t path_len; + size_t rename_size; + FILE_RENAME_INFO *rename_info; + HANDLE hFile; + apr_status_t status; + + apr_os_file_get(&hFile, file); + + SVN_ERR(svn_io__utf8_to_unicode_longpath( + &w_final_abspath, svn_dirent_local_style(to_path,pool), + pool)); + + path_len = wcslen(w_final_abspath); + rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len; + + /* The rename info struct doesn't need hacks for long paths, + so no ugly escaping calls here */ + rename_info = apr_pcalloc(pool, rename_size); + rename_info->ReplaceIfExists = TRUE; + rename_info->FileNameLength = path_len; + memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR)); + + status = win32_set_file_information_by_handle(hFile, FileRenameInfo, + rename_info, + rename_size); + + if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) { - rv = SetFileAttributesA(fname, flags); + /* Set the destination file writable because Windows will not allow + us to rename when final_abspath is read-only. */ + SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); + + status = win32_set_file_information_by_handle(hFile, + FileRenameInfo, + rename_info, + rename_size); + } + + /* Windows returns Vista+ client accessing network share stored on Windows + Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+ + client access Windows Server 2008 with disabled SMBv2 protocol. + + So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when + SetFileInformationByHandle() is not available and let caller to + handle it. + + See "Access denied error on checkout-commit after updating to 1.9.X" + discussion on dev@s.a.o: + http://svn.haxx.se/dev/archive-2015-09/0054.shtml */ + if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) + { + status = SVN_ERR_UNSUPPORTED_FEATURE; } -#endif - if (rv == 0) - return apr_get_os_error(); + 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 APR_SUCCESS; + return SVN_NO_ERROR; } -#endif +#endif /* WIN32 */ svn_error_t * svn_io_set_file_read_write_carefully(const char *path, @@ -1798,7 +2126,8 @@ svn_io_set_file_read_only(const char *path, pool); if (status && status != APR_ENOTIMPL) - if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status)) + if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status) + || SVN__APR_STATUS_IS_ENOTDIR(status)))) return svn_error_wrap_apr(status, _("Can't set file '%s' read-only"), svn_dirent_local_style(path, pool)); @@ -2089,6 +2418,35 @@ svn_io_file_lock2(const char *lock_file, return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool); } +svn_error_t * +svn_io__file_lock_autocreate(const char *lock_file, + apr_pool_t *pool) +{ + svn_error_t *err + = svn_io_file_lock2(lock_file, 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); + + /* This file creation is racy. + We don't care as long as file gets created at all. */ + err = svn_io_file_create_empty(lock_file, pool); + if (err && APR_STATUS_IS_EEXIST(err->apr_err)) + { + svn_error_clear(err); + err = NULL; + } + + /* Finally, lock the file - if it exists */ + if (!err) + err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool); + } + + return svn_error_trace(err); +} + /* Data consistency/coherency operations. */ @@ -2098,11 +2456,12 @@ svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file, { apr_os_file_t filehand; + /* ### In apr 1.4+ we could delegate most of this function to + apr_file_sync(). The only major difference is that this doesn't + contain the retry loop for EINTR on linux. */ + /* 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)); + SVN_ERR(svn_io_file_flush(file, pool)); apr_os_file_get(&filehand, file); @@ -2119,7 +2478,11 @@ svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file, int rv; do { +#ifdef F_FULLFSYNC + rv = fcntl(filehand, F_FULLFSYNC, 0); +#else rv = fsync(filehand); +#endif } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error())); /* If the file is in a memory filesystem, fsync() may return @@ -2159,36 +2522,34 @@ stringbuf_from_aprfile(svn_stringbuf_t **result, 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); + char *buf; /* 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; - } + apr_finfo_t finfo = { 0 }; - /* If we now know the filename, try to stat(). If we succeed, - we know how to allocate our stringbuf. */ - if (filename) + /* In some cases we get size 0 and no error for non files, + so we also check for the name. (= cached in apr_file_t) */ + if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname) { - apr_finfo_t finfo; - if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool))) - res_initial_len = (apr_size_t)finfo.size; + /* we've got the file length. Now, read it in one go. */ + svn_boolean_t eof; + res_initial_len = (apr_size_t)finfo.size; + res = svn_stringbuf_create_ensure(res_initial_len, pool); + SVN_ERR(svn_io_file_read_full2(file, res->data, + res_initial_len, &res->len, + &eof, pool)); + res->data[res->len] = 0; + + *result = res; + return SVN_NO_ERROR; } } - /* XXX: We should check the incoming data for being of type binary. */ - + buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 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 @@ -2204,7 +2565,7 @@ stringbuf_from_aprfile(svn_stringbuf_t **result, /* Having read all the data we *expect* EOF */ if (err && !APR_STATUS_IS_EOF(err->apr_err)) - return err; + return svn_error_trace(err); svn_error_clear(err); *result = res; @@ -2269,11 +2630,6 @@ svn_io_remove_file2(const char *path, 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 @@ -2289,30 +2645,36 @@ svn_io_remove_file2(const char *path, return SVN_NO_ERROR; } + /* Check to make sure we aren't trying to delete a directory */ + if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) + || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) { - 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; + 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)); - } + 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)); + /* Just return the delete error */ +#endif - return SVN_NO_ERROR; + if (!apr_err) + { + return SVN_NO_ERROR; + } + else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(apr_err))) + { + return SVN_NO_ERROR; + } + else + { + return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"), + svn_dirent_local_style(path, scratch_pool)); + } } @@ -2367,7 +2729,8 @@ svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent, if (err) { /* if the directory doesn't exist, our mission is accomplished */ - if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err)) + if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) { svn_error_clear(err); return SVN_NO_ERROR; @@ -2377,8 +2740,8 @@ svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent, 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 *name = apr_hash_this_key(hi); + const svn_io_dirent2_t *dirent = apr_hash_this_val(hi); const char *fullpath; fullpath = svn_dirent_join(path, name, subpool); @@ -2687,6 +3050,10 @@ svn_io_start_cmd3(apr_proc_t *cmd_proc, { const char *path_apr; + /* APR doesn't like our canonical path format for current directory */ + if (path[0] == '\0') + path = "."; + SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr); if (apr_err) @@ -3025,7 +3392,8 @@ svn_io_run_diff3_3(int *exitcode, 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); + args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, + SVN_VA_NULL); #ifndef NDEBUG ++nargs; #endif @@ -3416,6 +3784,100 @@ svn_io_file_seek(apr_file_t *file, apr_seek_where_t where, pool); } +svn_error_t * +svn_io_file_aligned_seek(apr_file_t *file, + apr_off_t block_size, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *scratch_pool) +{ + const apr_size_t apr_default_buffer_size = 4096; + apr_size_t file_buffer_size = apr_default_buffer_size; + apr_off_t desired_offset = 0; + apr_off_t current = 0; + apr_off_t aligned_offset = 0; + svn_boolean_t fill_buffer = FALSE; + + /* paranoia check: huge blocks on 32 bit machines may cause overflows */ + SVN_ERR_ASSERT(block_size == (apr_size_t)block_size); + + /* default for invalid block sizes */ + if (block_size == 0) + block_size = apr_default_buffer_size; + + file_buffer_size = apr_file_buffer_size_get(file); + + /* don't try to set a buffer size for non-buffered files! */ + if (file_buffer_size == 0) + { + aligned_offset = offset; + } + else if (file_buffer_size != (apr_size_t)block_size) + { + /* FILE has the wrong buffer size. correct it */ + char *buffer; + file_buffer_size = (apr_size_t)block_size; + buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size); + apr_file_buffer_set(file, buffer, file_buffer_size); + + /* seek to the start of the block and cause APR to read 1 block */ + aligned_offset = offset - (offset % block_size); + fill_buffer = TRUE; + } + else + { + aligned_offset = offset - (offset % file_buffer_size); + + /* We have no way to determine the block start of an APR file. + Furthermore, we don't want to throw away the current buffer + contents. Thus, we re-align the buffer only if the CURRENT + offset definitely lies outside the desired, aligned buffer. + This covers the typical case of linear reads getting very + close to OFFSET but reading the previous / following block. + + Note that ALIGNED_OFFSET may still be within the current + buffer and no I/O will actually happen in the FILL_BUFFER + section below. + */ + SVN_ERR(svn_io_file_seek(file, APR_CUR, ¤t, scratch_pool)); + fill_buffer = aligned_offset + file_buffer_size <= current + || current <= aligned_offset; + } + + if (fill_buffer) + { + char dummy; + apr_status_t status; + + /* seek to the start of the block and cause APR to read 1 block */ + SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset, + scratch_pool)); + status = apr_file_getc(&dummy, file); + + /* read may fail if we seek to or behind EOF. That's ok then. */ + if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status)) + return do_io_file_wrapper_cleanup(file, status, + N_("Can't read file '%s'"), + N_("Can't read stream"), + scratch_pool); + } + + /* finally, seek to the OFFSET the caller wants */ + desired_offset = offset; + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool)); + if (desired_offset != offset) + return do_io_file_wrapper_cleanup(file, APR_EOF, + N_("Can't seek in file '%s'"), + N_("Can't seek in stream"), + scratch_pool); + + /* return the buffer start that we (probably) enforced */ + if (buffer_start) + *buffer_start = aligned_offset; + + return SVN_NO_ERROR; +} + svn_error_t * svn_io_file_write(apr_file_t *file, const void *buf, @@ -3428,6 +3890,16 @@ svn_io_file_write(apr_file_t *file, const void *buf, pool)); } +svn_error_t * +svn_io_file_flush(apr_file_t *file, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(do_io_file_wrapper_cleanup( + file, apr_file_flush(file), + N_("Can't flush file '%s'"), + N_("Can't flush stream"), + scratch_pool)); +} svn_error_t * svn_io_file_write_full(apr_file_t *file, const void *buf, @@ -3493,13 +3965,81 @@ svn_io_write_unique(const char **tmp_path, err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool); if (!err) - err = svn_io_file_flush_to_disk(new_file, pool); + { + /* svn_io_file_flush_to_disk() can be very expensive, so use the + cheaper standard flush if the file is created as temporary file + anyway */ + if (delete_when == svn_io_file_del_none) + err = svn_io_file_flush_to_disk(new_file, pool); + else + err = svn_io_file_flush(new_file, pool); + } return svn_error_trace( svn_error_compose_create(err, svn_io_file_close(new_file, pool))); } +svn_error_t * +svn_io_write_atomic(const char *final_path, + const void *buf, + apr_size_t nbytes, + const char *copy_perms_path, + apr_pool_t *scratch_pool) +{ + apr_file_t *tmp_file; + const char *tmp_path; + svn_error_t *err; + const char *dirname = svn_dirent_dirname(final_path, scratch_pool); + + SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname, + svn_io_file_del_none, + scratch_pool, scratch_pool)); + + err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool); + + if (!err) + err = svn_io_file_flush_to_disk(tmp_file, scratch_pool); + + err = svn_error_compose_create(err, + svn_io_file_close(tmp_file, scratch_pool)); + + if (!err && copy_perms_path) + err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool); + + if (!err) + err = svn_io_file_rename(tmp_path, final_path, scratch_pool); + + if (err) + { + err = svn_error_compose_create(err, + svn_io_remove_file2(tmp_path, TRUE, + scratch_pool)); + + return svn_error_createf(err->apr_err, err, + _("Can't write '%s' atomically"), + svn_dirent_local_style(final_path, + scratch_pool)); + } + +#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. */ + apr_file_t *file; + + SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, + scratch_pool)); + SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); + SVN_ERR(svn_io_file_close(file, scratch_pool)); + } +#endif + + return SVN_NO_ERROR; +} svn_error_t * svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool) @@ -3726,7 +4266,13 @@ dir_make(const char *path, apr_fileperms_t perm, #endif status = apr_dir_make(path_apr, perm, pool); - WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool)); + +#ifdef WIN32 + /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a + permanent error */ + if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) + WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool)); +#endif if (status) return svn_error_wrap_apr(status, _("Can't create directory '%s'"), @@ -3740,22 +4286,26 @@ dir_make(const char *path, apr_fileperms_t perm, 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)); +#else + /* on Windows, use our wrapper so we can also set the + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */ + svn_error_t *err = + io_win_file_attrs_set(path_apr, + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + pool); + if (err) + return svn_error_createf(err->apr_err, err, + _("Can't hide directory '%s'"), + svn_dirent_local_style(path, pool)); +#endif /* WIN32 */ } -#endif +#endif /* APR_FILE_ATTR_HIDDEN */ /* Windows does not implement sgid. Skip here because retrieving the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented @@ -4173,7 +4723,6 @@ svn_io_read_version_file(int *version, } - /* Do a byte-for-byte comparison of FILE1 and FILE2. */ static svn_error_t * contents_identical_p(svn_boolean_t *identical_p, @@ -4248,7 +4797,6 @@ contents_three_identical_p(svn_boolean_t *identical_p12, 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); @@ -4258,7 +4806,6 @@ contents_three_identical_p(svn_boolean_t *identical_p12, 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)); @@ -4292,6 +4839,9 @@ contents_three_identical_p(svn_boolean_t *identical_p12, || (*identical_p23 && !eof2 && !eof3) || (*identical_p13 && !eof1 && !eof3))) { + apr_size_t bytes_read1, bytes_read2, bytes_read3; + svn_boolean_t read_1, read_2, read_3; + read_1 = read_2 = read_3 = FALSE; /* As long as a file is not at the end yet, and it is still @@ -4686,12 +5236,9 @@ svn_io_open_unique_file3(apr_file_t **file, 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); + return svn_error_quick_wrapf( + err, _("Can't set permissions on '%s'"), + svn_dirent_local_style(tempname, scratch_pool)); } } } diff --git a/contrib/subversion/subversion/libsvn_subr/iter.c b/contrib/subversion/subversion/libsvn_subr/iter.c index 45ec48925..ebccb6675 100644 --- a/contrib/subversion/subversion/libsvn_subr/iter.c +++ b/contrib/subversion/subversion/libsvn_subr/iter.c @@ -184,14 +184,8 @@ svn_iter__break(void) return &internal_break_error; } -/* Note about the type casts: apr_hash_this() does not expect a const hash - * index pointer even though it does not modify the hash index. In - * Subversion we're trying to be const-correct, so these functions all take - * a const hash index and we cast away the const when passing it down to - * APR. (A compiler may warn about casting away 'const', but at least this - * cast is explicit and gathered in one place.) */ - -const void *svn__apr_hash_index_key(const apr_hash_index_t *hi) +#if !APR_VERSION_AT_LEAST(1, 5, 0) +const void *apr_hash_this_key(apr_hash_index_t *hi) { const void *key; @@ -199,7 +193,7 @@ const void *svn__apr_hash_index_key(const apr_hash_index_t *hi) return key; } -apr_ssize_t svn__apr_hash_index_klen(const apr_hash_index_t *hi) +apr_ssize_t apr_hash_this_key_len(apr_hash_index_t *hi) { apr_ssize_t klen; @@ -207,10 +201,11 @@ apr_ssize_t svn__apr_hash_index_klen(const apr_hash_index_t *hi) return klen; } -void *svn__apr_hash_index_val(const apr_hash_index_t *hi) +void *apr_hash_this_val(apr_hash_index_t *hi) { void *val; apr_hash_this((apr_hash_index_t *)hi, NULL, NULL, &val); return val; } +#endif diff --git a/contrib/subversion/subversion/libsvn_subr/libsvn_subr.pc.in b/contrib/subversion/subversion/libsvn_subr/libsvn_subr.pc.in new file mode 100644 index 000000000..309b697bf --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/libsvn_subr.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_subr +Description: Subversion General Utility Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: sqlite3 +Libs: -L${libdir} -lsvn_subr @SVN_XML_LIBS@ @SVN_ZLIB_LIBS@ @SVN_APR_MEMCACHE_LIBS@ @SVN_MAGIC_LIBS@ @SVN_INTL_LIBS@ +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_subr/log.c b/contrib/subversion/subversion/libsvn_subr/log.c index 9e0b22a29..be6d77a80 100644 --- a/contrib/subversion/subversion/libsvn_subr/log.c +++ b/contrib/subversion/subversion/libsvn_subr/log.c @@ -36,6 +36,7 @@ #include "svn_path.h" #include "svn_pools.h" #include "svn_string.h" +#include "svn_hash.h" #include "private/svn_log.h" @@ -45,7 +46,7 @@ log_depth(svn_depth_t depth, apr_pool_t *pool) { if (depth == svn_depth_unknown) return ""; - return apr_pstrcat(pool, " depth=", svn_depth_to_word(depth), (char *)NULL); + return apr_pstrcat(pool, " depth=", svn_depth_to_word(depth), SVN_VA_NULL); } static const char * @@ -306,18 +307,18 @@ svn_log__get_file_revs(const char *path, svn_revnum_t start, svn_revnum_t end, } const char * -svn_log__lock(const apr_array_header_t *paths, +svn_log__lock(apr_hash_t *targets, svn_boolean_t steal, apr_pool_t *pool) { - int i; + apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(pool); svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool); - for (i = 0; i < paths->nelts; i++) + for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) { - const char *path = APR_ARRAY_IDX(paths, i, const char *); + const char *path = apr_hash_this_key(hi); svn_pool_clear(iterpool); - if (i != 0) + if (space_separated_paths->len) svn_stringbuf_appendcstr(space_separated_paths, " "); svn_stringbuf_appendcstr(space_separated_paths, svn_path_uri_encode(path, iterpool)); @@ -329,18 +330,18 @@ svn_log__lock(const apr_array_header_t *paths, } const char * -svn_log__unlock(const apr_array_header_t *paths, +svn_log__unlock(apr_hash_t *targets, svn_boolean_t break_lock, apr_pool_t *pool) { - int i; + apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(pool); svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool); - for (i = 0; i < paths->nelts; i++) + for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) { - const char *path = APR_ARRAY_IDX(paths, i, const char *); + const char *path = apr_hash_this_key(hi); svn_pool_clear(iterpool); - if (i != 0) + if (space_separated_paths->len) svn_stringbuf_appendcstr(space_separated_paths, " "); svn_stringbuf_appendcstr(space_separated_paths, svn_path_uri_encode(path, iterpool)); @@ -355,18 +356,18 @@ const char * svn_log__lock_one_path(const char *path, svn_boolean_t steal, apr_pool_t *pool) { - apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(path)); - APR_ARRAY_PUSH(paths, const char *) = path; - return svn_log__lock(paths, steal, pool); + apr_hash_t *paths = apr_hash_make(pool); + svn_hash_sets(paths, path, path); + return svn_log__lock(paths, steal, pool); } const char * svn_log__unlock_one_path(const char *path, svn_boolean_t break_lock, apr_pool_t *pool) { - apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(path)); - APR_ARRAY_PUSH(paths, const char *) = path; - return svn_log__unlock(paths, break_lock, pool); + apr_hash_t *paths = apr_hash_make(pool); + svn_hash_sets(paths, path, path); + return svn_log__unlock(paths, break_lock, pool); } const char * diff --git a/contrib/subversion/subversion/libsvn_subr/macos_keychain.c b/contrib/subversion/subversion/libsvn_subr/macos_keychain.c index f15324e50..8ef0c2932 100644 --- a/contrib/subversion/subversion/libsvn_subr/macos_keychain.c +++ b/contrib/subversion/subversion/libsvn_subr/macos_keychain.c @@ -32,6 +32,7 @@ #include "svn_config.h" #include "svn_user.h" +#include "auth.h" #include "private/svn_auth_private.h" #include "svn_private_config.h" @@ -241,7 +242,7 @@ static const svn_auth_provider_t keychain_ssl_client_cert_pw_provider = { /* Public API */ void -svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, +svn_auth__get_keychain_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool) { svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); @@ -251,7 +252,7 @@ svn_auth_get_keychain_simple_provider(svn_auth_provider_object_t **provider, } void -svn_auth_get_keychain_ssl_client_cert_pw_provider +svn_auth__get_keychain_ssl_client_cert_pw_provider (svn_auth_provider_object_t **provider, apr_pool_t *pool) { diff --git a/contrib/subversion/subversion/libsvn_subr/magic.c b/contrib/subversion/subversion/libsvn_subr/magic.c index 812a2636e..7f5e95dc1 100644 --- a/contrib/subversion/subversion/libsvn_subr/magic.c +++ b/contrib/subversion/subversion/libsvn_subr/magic.c @@ -33,6 +33,8 @@ #include "svn_types.h" #include "svn_pools.h" #include "svn_error.h" +#include "svn_config.h" +#include "svn_hash.h" #include "svn_private_config.h" @@ -61,14 +63,30 @@ close_magic_cookie(void *baton) } #endif -void +svn_error_t * svn_magic__init(svn_magic__cookie_t **magic_cookie, + apr_hash_t *config, apr_pool_t *result_pool) { - svn_magic__cookie_t *mc = NULL; #ifdef SVN_HAVE_LIBMAGIC + if (config) + { + svn_boolean_t enable; + svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); + + SVN_ERR(svn_config_get_bool(cfg, &enable, + SVN_CONFIG_SECTION_MISCELLANY, + SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE, + TRUE)); + if (!enable) + { + *magic_cookie = NULL; + return SVN_NO_ERROR; + } + } + mc = apr_palloc(result_pool, sizeof(*mc)); /* Initialise libmagic. */ @@ -97,6 +115,8 @@ svn_magic__init(svn_magic__cookie_t **magic_cookie, #endif *magic_cookie = mc; + + return SVN_NO_ERROR; } svn_error_t * diff --git a/contrib/subversion/subversion/libsvn_subr/md5.c b/contrib/subversion/subversion/libsvn_subr/md5.c index a707a7172..c47b4da7b 100644 --- a/contrib/subversion/subversion/libsvn_subr/md5.c +++ b/contrib/subversion/subversion/libsvn_subr/md5.c @@ -23,88 +23,36 @@ #include -#include "md5.h" + +#include "svn_checksum.h" #include "svn_md5.h" +#include "checksum.h" -/* The MD5 digest for the empty string. */ -static const unsigned char svn_md5__empty_string_digest_array[] = { - 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e -}; - -const unsigned char * -svn_md5__empty_string_digest(void) -{ - return svn_md5__empty_string_digest_array; -} - - -const char * -svn_md5__digest_to_cstring_display(const unsigned char digest[], - apr_pool_t *pool) -{ - static const char *hex = "0123456789abcdef"; - char *str = apr_palloc(pool, (APR_MD5_DIGESTSIZE * 2) + 1); - int i; - - for (i = 0; i < APR_MD5_DIGESTSIZE; i++) - { - str[i*2] = hex[digest[i] >> 4]; - str[i*2+1] = hex[digest[i] & 0x0f]; - } - str[i*2] = '\0'; - - return str; -} - - -const char * -svn_md5__digest_to_cstring(const unsigned char digest[], apr_pool_t *pool) -{ - static const unsigned char zeros_digest[APR_MD5_DIGESTSIZE] = { 0 }; - - if (memcmp(digest, zeros_digest, APR_MD5_DIGESTSIZE) != 0) - return svn_md5__digest_to_cstring_display(digest, pool); - else - return NULL; -} - - -svn_boolean_t -svn_md5__digests_match(const unsigned char d1[], const unsigned char d2[]) -{ - static const unsigned char zeros[APR_MD5_DIGESTSIZE] = { 0 }; - - return ((memcmp(d1, zeros, APR_MD5_DIGESTSIZE) == 0) - || (memcmp(d2, zeros, APR_MD5_DIGESTSIZE) == 0) - || (memcmp(d1, d2, APR_MD5_DIGESTSIZE) == 0)); -} - /* These are all deprecated, and just wrap the internal functions defined above. */ const unsigned char * svn_md5_empty_string_digest(void) { - return svn_md5__empty_string_digest(); + return svn__empty_string_digest(svn_checksum_md5); } const char * svn_md5_digest_to_cstring_display(const unsigned char digest[], apr_pool_t *pool) { - return svn_md5__digest_to_cstring_display(digest, pool); + return svn__digest_to_cstring_display(digest, APR_MD5_DIGESTSIZE, pool); } const char * svn_md5_digest_to_cstring(const unsigned char digest[], apr_pool_t *pool) { - return svn_md5__digest_to_cstring(digest, pool); + return svn__digest_to_cstring(digest, APR_MD5_DIGESTSIZE, pool); } svn_boolean_t svn_md5_digests_match(const unsigned char d1[], const unsigned char d2[]) { - return svn_md5__digests_match(d1, d2); + return svn__digests_match(d1, d2, APR_MD5_DIGESTSIZE); } diff --git a/contrib/subversion/subversion/libsvn_subr/mergeinfo.c b/contrib/subversion/subversion/libsvn_subr/mergeinfo.c index 131fe59cd..e46d67234 100644 --- a/contrib/subversion/subversion/libsvn_subr/mergeinfo.c +++ b/contrib/subversion/subversion/libsvn_subr/mergeinfo.c @@ -34,6 +34,7 @@ #include "svn_mergeinfo.h" #include "private/svn_fspath.h" #include "private/svn_mergeinfo_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_string_private.h" #include "private/svn_subr_private.h" #include "svn_private_config.h" @@ -48,7 +49,7 @@ 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 + result is inheritable. And only if both ranges are non-inheritable the result is non-inheritable. Range overlapping detection algorithm from @@ -98,9 +99,6 @@ parse_pathname(const char **input, 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. */ @@ -269,7 +267,7 @@ combine_with_lastrange(const svn_merge_range_t *new_range, { /* 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. */ + don't intersect at all we simply push NEW_RANGE onto RANGELIST. */ if (combine_ranges(&combined_range, lastrange, new_range, FALSE)) { *lastrange = combined_range; @@ -303,7 +301,7 @@ combine_with_lastrange(const svn_merge_range_t *new_range, { case svn__no_intersection: /* NEW_RANGE and *LASTRANGE *really* don't intersect so - just push NEW_RANGE only RANGELIST. */ + 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, @@ -454,8 +452,7 @@ combine_with_lastrange(const svn_merge_range_t *new_range, /* 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); + svn_sort__array(rangelist, svn_sort_compare_ranges); } } @@ -639,8 +636,7 @@ svn_rangelist__canonicalize(svn_rangelist_t *rangelist, { if (! is_rangelist_normalized(rangelist)) { - qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, - svn_sort_compare_ranges); + svn_sort__array(rangelist, svn_sort_compare_ranges); SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); } @@ -696,7 +692,11 @@ svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist, return SVN_NO_ERROR; } -/* revisionline -> PATHNAME COLON revisionlist */ +/* revisionline -> PATHNAME COLON revisionlist + * + * Parse one line of mergeinfo starting at INPUT, not reading beyond END, + * into HASH. Allocate the new entry in HASH deeply from HASH's pool. + */ static svn_error_t * parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, apr_pool_t *scratch_pool) @@ -753,12 +753,16 @@ parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, return SVN_NO_ERROR; } -/* top -> revisionline (NEWLINE revisionline)* */ +/* top -> revisionline (NEWLINE revisionline)* + * + * Parse mergeinfo starting at INPUT, not reading beyond END, into HASH. + * Allocate all the new entries in HASH deeply from HASH's pool. + */ static svn_error_t * parse_top(const char **input, const char *end, svn_mergeinfo_t hash, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { - apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); while (*input < end) { @@ -871,7 +875,7 @@ adjust_remaining_ranges(svn_rangelist_t *rangelist, ______________________________________________ | | M MODIFIED_RANGE N - | (!inhertiable) | + | (!inheritable) | |______________________________________________| | | O NEXT_RANGE P @@ -882,7 +886,7 @@ adjust_remaining_ranges(svn_rangelist_t *rangelist, _______________________________________________ | | | | M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N - | (!inhertiable) | (inheritable)| (!inheritable)| + | (!inheritable) | (inheritable)| (!inheritable)| |________________|______________|_______________| */ svn_merge_range_t *new_modified_range = @@ -892,7 +896,7 @@ adjust_remaining_ranges(svn_rangelist_t *rangelist, new_modified_range->inheritable = FALSE; modified_range->end = next_range->start; (*range_index)+=2; - svn_sort__array_insert(&new_modified_range, rangelist, + svn_sort__array_insert(rangelist, &new_modified_range, *range_index); /* Recurse with the new range. */ adjust_remaining_ranges(rangelist, range_index, result_pool); @@ -974,7 +978,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, if (res == 0) { /* Only when merging two non-inheritable ranges is the result also - non-inheritable. In all other cases ensure an inheritiable + non-inheritable. In all other cases ensure an inheritable result. */ if (range->inheritable || change->inheritable) range->inheritable = TRUE; @@ -1033,7 +1037,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, svn_merge_range_dup(range, result_pool); range_copy->end = change->start; range->start = change->start; - svn_sort__array_insert(&range_copy, rangelist, i++); + svn_sort__array_insert(rangelist, &range_copy, i++); } else { @@ -1055,7 +1059,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, into RANGELIST. */ svn_merge_range_t *change_copy = svn_merge_range_dup(change, result_pool); - svn_sort__array_insert(&change_copy, rangelist, i++); + svn_sort__array_insert(rangelist, &change_copy, i++); j++; } else if (change->end == range->start) @@ -1074,7 +1078,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, 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); + svn_sort__array_insert(rangelist, &change_copy, i); j++; } } @@ -1106,7 +1110,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, svn_merge_range_dup(change, result_pool); change_copy->end = range->start; change->start = range->start; - svn_sort__array_insert(&change_copy, rangelist, i++); + svn_sort__array_insert(rangelist, &change_copy, i++); } else { @@ -1149,7 +1153,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, range->start = change->start; range->end = change->end; range->inheritable = TRUE; - svn_sort__array_insert(&range_copy, rangelist, ++i); + svn_sort__array_insert(rangelist, &range_copy, ++i); j++; } } @@ -1167,7 +1171,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, range_copy->end = change->end; range_copy->inheritable = TRUE; range->start = change->end; - svn_sort__array_insert(&range_copy, rangelist, i++); + svn_sort__array_insert(rangelist, &range_copy, i++); j++; } } @@ -1182,7 +1186,7 @@ svn_rangelist_merge2(svn_rangelist_t *rangelist, 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); + svn_sort__array_insert(rangelist, &change_copy, rangelist->nelts); } return SVN_NO_ERROR; @@ -1273,7 +1277,7 @@ svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo, hi; hi = apr_hash_next(hi)) { - svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); if (rangelist) svn_rangelist__set_inheritance(rangelist, inheritable); @@ -1871,14 +1875,14 @@ svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, /* ### 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 + ### has non-inheritable 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); + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist1 = apr_hash_this_val(hi); svn_rangelist_t *rangelist2; svn_pool_clear(iterpool); @@ -1999,9 +2003,9 @@ svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool) for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi)) { - apr_array_header_t *rl = svn__apr_hash_index_val(hi); + apr_array_header_t *rl = apr_hash_this_val(hi); - qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges); + svn_sort__array(rl, svn_sort_compare_ranges); } return SVN_NO_ERROR; } @@ -2014,7 +2018,7 @@ svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo, 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); + apr_array_header_t *rl = apr_hash_this_val(hi); SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool)); } @@ -2033,8 +2037,8 @@ svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t 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); + const char *key = apr_hash_this_key(hi); + svn_mergeinfo_t val = apr_hash_this_val(hi); svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key), svn_mergeinfo_dup(val, pool)); @@ -2051,9 +2055,9 @@ svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool) 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); + const char *path = apr_hash_this_key(hi); + apr_ssize_t pathlen = apr_hash_this_key_len(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen, svn_rangelist_dup(rangelist, pool)); @@ -2079,9 +2083,9 @@ svn_mergeinfo_inheritable2(svn_mergeinfo_t *output, 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); + const char *key = apr_hash_this_key(hi); + apr_ssize_t keylen = apr_hash_this_key_len(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_rangelist_t *inheritable_rangelist; if (!path || svn_path_compare_paths(path, key) == 0) @@ -2120,28 +2124,24 @@ svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist, || !SVN_IS_VALID_REVNUM(end) || end < start) { + /* We want all (non-inheritable or inheritable) ranges removed. */ 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; + APR_ARRAY_PUSH(*inheritable_rangelist, svn_merge_range_t *) + = svn_merge_range_dup(range, result_pool); } } } else { - /* We want only the non-inheritable ranges bound by START - and END removed. */ + /* We want only the (non-inheritable or inheritable) ranges + bound by START and END removed. */ svn_rangelist_t *ranges_inheritable = svn_rangelist__initialize(start, end, inheritable, scratch_pool); @@ -2158,17 +2158,18 @@ svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist, svn_boolean_t svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo, - apr_pool_t *pool) + apr_pool_t *scratch_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)) + 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); + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); if (rangelist->nelts == 0) { @@ -2194,8 +2195,8 @@ svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog, 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 *original_path = apr_hash_this_key(hi); + svn_mergeinfo_t value = apr_hash_this_val(hi); const char *new_path; new_path = svn_fspath__skip_ancestor(prefix_path, original_path); @@ -2222,8 +2223,8 @@ svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_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 *original_path = apr_hash_this_key(hi); + svn_mergeinfo_t value = apr_hash_this_val(hi); if (original_path[0] == '/') original_path++; @@ -2253,8 +2254,8 @@ svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_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); + const char *fspath = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_hash_sets(*out_mergeinfo, svn_fspath__join(fspath, suffix_relpath, result_pool), @@ -2264,26 +2265,42 @@ svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo, return SVN_NO_ERROR; } -svn_rangelist_t * -svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool) +/* Deep-copy an array of pointers to simple objects. + * + * Return a duplicate in POOL of the array ARRAY of pointers to objects + * of size OBJECT_SIZE bytes. Duplicate each object bytewise. + */ +static apr_array_header_t * +ptr_array_dup(const apr_array_header_t *array, + size_t object_size, + apr_pool_t *pool) { - svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts, - sizeof(svn_merge_range_t *)); + apr_array_header_t *new_array = apr_array_make(pool, array->nelts, + sizeof(void *)); /* allocate target range buffer with a single operation */ - svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts); + char *copy = apr_palloc(pool, object_size * array->nelts); + + /* for efficiency, directly address source and target reference buffers */ + void **source = (void **)(array->elts); + void **target = (void **)(new_array->elts); int i; - /* fill it iteratively and link it into the range list */ - for (i = 0; i < rangelist->nelts; i++) + /* copy ranges iteratively and link them into the target range list */ + for (i = 0; i < array->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; + target[i] = ©[i * object_size]; + memcpy(target[i], source[i], object_size); } + new_array->nelts = array->nelts; + + return new_array; +} - return new_rl; +svn_rangelist_t * +svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool) +{ + return ptr_array_dup(rangelist, sizeof(svn_merge_range_t), pool); } svn_merge_range_t * @@ -2380,7 +2397,7 @@ svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev, for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) { - svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); if (rangelist->nelts) { @@ -2417,8 +2434,8 @@ svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat, 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); + const char *path = apr_hash_this_key(hi); + svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi); svn_mergeinfo_t filtered_mergeinfo; SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo, @@ -2462,8 +2479,8 @@ svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_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); + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); if (rangelist->nelts) { @@ -2499,8 +2516,8 @@ svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo, 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); + const char *path = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_rangelist_t *adjusted_rangelist = apr_array_make(result_pool, rangelist->nelts, sizeof(svn_merge_range_t *)); @@ -2512,15 +2529,8 @@ svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo, 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; + range->start = range->start + offset; + range->end = range->end + offset; APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) = range; } @@ -2546,7 +2556,7 @@ svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo, hi; hi = apr_hash_next(hi)) { - svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); int i; for (i = 0; i < rangelist->nelts; i++) @@ -2600,7 +2610,7 @@ svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p, continue; /* Prepend a leading slash to our path. */ - source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL); + source_path = apr_pstrcat(pool, "/", segment->path, SVN_VA_NULL); /* See if we already stored ranges for this path. If not, make a new list. */ @@ -2643,7 +2653,7 @@ svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist, hi; hi = apr_hash_next(hi)) { - svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi); + svn_rangelist_t *subtree_rangelist = apr_hash_this_val(hi); svn_pool_clear(iterpool); SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist, diff --git a/contrib/subversion/subversion/libsvn_subr/mutex.c b/contrib/subversion/subversion/libsvn_subr/mutex.c index 04988ebd0..3c348dfed 100644 --- a/contrib/subversion/subversion/libsvn_subr/mutex.c +++ b/contrib/subversion/subversion/libsvn_subr/mutex.c @@ -21,9 +21,32 @@ * ==================================================================== */ +#include + #include "svn_private_config.h" +#include "private/svn_atomic.h" #include "private/svn_mutex.h" +/* With CHECKED set to TRUE, LOCKED and OWNER must be set *after* acquiring + * the MUTEX and be reset *before* releasing it again. This is sufficient + * because we only want to check whether the current thread already holds + * the lock. And the current thread cannot be acquiring / releasing a lock + * *while* checking for recursion at the same time. + */ +struct svn_mutex__t +{ +#if APR_HAS_THREADS + + apr_thread_mutex_t *mutex; + +#else + + /* Truly empty structs are not allowed. */ + int dummy; + +#endif +}; + svn_error_t * svn_mutex__init(svn_mutex__t **mutex_p, svn_boolean_t mutex_required, @@ -33,20 +56,21 @@ svn_mutex__init(svn_mutex__t **mutex_p, strictly necessary if APR_HAS_THREADS has not been set */ *mutex_p = NULL; -#if APR_HAS_THREADS if (mutex_required) { - apr_thread_mutex_t *apr_mutex; + svn_mutex__t *mutex = apr_pcalloc(result_pool, sizeof(*mutex)); + +#if APR_HAS_THREADS apr_status_t status = - apr_thread_mutex_create(&apr_mutex, + apr_thread_mutex_create(&mutex->mutex, APR_THREAD_MUTEX_DEFAULT, result_pool); if (status) return svn_error_wrap_apr(status, _("Can't create mutex")); +#endif - *mutex_p = apr_mutex; + *mutex_p = mutex; } -#endif return SVN_NO_ERROR; } @@ -54,14 +78,14 @@ svn_mutex__init(svn_mutex__t **mutex_p, svn_error_t * svn_mutex__lock(svn_mutex__t *mutex) { -#if APR_HAS_THREADS if (mutex) { - apr_status_t status = apr_thread_mutex_lock(mutex); +#if APR_HAS_THREADS + apr_status_t status = apr_thread_mutex_lock(mutex->mutex); if (status) return svn_error_wrap_apr(status, _("Can't lock mutex")); - } #endif + } return SVN_NO_ERROR; } @@ -70,14 +94,14 @@ svn_error_t * svn_mutex__unlock(svn_mutex__t *mutex, svn_error_t *err) { -#if APR_HAS_THREADS if (mutex) { - apr_status_t status = apr_thread_mutex_unlock(mutex); +#if APR_HAS_THREADS + apr_status_t status = apr_thread_mutex_unlock(mutex->mutex); if (status && !err) return svn_error_wrap_apr(status, _("Can't unlock mutex")); - } #endif + } return err; } diff --git a/contrib/subversion/subversion/libsvn_subr/named_atomic.c b/contrib/subversion/subversion/libsvn_subr/named_atomic.c deleted file mode 100644 index cd58bbb38..000000000 --- a/contrib/subversion/subversion/libsvn_subr/named_atomic.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * svn_named_atomic.c: routines for machine-wide named atomics. - * - * ==================================================================== - * 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 "private/svn_named_atomic.h" - -#include -#include - -#include "svn_private_config.h" -#include "private/svn_atomic.h" -#include "private/svn_mutex.h" -#include "svn_pools.h" -#include "svn_dirent_uri.h" -#include "svn_io.h" - -/* Implementation aspects. - * - * We use a single shared memory block (memory mapped file) that will be - * created by the first user and merely mapped by all subsequent ones. - * The memory block contains an short header followed by a fixed-capacity - * array of named atomics. The number of entries currently in use is stored - * in the header part. - * - * Finding / creating the MMAP object as well as adding new array entries - * is being guarded by an APR global mutex. Since releasing the MMAP - * structure and closing the underlying does not affect other users of the - * same, cleanup will not be synchronized. - * - * The array is append-only. Once a process mapped the block into its - * address space, it may freely access any of the used entries. However, - * it must synchronize access to the volatile data within the entries. - * On Windows and where otherwise supported by GCC, lightweight "lock-free" - * synchronization will be used. Other targets serialize all access using - * a global mutex. - * - * Atomics will be identified by their name (a short string) and lookup - * takes linear time. But even that takes only about 10 microseconds for a - * full array scan -- which is in the same order of magnitude than e.g. a - * single global mutex lock / unlock pair. - */ - -/* Capacity of our shared memory object, i.e. max number of named atomics - * that may be created. Should have the form 2**N - 1. - */ -#define MAX_ATOMIC_COUNT 1023 - -/* We choose the size of a single named atomic object to fill a complete - * cache line (on most architectures). Thereby, we minimize the cache - * sync. overhead between different CPU cores. - */ -#define CACHE_LINE_LENGTH 64 - -/* We need 8 bytes for the actual value and the remainder is used to - * store the NUL-terminated name. - * - * Must not be smaller than SVN_NAMED_ATOMIC__MAX_NAME_LENGTH. - */ -#define MAX_NAME_LENGTH (CACHE_LINE_LENGTH - sizeof(apr_int64_t) - 1) - -/* Particle that will be appended to the namespace name to form the - * name of the mutex / lock file used for that namespace. - */ -#define MUTEX_NAME_SUFFIX ".mutex" - -/* Particle that will be appended to the namespace name to form the - * name of the shared memory file that backs that namespace. - */ -#define SHM_NAME_SUFFIX ".shm" - -/* Platform-dependent implementations of our basic atomic operations. - * NA_SYNCHRONIZE(op) will ensure that the OP gets executed atomically. - * This will be zero-overhead if OP itself is already atomic. - * - * (We don't call it SYNCHRONIZE because Windows has a preprocess macro by - * that name.) - * - * The default implementation will use the same mutex for initialization - * as well as any type of data access. This is quite expensive and we - * can do much better on most platforms. - */ -#if defined(WIN32) && ((_WIN32_WINNT >= 0x0502) || defined(InterlockedExchangeAdd64)) - -/* Interlocked API / intrinsics guarantee full data synchronization - */ -#define synched_read(mem) *mem -#define synched_write(mem, value) InterlockedExchange64(mem, value) -#define synched_add(mem, delta) InterlockedExchangeAdd64(mem, delta) -#define synched_cmpxchg(mem, value, comperand) \ - InterlockedCompareExchange64(mem, value, comperand) - -#define NA_SYNCHRONIZE(_atomic,op) op; -#define NA_SYNCHRONIZE_IS_FAST TRUE - -#elif SVN_HAS_ATOMIC_BUILTINS - -/* GCC provides atomic intrinsics for most common CPU types - */ -#define synched_read(mem) *mem -#define synched_write(mem, value) __sync_lock_test_and_set(mem, value) -#define synched_add(mem, delta) __sync_add_and_fetch(mem, delta) -#define synched_cmpxchg(mem, value, comperand) \ - __sync_val_compare_and_swap(mem, comperand, value) - -#define NA_SYNCHRONIZE(_atomic,op) op; -#define NA_SYNCHRONIZE_IS_FAST TRUE - -#else - -/* Default implementation - */ -static apr_int64_t -synched_read(volatile apr_int64_t *mem) -{ - return *mem; -} - -static apr_int64_t -synched_write(volatile apr_int64_t *mem, apr_int64_t value) -{ - apr_int64_t old_value = *mem; - *mem = value; - - return old_value; -} - -static apr_int64_t -synched_add(volatile apr_int64_t *mem, apr_int64_t delta) -{ - return *mem += delta; -} - -static apr_int64_t -synched_cmpxchg(volatile apr_int64_t *mem, - apr_int64_t value, - apr_int64_t comperand) -{ - apr_int64_t old_value = *mem; - if (old_value == comperand) - *mem = value; - - return old_value; -} - -#define NA_SYNCHRONIZE(_atomic,op)\ - do{\ - SVN_ERR(lock(_atomic->mutex));\ - op;\ - SVN_ERR(unlock(_atomic->mutex,SVN_NO_ERROR));\ - }while(0) - -#define NA_SYNCHRONIZE_IS_FAST FALSE - -#endif - -/* Structure describing a single atomic: its VALUE and NAME. - */ -struct named_atomic_data_t -{ - volatile apr_int64_t value; - char name[MAX_NAME_LENGTH + 1]; -}; - -/* Content of our shared memory buffer. COUNT is the number - * of used entries in ATOMICS. Insertion is append-only. - * PADDING is used to align the header information with the - * atomics to create a favorable data alignment. - */ -struct shared_data_t -{ - volatile apr_uint32_t count; - char padding [sizeof(struct named_atomic_data_t) - sizeof(apr_uint32_t)]; - - struct named_atomic_data_t atomics[MAX_ATOMIC_COUNT]; -}; - -/* Structure combining all objects that we need for access serialization. - */ -struct mutex_t -{ - /* Inter-process sync. is handled by through lock file. */ - apr_file_t *lock_file; - - /* Pool to be used with lock / unlock functions */ - apr_pool_t *pool; -}; - -/* API structure combining the atomic data and the access mutex - */ -struct svn_named_atomic__t -{ - /* pointer into the shared memory */ - struct named_atomic_data_t *data; - - /* sync. object; never NULL (even if unused) */ - struct mutex_t *mutex; -}; - -/* This is intended to be a singleton struct. It contains all - * information necessary to initialize and access the shared - * memory. - */ -struct svn_atomic_namespace__t -{ - /* Pointer to the shared data mapped into our process */ - struct shared_data_t *data; - - /* Last time we checked, this was the number of used - * (i.e. fully initialized) items. I.e. we can read - * their names without further sync. */ - volatile svn_atomic_t min_used; - - /* for each atomic in the shared memory, we hand out - * at most one API-level object. */ - struct svn_named_atomic__t atomics[MAX_ATOMIC_COUNT]; - - /* Synchronization object for this namespace */ - struct mutex_t mutex; -}; - -/* On most operating systems APR implements file locks per process, not - * per file. I.e. the lock file will only sync. among processes but within - * a process, we must use a mutex to sync the threads. */ -/* Compare ../libsvn_fs_fs/fs.h:SVN_FS_FS__USE_LOCK_MUTEX */ -#if APR_HAS_THREADS && !defined(WIN32) -#define USE_THREAD_MUTEX 1 -#else -#define USE_THREAD_MUTEX 0 -#endif - -/* Used for process-local thread sync. - */ -static svn_mutex__t *thread_mutex = NULL; - -#if APR_HAS_MMAP -/* Initialization flag for the above used by svn_atomic__init_once. - */ -static volatile svn_atomic_t mutex_initialized = FALSE; - -/* Initialize the thread sync. structures. - * To be called by svn_atomic__init_once. - */ -static svn_error_t * -init_thread_mutex(void *baton, apr_pool_t *pool) -{ - /* let the mutex live as long as the APR */ - apr_pool_t *global_pool = svn_pool_create(NULL); - - return svn_mutex__init(&thread_mutex, USE_THREAD_MUTEX, global_pool); -} -#endif /* APR_HAS_MMAP */ - -/* Utility that acquires our global mutex and converts error types. - */ -static svn_error_t * -lock(struct mutex_t *mutex) -{ - svn_error_t *err; - - /* Get lock on the filehandle. */ - SVN_ERR(svn_mutex__lock(thread_mutex)); - err = svn_io_lock_open_file(mutex->lock_file, TRUE, FALSE, mutex->pool); - - return err - ? svn_mutex__unlock(thread_mutex, err) - : err; -} - -/* Utility that releases the lock previously acquired via lock(). If the - * unlock succeeds and OUTER_ERR is not NULL, OUTER_ERR will be returned. - * Otherwise, return the result of the unlock operation. - */ -static svn_error_t * -unlock(struct mutex_t *mutex, svn_error_t * outer_err) -{ - svn_error_t *unlock_err - = svn_io_unlock_open_file(mutex->lock_file, mutex->pool); - return svn_mutex__unlock(thread_mutex, - svn_error_compose_create(outer_err, - unlock_err)); -} - -#if APR_HAS_MMAP -/* The last user to close a particular namespace should also remove the - * lock file. Failure to do so, however, does not affect further uses - * of the same namespace. - */ -static apr_status_t -delete_lock_file(void *arg) -{ - struct mutex_t *mutex = arg; - const char *lock_name = NULL; - - /* locks have already been cleaned up. Simply close the file */ - apr_status_t status = apr_file_close(mutex->lock_file); - - /* Remove the file from disk. This will fail if there ares still other - * users of this lock file, i.e. namespace. */ - apr_file_name_get(&lock_name, mutex->lock_file); - if (lock_name) - apr_file_remove(lock_name, mutex->pool); - - return status; -} -#endif /* APR_HAS_MMAP */ - -/* Validate the ATOMIC parameter, i.e it's address. Correct code will - * never need this but if someone should accidentally to use a NULL or - * incomplete structure, let's catch that here instead of segfaulting. - */ -static svn_error_t * -validate(svn_named_atomic__t *atomic) -{ - return atomic && atomic->data && atomic->mutex - ? SVN_NO_ERROR - : svn_error_create(SVN_ERR_BAD_ATOMIC, 0, _("Not a valid atomic")); -} - -/* Auto-initialize and return in *ATOMIC the API-level object for the - * atomic with index I within NS. */ -static void -return_atomic(svn_named_atomic__t **atomic, - svn_atomic_namespace__t *ns, - int i) -{ - *atomic = &ns->atomics[i]; - if (ns->atomics[i].data == NULL) - { - (*atomic)->mutex = &ns->mutex; - (*atomic)->data = &ns->data->atomics[i]; - } -} - -/* Implement API */ - -svn_boolean_t -svn_named_atomic__is_supported(void) -{ -#if !APR_HAS_MMAP - return FALSE; -#elif !defined(_WIN32) - return TRUE; -#else - static svn_tristate_t result = svn_tristate_unknown; - - if (result == svn_tristate_unknown) - { - /* APR SHM implementation requires the creation of global objects */ - HANDLE handle = CreateFileMappingA(INVALID_HANDLE_VALUE, - NULL, - PAGE_READONLY, - 0, - 1, - "Global\\__RandomXZY_svn"); - if (handle != NULL) - { - CloseHandle(handle); - result = svn_tristate_true; - } - else - result = svn_tristate_false; - } - - return result == svn_tristate_true; -#endif /* _WIN32 */ -} - -svn_boolean_t -svn_named_atomic__is_efficient(void) -{ - return NA_SYNCHRONIZE_IS_FAST; -} - -svn_error_t * -svn_atomic_namespace__create(svn_atomic_namespace__t **ns, - const char *name, - apr_pool_t *result_pool) -{ -#if !APR_HAS_MMAP - return svn_error_create(APR_ENOTIMPL, NULL, NULL); -#else - apr_status_t apr_err; - svn_error_t *err; - apr_file_t *file; - apr_mmap_t *mmap; - const char *shm_name, *lock_name; - apr_finfo_t finfo; - - apr_pool_t *subpool = svn_pool_create(result_pool); - - /* allocate the namespace data structure - */ - svn_atomic_namespace__t *new_ns = apr_pcalloc(result_pool, sizeof(**ns)); - - /* construct the names of the system objects that we need - */ - shm_name = apr_pstrcat(subpool, name, SHM_NAME_SUFFIX, NULL); - lock_name = apr_pstrcat(subpool, name, MUTEX_NAME_SUFFIX, NULL); - - /* initialize the lock objects - */ - SVN_ERR(svn_atomic__init_once(&mutex_initialized, init_thread_mutex, NULL, - result_pool)); - - new_ns->mutex.pool = result_pool; - SVN_ERR(svn_io_file_open(&new_ns->mutex.lock_file, lock_name, - APR_READ | APR_WRITE | APR_CREATE, - APR_OS_DEFAULT, - result_pool)); - - /* Make sure the last user of our lock file will actually remove it. - * Please note that only the last file handle begin closed will actually - * remove the underlying file (see docstring for apr_file_remove). - */ - apr_pool_cleanup_register(result_pool, &new_ns->mutex, - delete_lock_file, - apr_pool_cleanup_null); - - /* Prevent concurrent initialization. - */ - SVN_ERR(lock(&new_ns->mutex)); - - /* First, make sure that the underlying file exists. If it doesn't - * exist, create one and initialize its content. - */ - err = svn_io_file_open(&file, shm_name, - APR_READ | APR_WRITE | APR_CREATE, - APR_OS_DEFAULT, - result_pool); - if (!err) - { - err = svn_io_stat(&finfo, shm_name, APR_FINFO_SIZE, subpool); - if (!err && finfo.size < sizeof(struct shared_data_t)) - { - /* Zero all counters, values and names. - */ - struct shared_data_t initial_data; - memset(&initial_data, 0, sizeof(initial_data)); - err = svn_io_file_write_full(file, &initial_data, - sizeof(initial_data), NULL, - subpool); - } - } - - /* Now, map it into memory. - */ - if (!err) - { - apr_err = apr_mmap_create(&mmap, file, 0, sizeof(*new_ns->data), - APR_MMAP_READ | APR_MMAP_WRITE , result_pool); - if (!apr_err) - new_ns->data = mmap->mm; - else - err = svn_error_createf(apr_err, NULL, - _("MMAP failed for file '%s'"), shm_name); - } - - svn_pool_destroy(subpool); - - if (!err && new_ns->data) - { - /* Detect severe cases of corruption (i.e. when some outsider messed - * with our data file) - */ - if (new_ns->data->count > MAX_ATOMIC_COUNT) - return svn_error_create(SVN_ERR_CORRUPTED_ATOMIC_STORAGE, 0, - _("Number of atomics in namespace is too large.")); - - /* Cache the number of existing, complete entries. There can't be - * incomplete ones from other processes because we hold the mutex. - * Our process will also not access this information since we are - * either being called from within svn_atomic__init_once or by - * svn_atomic_namespace__create for a new object. - */ - new_ns->min_used = new_ns->data->count; - *ns = new_ns; - } - - /* Unlock to allow other processes may access the shared memory as well. - */ - return unlock(&new_ns->mutex, err); -#endif /* APR_HAS_MMAP */ -} - -svn_error_t * -svn_atomic_namespace__cleanup(const char *name, - apr_pool_t *pool) -{ - const char *shm_name, *lock_name; - - /* file names used for the specified namespace */ - shm_name = apr_pstrcat(pool, name, SHM_NAME_SUFFIX, NULL); - lock_name = apr_pstrcat(pool, name, MUTEX_NAME_SUFFIX, NULL); - - /* remove these files if they exist */ - SVN_ERR(svn_io_remove_file2(shm_name, TRUE, pool)); - SVN_ERR(svn_io_remove_file2(lock_name, TRUE, pool)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_named_atomic__get(svn_named_atomic__t **atomic, - svn_atomic_namespace__t *ns, - const char *name, - svn_boolean_t auto_create) -{ - apr_uint32_t i, count; - svn_error_t *error = SVN_NO_ERROR; - apr_size_t len = strlen(name); - - /* Check parameters and make sure we return a NULL atomic - * in case of failure. - */ - *atomic = NULL; - if (len > SVN_NAMED_ATOMIC__MAX_NAME_LENGTH) - return svn_error_create(SVN_ERR_BAD_ATOMIC, 0, - _("Atomic's name is too long.")); - - /* If no namespace has been provided, bail out. - */ - if (ns == NULL || ns->data == NULL) - return svn_error_create(SVN_ERR_BAD_ATOMIC, 0, - _("Namespace has not been initialized.")); - - /* Optimistic lookup. - * Because we never change the name of existing atomics and may only - * append new ones, we can safely compare the name of existing ones - * with the name that we are looking for. - */ - for (i = 0, count = svn_atomic_read(&ns->min_used); i < count; ++i) - if (strncmp(ns->data->atomics[i].name, name, len + 1) == 0) - { - return_atomic(atomic, ns, i); - return SVN_NO_ERROR; - } - - /* Try harder: - * Serialize all lookup and insert the item, if necessary and allowed. - */ - SVN_ERR(lock(&ns->mutex)); - - /* We only need to check for new entries. - */ - for (i = count; i < ns->data->count; ++i) - if (strncmp(ns->data->atomics[i].name, name, len + 1) == 0) - { - return_atomic(atomic, ns, i); - - /* Update our cached number of complete entries. */ - svn_atomic_set(&ns->min_used, ns->data->count); - - return unlock(&ns->mutex, error); - } - - /* Not found. Append a new entry, if allowed & possible. - */ - if (auto_create) - { - if (ns->data->count < MAX_ATOMIC_COUNT) - { - ns->data->atomics[ns->data->count].value = 0; - memcpy(ns->data->atomics[ns->data->count].name, - name, - len+1); - - return_atomic(atomic, ns, ns->data->count); - ++ns->data->count; - } - else - error = svn_error_create(SVN_ERR_BAD_ATOMIC, 0, - _("Out of slots for named atomic.")); - } - - /* We are mainly done here. Let others continue their work. - */ - SVN_ERR(unlock(&ns->mutex, error)); - - /* Only now can we be sure that a full memory barrier has been set - * and that the new entry has been written to memory in full. - */ - svn_atomic_set(&ns->min_used, ns->data->count); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_named_atomic__read(apr_int64_t *value, - svn_named_atomic__t *atomic) -{ - SVN_ERR(validate(atomic)); - NA_SYNCHRONIZE(atomic, *value = synched_read(&atomic->data->value)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_named_atomic__write(apr_int64_t *old_value, - apr_int64_t new_value, - svn_named_atomic__t *atomic) -{ - apr_int64_t temp; - - SVN_ERR(validate(atomic)); - NA_SYNCHRONIZE(atomic, temp = synched_write(&atomic->data->value, new_value)); - - if (old_value) - *old_value = temp; - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_named_atomic__add(apr_int64_t *new_value, - apr_int64_t delta, - svn_named_atomic__t *atomic) -{ - apr_int64_t temp; - - SVN_ERR(validate(atomic)); - NA_SYNCHRONIZE(atomic, temp = synched_add(&atomic->data->value, delta)); - - if (new_value) - *new_value = temp; - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_named_atomic__cmpxchg(apr_int64_t *old_value, - apr_int64_t new_value, - apr_int64_t comperand, - svn_named_atomic__t *atomic) -{ - apr_int64_t temp; - - SVN_ERR(validate(atomic)); - NA_SYNCHRONIZE(atomic, temp = synched_cmpxchg(&atomic->data->value, - new_value, - comperand)); - - if (old_value) - *old_value = temp; - - return SVN_NO_ERROR; -} diff --git a/contrib/subversion/subversion/libsvn_subr/nls.c b/contrib/subversion/subversion/libsvn_subr/nls.c index b026e3917..2be97a7ac 100644 --- a/contrib/subversion/subversion/libsvn_subr/nls.c +++ b/contrib/subversion/subversion/libsvn_subr/nls.c @@ -37,6 +37,8 @@ #include "svn_pools.h" #include "svn_path.h" +#include "private/svn_utf_private.h" + #include "svn_private_config.h" svn_error_t * @@ -53,69 +55,38 @@ svn_nls_init(void) { #ifdef WIN32 WCHAR ucs2_path[MAX_PATH]; - char* utf8_path; + const char* utf8_path; const char* internal_path; - apr_pool_t* pool; - apr_size_t inwords, outbytes, outlength; + apr_pool_t* scratch_pool; - apr_pool_create(&pool, 0); + scratch_pool = svn_pool_create(NULL); /* get exe name - our locale info will be in '../share/locale' */ - inwords = GetModuleFileNameW(0, ucs2_path, - sizeof(ucs2_path) / sizeof(ucs2_path[0])); - if (! inwords) + GetModuleFileNameW(NULL, ucs2_path, + sizeof(ucs2_path) / sizeof(ucs2_path[0])); + if (apr_get_os_error()) { - /* We must be on a Win9x machine, so attempt to get an ANSI path, - and convert it to Unicode. */ - CHAR ansi_path[MAX_PATH]; - - if (GetModuleFileNameA(0, ansi_path, sizeof(ansi_path))) - { - inwords = - MultiByteToWideChar(CP_ACP, 0, ansi_path, -1, ucs2_path, - sizeof(ucs2_path) / sizeof(ucs2_path[0])); - if (! inwords) - { - err = - svn_error_createf(APR_EINVAL, NULL, - _("Can't convert string to UCS-2: '%s'"), - ansi_path); - } - } - else - { - err = svn_error_create(APR_EINVAL, NULL, - _("Can't get module file name")); - } + err = svn_error_wrap_apr(apr_get_os_error(), + _("Can't get module file name")); } if (! err) - { - outbytes = outlength = 3 * (inwords + 1); - utf8_path = apr_palloc(pool, outlength); - - outbytes = WideCharToMultiByte(CP_UTF8, 0, ucs2_path, inwords, - utf8_path, outbytes, NULL, NULL); + err = svn_utf__win32_utf16_to_utf8(&utf8_path, ucs2_path, + NULL, scratch_pool); - if (outbytes == 0) - { - err = svn_error_wrap_apr(apr_get_os_error(), - _("Can't convert module path " - "to UTF-8 from UCS-2: '%s'"), - ucs2_path); - } - else - { - utf8_path[outlength - outbytes] = '\0'; - internal_path = svn_dirent_internal_style(utf8_path, pool); - /* get base path name */ - internal_path = svn_dirent_dirname(internal_path, pool); - internal_path = svn_dirent_join(internal_path, - SVN_LOCALE_RELATIVE_PATH, - pool); - bindtextdomain(PACKAGE_NAME, internal_path); - } + if (! err) + { + internal_path = svn_dirent_internal_style(utf8_path, scratch_pool); + /* get base path name */ + internal_path = svn_dirent_dirname(internal_path, scratch_pool); + internal_path = svn_dirent_join(internal_path, + SVN_LOCALE_RELATIVE_PATH, + scratch_pool); + SVN_ERR(svn_dirent_get_absolute(&internal_path, internal_path, + scratch_pool)); + bindtextdomain(PACKAGE_NAME, internal_path); } - svn_pool_destroy(pool); + + svn_pool_destroy(scratch_pool); } #else /* ! WIN32 */ bindtextdomain(PACKAGE_NAME, SVN_LOCALE_DIR); diff --git a/contrib/subversion/subversion/libsvn_subr/object_pool.c b/contrib/subversion/subversion/libsvn_subr/object_pool.c new file mode 100644 index 000000000..782ffa27f --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/object_pool.c @@ -0,0 +1,398 @@ +/* + * config_pool.c : pool of configuration objects + * + * ==================================================================== + * 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 + +#include "svn_error.h" +#include "svn_hash.h" +#include "svn_pools.h" + +#include "private/svn_atomic.h" +#include "private/svn_object_pool.h" +#include "private/svn_subr_private.h" +#include "private/svn_dep_compat.h" + + + +/* A reference counting wrapper around the user-provided object. + */ +typedef struct object_ref_t +{ + /* reference to the parent container */ + svn_object_pool__t *object_pool; + + /* identifies the bucket in OBJECT_POOL->OBJECTS in which this entry + * belongs. */ + svn_membuf_t key; + + /* User provided object. Usually a wrapper. */ + void *wrapper; + + /* private pool. This instance and its other members got allocated in it. + * Will be destroyed when this instance is cleaned up. */ + apr_pool_t *pool; + + /* Number of references to this data struct */ + volatile svn_atomic_t ref_count; +} object_ref_t; + + +/* Core data structure. All access to it must be serialized using MUTEX. + */ +struct svn_object_pool__t +{ + /* serialization object for all non-atomic data in this struct */ + svn_mutex__t *mutex; + + /* object_ref_t.KEY -> object_ref_t* mapping. + * + * In shared object mode, there is at most one such entry per key and it + * may or may not be in use. In exclusive mode, only unused references + * will be put here and they form chains if there are multiple unused + * instances for the key. */ + apr_hash_t *objects; + + /* same as objects->count but allows for non-sync'ed access */ + volatile svn_atomic_t object_count; + + /* Number of entries in OBJECTS with a reference count 0. + Due to races, this may be *temporarily* off by one or more. + Hence we must not strictly depend on it. */ + volatile svn_atomic_t unused_count; + + /* the root pool owning this structure */ + apr_pool_t *pool; + + /* extractor and updater for the user object wrappers */ + svn_object_pool__getter_t getter; + svn_object_pool__setter_t setter; +}; + + +/* Pool cleanup function for the whole object pool. + */ +static apr_status_t +object_pool_cleanup(void *baton) +{ + svn_object_pool__t *object_pool = baton; + + /* all entries must have been released up by now */ + SVN_ERR_ASSERT_NO_RETURN( object_pool->object_count + == object_pool->unused_count); + + return APR_SUCCESS; +} + +/* Remove entries from OBJECTS in OBJECT_POOL that have a ref-count of 0. + * + * Requires external serialization on OBJECT_POOL. + */ +static void +remove_unused_objects(svn_object_pool__t *object_pool) +{ + apr_pool_t *subpool = svn_pool_create(object_pool->pool); + + /* process all hash buckets */ + apr_hash_index_t *hi; + for (hi = apr_hash_first(subpool, object_pool->objects); + hi != NULL; + hi = apr_hash_next(hi)) + { + object_ref_t *object_ref = apr_hash_this_val(hi); + + /* note that we won't hand out new references while access + to the hash is serialized */ + if (svn_atomic_read(&object_ref->ref_count) == 0) + { + apr_hash_set(object_pool->objects, object_ref->key.data, + object_ref->key.size, NULL); + svn_atomic_dec(&object_pool->object_count); + svn_atomic_dec(&object_pool->unused_count); + + svn_pool_destroy(object_ref->pool); + } + } + + svn_pool_destroy(subpool); +} + +/* Cleanup function called when an object_ref_t gets released. + */ +static apr_status_t +object_ref_cleanup(void *baton) +{ + object_ref_t *object = baton; + svn_object_pool__t *object_pool = object->object_pool; + + /* If we released the last reference to object, there is one more + unused entry. + + Note that unused_count does not need to be always exact but only + needs to become exact *eventually* (we use it to check whether we + should remove unused objects every now and then). I.e. it must + never drift off / get stuck but always reflect the true value once + all threads left the racy sections. + */ + if (svn_atomic_dec(&object->ref_count) == 0) + svn_atomic_inc(&object_pool->unused_count); + + return APR_SUCCESS; +} + +/* Handle reference counting for the OBJECT_REF that the caller is about + * to return. The reference will be released when POOL gets cleaned up. + * + * Requires external serialization on OBJECT_REF->OBJECT_POOL. + */ +static void +add_object_ref(object_ref_t *object_ref, + apr_pool_t *pool) +{ + /* Update ref counter. + Note that this is racy with object_ref_cleanup; see comment there. */ + if (svn_atomic_inc(&object_ref->ref_count) == 0) + svn_atomic_dec(&object_ref->object_pool->unused_count); + + /* make sure the reference gets released automatically */ + apr_pool_cleanup_register(pool, object_ref, object_ref_cleanup, + apr_pool_cleanup_null); +} + +/* Actual implementation of svn_object_pool__lookup. + * + * Requires external serialization on OBJECT_POOL. + */ +static svn_error_t * +lookup(void **object, + svn_object_pool__t *object_pool, + svn_membuf_t *key, + void *baton, + apr_pool_t *result_pool) +{ + object_ref_t *object_ref + = apr_hash_get(object_pool->objects, key->data, key->size); + + if (object_ref) + { + *object = object_pool->getter(object_ref->wrapper, baton, result_pool); + add_object_ref(object_ref, result_pool); + } + else + { + *object = NULL; + } + + return SVN_NO_ERROR; +} + +/* Actual implementation of svn_object_pool__insert. + * + * Requires external serialization on OBJECT_POOL. + */ +static svn_error_t * +insert(void **object, + svn_object_pool__t *object_pool, + const svn_membuf_t *key, + void *wrapper, + void *baton, + apr_pool_t *wrapper_pool, + apr_pool_t *result_pool) +{ + object_ref_t *object_ref + = apr_hash_get(object_pool->objects, key->data, key->size); + if (object_ref) + { + /* entry already exists (e.g. race condition) */ + svn_error_t *err = object_pool->setter(&object_ref->wrapper, + wrapper, baton, + object_ref->pool); + if (err) + { + /* if we had an issue in the setter, then OBJECT_REF is in an + * unknown state now. Keep it around for the current users + * (i.e. don't clean the pool) but remove it from the list of + * available ones. + */ + apr_hash_set(object_pool->objects, key->data, key->size, NULL); + svn_atomic_dec(&object_pool->object_count); + + /* for the unlikely case that the object got created _and_ + * already released since we last checked: */ + if (svn_atomic_read(&object_ref->ref_count) == 0) + svn_atomic_dec(&object_pool->unused_count); + + /* cleanup the new data as well because it's not safe to use + * either. + */ + svn_pool_destroy(wrapper_pool); + + /* propagate error */ + return svn_error_trace(err); + } + + /* Destroy the new one and return a reference to the existing one + * because the existing one may already have references on it. + */ + svn_pool_destroy(wrapper_pool); + } + else + { + /* add new index entry */ + object_ref = apr_pcalloc(wrapper_pool, sizeof(*object_ref)); + object_ref->object_pool = object_pool; + object_ref->wrapper = wrapper; + object_ref->pool = wrapper_pool; + + svn_membuf__create(&object_ref->key, key->size, wrapper_pool); + object_ref->key.size = key->size; + memcpy(object_ref->key.data, key->data, key->size); + + apr_hash_set(object_pool->objects, object_ref->key.data, + object_ref->key.size, object_ref); + svn_atomic_inc(&object_pool->object_count); + + /* the new entry is *not* in use yet. + * add_object_ref will update counters again. + */ + svn_atomic_inc(&object_ref->object_pool->unused_count); + } + + /* return a reference to the object we just added */ + *object = object_pool->getter(object_ref->wrapper, baton, result_pool); + add_object_ref(object_ref, result_pool); + + /* limit memory usage */ + if (svn_atomic_read(&object_pool->unused_count) * 2 + > apr_hash_count(object_pool->objects) + 2) + remove_unused_objects(object_pool); + + return SVN_NO_ERROR; +} + +/* Implement svn_object_pool__getter_t as no-op. + */ +static void * +default_getter(void *object, + void *baton, + apr_pool_t *pool) +{ + return object; +} + +/* Implement svn_object_pool__setter_t as no-op. + */ +static svn_error_t * +default_setter(void **target, + void *source, + void *baton, + apr_pool_t *pool) +{ + return SVN_NO_ERROR; +} + + +/* API implementation */ + +svn_error_t * +svn_object_pool__create(svn_object_pool__t **object_pool, + svn_object_pool__getter_t getter, + svn_object_pool__setter_t setter, + svn_boolean_t thread_safe, + apr_pool_t *pool) +{ + svn_object_pool__t *result; + + /* construct the object pool in our private ROOT_POOL to survive POOL + * cleanup and to prevent threading issues with the allocator + */ + result = apr_pcalloc(pool, sizeof(*result)); + SVN_ERR(svn_mutex__init(&result->mutex, thread_safe, pool)); + + result->pool = pool; + result->objects = svn_hash__make(result->pool); + result->getter = getter ? getter : default_getter; + result->setter = setter ? setter : default_setter; + + /* make sure we clean up nicely. + * We need two cleanup functions of which exactly one will be run + * (disabling the respective other as the first step). If the owning + * pool does not cleaned up / destroyed explicitly, it may live longer + * than our allocator. So, we need do act upon cleanup requests from + * either side - owning_pool and root_pool. + */ + apr_pool_cleanup_register(pool, result, object_pool_cleanup, + apr_pool_cleanup_null); + + *object_pool = result; + return SVN_NO_ERROR; +} + +apr_pool_t * +svn_object_pool__new_wrapper_pool(svn_object_pool__t *object_pool) +{ + return svn_pool_create(object_pool->pool); +} + +svn_mutex__t * +svn_object_pool__mutex(svn_object_pool__t *object_pool) +{ + return object_pool->mutex; +} + +unsigned +svn_object_pool__count(svn_object_pool__t *object_pool) +{ + return svn_atomic_read(&object_pool->object_count); +} + +svn_error_t * +svn_object_pool__lookup(void **object, + svn_object_pool__t *object_pool, + svn_membuf_t *key, + void *baton, + apr_pool_t *result_pool) +{ + *object = NULL; + SVN_MUTEX__WITH_LOCK(object_pool->mutex, + lookup(object, object_pool, key, baton, result_pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_object_pool__insert(void **object, + svn_object_pool__t *object_pool, + const svn_membuf_t *key, + void *wrapper, + void *baton, + apr_pool_t *wrapper_pool, + apr_pool_t *result_pool) +{ + *object = NULL; + SVN_MUTEX__WITH_LOCK(object_pool->mutex, + insert(object, object_pool, key, wrapper, baton, + wrapper_pool, result_pool)); + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_subr/opt.c b/contrib/subversion/subversion/libsvn_subr/opt.c index d91a2ef37..facc3110a 100644 --- a/contrib/subversion/subversion/libsvn_subr/opt.c +++ b/contrib/subversion/subversion/libsvn_subr/opt.c @@ -186,7 +186,7 @@ format_option(const char **string, opts = apr_psprintf(pool, "--%s", opt->name); if (opt->has_arg) - opts = apr_pstrcat(pool, opts, _(" ARG"), (char *)NULL); + opts = apr_pstrcat(pool, opts, _(" ARG"), SVN_VA_NULL); if (doc) opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description)); @@ -783,7 +783,7 @@ svn_opt_parse_path(svn_opt_revision_t *rev, if (svn_path_is_url(path)) { /* URLs are URI-encoded, so we look for dates with - URI-encoded delimeters. */ + URI-encoded delimiters. */ size_t rev_len = strlen(rev_str); if (rev_len > 6 && rev_str[0] == '%' @@ -935,7 +935,7 @@ svn_opt__args_to_target_array(apr_array_header_t **targets_p, } } - target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); + target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL); APR_ARRAY_PUSH(output_targets, const char *) = target; } @@ -1012,13 +1012,6 @@ svn_opt__split_arg_at_peg_revision(const char **true_target, if (peg_start) { - /* Error out if target is the empty string. */ - if (ptr == utf8_target) - return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, - _("'%s' is just a peg revision. " - "Maybe try '%s@' instead?"), - utf8_target, utf8_target); - *true_target = apr_pstrmemdup(pool, utf8_target, ptr - utf8_target); if (peg_revision) *peg_revision = apr_pstrdup(pool, peg_start); diff --git a/contrib/subversion/subversion/libsvn_subr/packed_data.c b/contrib/subversion/subversion/libsvn_subr/packed_data.c new file mode 100644 index 000000000..27651ff9a --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/packed_data.c @@ -0,0 +1,1099 @@ +/* packed_data.c : implement the packed binary stream data structure + * + * ==================================================================== + * 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 + +#include "svn_string.h" +#include "svn_sorts.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_delta_private.h" +#include "private/svn_packed_data.h" + +#include "svn_private_config.h" + + + +/* Private int stream data referenced by svn_packed__int_stream_t. + */ +typedef struct packed_int_private_t +{ + /* First sub-stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *first_substream; + + /* Last sub-stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *last_substream; + + /* Current sub-stream to read from / write to, if any. NULL otherwise. + This will be initialized to FIRST_SUBSTREAM and then advanced in a + round-robin scheme after each number being read. */ + svn_packed__int_stream_t *current_substream; + + /* Number of sub-streams. */ + apr_size_t substream_count; + + /* Next (sibling) integer stream. If this is the last one, points to + the first in the list (i.e. this forms a ring list). Never NULL. */ + svn_packed__int_stream_t *next; + + /* 7b/8b encoded integer values (previously diff'ed and sign-handled, + if indicated by the flags below). The contents are disjoint from + the unparsed number buffer. May be NULL while not written to. */ + svn_stringbuf_t *packed; + + /* Initialized to 0. Latest value written to / read from PACKED. + Undefined if DIFF is FALSE. */ + apr_uint64_t last_value; + + /* Deltify data before storing it in PACKED. */ + svn_boolean_t diff; + + /* Numbers are likely to contain negative values with small absolutes. + If TRUE, store the signed bit in LSB before encoding. */ + svn_boolean_t is_signed; + + /* Number of integers in this stream. */ + apr_size_t item_count; + + /* TRUE for the last stream in a list of siblings. */ + svn_boolean_t is_last; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +} packed_int_private_t; + +/* A byte sequence stream. Please note that NEXT is defined different + * from the NEXT member in integer streams. + */ +struct svn_packed__byte_stream_t +{ + /* First sub-stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *first_substream; + + /* Last sub-stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *last_substream; + + /* Next (sibling) byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *next; + + /* Stream to store the sequence lengths. */ + svn_packed__int_stream_t *lengths_stream; + + /* It's index (relative to its parent). */ + apr_size_t lengths_stream_index; + + /* Concatenated byte sequences. */ + svn_stringbuf_t *packed; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +}; + +/* The serialization root object. It references the top-level streams. + */ +struct svn_packed__data_root_t +{ + /* First top-level integer stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *first_int_stream; + + /* Last top-level integer stream, if any. NULL otherwise. */ + svn_packed__int_stream_t *last_int_stream; + + /* Number of top-level integer streams. */ + apr_size_t int_stream_count; + + /* First top-level byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *first_byte_stream; + + /* Last top-level byte sequence stream, if any. NULL otherwise. */ + svn_packed__byte_stream_t *last_byte_stream; + + /* Number of top-level byte sequence streams. */ + apr_size_t byte_stream_count; + + /* Pool to use for allocations. */ + apr_pool_t *pool; +}; + +/* Write access. */ + +svn_packed__data_root_t * +svn_packed__data_create_root(apr_pool_t *pool) +{ + svn_packed__data_root_t *root = apr_pcalloc(pool, sizeof(*root)); + root->pool = pool; + + return root; +} + +svn_packed__int_stream_t * +svn_packed__create_int_stream(svn_packed__data_root_t *root, + svn_boolean_t diff, + svn_boolean_t signed_ints) +{ + /* allocate and initialize the stream node */ + packed_int_private_t *private_data + = apr_pcalloc(root->pool, sizeof(*private_data)); + svn_packed__int_stream_t *stream + = apr_palloc(root->pool, sizeof(*stream)); + + private_data->diff = diff; + private_data->is_signed = signed_ints; + private_data->is_last = TRUE; + private_data->pool = root->pool; + + stream->buffer_used = 0; + stream->private_data = private_data; + + /* maintain the ring list */ + if (root->last_int_stream) + { + packed_int_private_t *previous_private_data + = root->last_int_stream->private_data; + previous_private_data->next = stream; + previous_private_data->is_last = FALSE; + } + else + { + root->first_int_stream = stream; + } + + root->last_int_stream = stream; + root->int_stream_count++; + + return stream; +} + +svn_packed__int_stream_t * +svn_packed__create_int_substream(svn_packed__int_stream_t *parent, + svn_boolean_t diff, + svn_boolean_t signed_ints) +{ + packed_int_private_t *parent_private = parent->private_data; + + /* allocate and initialize the stream node */ + packed_int_private_t *private_data + = apr_pcalloc(parent_private->pool, sizeof(*private_data)); + svn_packed__int_stream_t *stream + = apr_palloc(parent_private->pool, sizeof(*stream)); + + private_data->diff = diff; + private_data->is_signed = signed_ints; + private_data->is_last = TRUE; + private_data->pool = parent_private->pool; + + stream->buffer_used = 0; + stream->private_data = private_data; + + /* maintain the ring list */ + if (parent_private->last_substream) + { + packed_int_private_t *previous_private_data + = parent_private->last_substream->private_data; + previous_private_data->next = stream; + previous_private_data->is_last = FALSE; + } + else + { + parent_private->first_substream = stream; + parent_private->current_substream = stream; + } + + parent_private->last_substream = stream; + parent_private->substream_count++; + private_data->next = parent_private->first_substream; + + return stream; +} + +/* Returns a new top-level byte sequence stream for ROOT but does not + * initialize the LENGTH_STREAM member. + */ +static svn_packed__byte_stream_t * +create_bytes_stream_body(svn_packed__data_root_t *root) +{ + svn_packed__byte_stream_t *stream + = apr_pcalloc(root->pool, sizeof(*stream)); + + stream->packed = svn_stringbuf_create_empty(root->pool); + + if (root->last_byte_stream) + root->last_byte_stream->next = stream; + else + root->first_byte_stream = stream; + + root->last_byte_stream = stream; + root->byte_stream_count++; + + return stream; +} + +svn_packed__byte_stream_t * +svn_packed__create_bytes_stream(svn_packed__data_root_t *root) +{ + svn_packed__byte_stream_t *stream + = create_bytes_stream_body(root); + + stream->lengths_stream_index = root->int_stream_count; + stream->lengths_stream = svn_packed__create_int_stream(root, FALSE, FALSE); + + return stream; +} + +/* Write the 7b/8b representation of VALUE into BUFFER. BUFFER must + * provide at least 10 bytes space. + * Returns the first position behind the written data. + */ +static unsigned char * +write_packed_uint_body(unsigned char *buffer, apr_uint64_t value) +{ + while (value >= 0x80) + { + *(buffer++) = (unsigned char)((value % 0x80) + 0x80); + value /= 0x80; + } + + *(buffer++) = (unsigned char)value; + return buffer; +} + +/* Return remapped VALUE. + * + * Due to sign conversion and diff underflow, values close to UINT64_MAX + * are almost as frequent as those close to 0. Remap them such that the + * MSB is stored in the LSB and the remainder stores the absolute distance + * to 0. + * + * This minimizes the absolute value to store in many scenarios. + * Hence, the variable-length representation on disk is shorter, too. + */ +static apr_uint64_t +remap_uint(apr_uint64_t value) +{ + return value & APR_UINT64_C(0x8000000000000000) + ? APR_UINT64_MAX - (2 * value) + : 2 * value; +} + +/* Invert remap_uint. */ +static apr_uint64_t +unmap_uint(apr_uint64_t value) +{ + return value & 1 + ? (APR_UINT64_MAX - value / 2) + : value / 2; +} + +/* Empty the unprocessed integer buffer in STREAM by either pushing the + * data to the sub-streams or writing to the packed data (in case there + * are no sub-streams). + */ +static void +svn_packed__data_flush_buffer(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t i; + + /* if we have sub-streams, push the data down to them */ + if (private_data->current_substream) + for (i = 0; i < stream->buffer_used; ++i) + { + packed_int_private_t *current_private_data + = private_data->current_substream->private_data; + + svn_packed__add_uint(private_data->current_substream, + stream->buffer[i]); + private_data->current_substream = current_private_data->next; + } + else + { + /* pack the numbers into our local PACKED buffer */ + + /* temporary buffer, max 10 bytes required per 7b/8b encoded number */ + unsigned char local_buffer[10 * SVN__PACKED_DATA_BUFFER_SIZE]; + unsigned char *p = local_buffer; + + /* if configured, deltify numbers before packing them. + Since delta may be negative, always use the 'signed' encoding. */ + if (private_data->diff) + { + apr_uint64_t last_value = private_data->last_value; + for (i = 0; i < stream->buffer_used; ++i) + { + apr_uint64_t temp = stream->buffer[i]; + stream->buffer[i] = remap_uint(temp - last_value); + last_value = temp; + } + + private_data->last_value = last_value; + } + + /* if configured and not already done by the deltification above, + transform to 'signed' encoding. Store the sign in the LSB and + the absolute value (-1 for negative values) in the remaining + 63 bits. */ + if (!private_data->diff && private_data->is_signed) + for (i = 0; i < stream->buffer_used; ++i) + stream->buffer[i] = remap_uint(stream->buffer[i]); + + /* auto-create packed data buffer. Give it some reasonable initial + size - just enough for a few tens of values. */ + if (private_data->packed == NULL) + private_data->packed + = svn_stringbuf_create_ensure(256, private_data->pool); + + /* encode numbers into our temp buffer. */ + for (i = 0; i < stream->buffer_used; ++i) + p = write_packed_uint_body(p, stream->buffer[i]); + + /* append them to the final packed data */ + svn_stringbuf_appendbytes(private_data->packed, + (char *)local_buffer, + p - local_buffer); + } + + /* maintain counters */ + private_data->item_count += stream->buffer_used; + stream->buffer_used = 0; +} + +void +svn_packed__add_uint(svn_packed__int_stream_t *stream, + apr_uint64_t value) +{ + stream->buffer[stream->buffer_used] = value; + if (++stream->buffer_used == SVN__PACKED_DATA_BUFFER_SIZE) + svn_packed__data_flush_buffer(stream); +} + +void +svn_packed__add_int(svn_packed__int_stream_t *stream, + apr_int64_t value) +{ + svn_packed__add_uint(stream, (apr_uint64_t)value); +} + +void +svn_packed__add_bytes(svn_packed__byte_stream_t *stream, + const char *data, + apr_size_t len) +{ + svn_packed__add_uint(stream->lengths_stream, len); + svn_stringbuf_appendbytes(stream->packed, data, len); +} + +/* Append the 7b/8b encoded representation of VALUE to PACKED. + */ +static void +write_packed_uint(svn_stringbuf_t* packed, apr_uint64_t value) +{ + if (value < 0x80) + { + svn_stringbuf_appendbyte(packed, (char)value); + } + else + { + unsigned char buffer[10]; + unsigned char *p = write_packed_uint_body(buffer, value); + + svn_stringbuf_appendbytes(packed, (char *)buffer, p - buffer); + } +} + +/* Recursively write the structure (config parameters, sub-streams, data + * sizes) of the STREAM and all its siblings to the TREE_STRUCT buffer. + */ +static void +write_int_stream_structure(svn_stringbuf_t* tree_struct, + svn_packed__int_stream_t* stream) +{ + while (stream) + { + /* store config parameters and number of sub-streams in 1 number */ + packed_int_private_t *private_data = stream->private_data; + write_packed_uint(tree_struct, (private_data->substream_count << 2) + + (private_data->diff ? 1 : 0) + + (private_data->is_signed ? 2 : 0)); + + /* store item count and length their of packed representation */ + svn_packed__data_flush_buffer(stream); + + write_packed_uint(tree_struct, private_data->item_count); + write_packed_uint(tree_struct, private_data->packed + ? private_data->packed->len + : 0); + + /* append all sub-stream structures */ + write_int_stream_structure(tree_struct, private_data->first_substream); + + /* continue with next sibling */ + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Recursively write the structure (sub-streams, data sizes) of the STREAM + * and all its siblings to the TREE_STRUCT buffer. + */ +static void +write_byte_stream_structure(svn_stringbuf_t* tree_struct, + svn_packed__byte_stream_t* stream) +{ + /* for this and all siblings */ + for (; stream; stream = stream->next) + { + /* this stream's structure and size */ + write_packed_uint(tree_struct, 0); + write_packed_uint(tree_struct, stream->lengths_stream_index); + write_packed_uint(tree_struct, stream->packed->len); + + /* followed by all its sub-streams */ + write_byte_stream_structure(tree_struct, stream->first_substream); + } +} + +/* Write the 7b/8b encoded representation of VALUE to STREAM. + */ +static svn_error_t * +write_stream_uint(svn_stream_t *stream, + apr_uint64_t value) +{ + unsigned char buffer[10]; + apr_size_t count = write_packed_uint_body(buffer, value) - buffer; + + SVN_ERR(svn_stream_write(stream, (char *)buffer, &count)); + + return SVN_NO_ERROR; +} + +/* Return the total size of all packed data in STREAM, its siblings and + * all sub-streams. To get an accurate value, flush all buffers prior to + * calling this function. + */ +static apr_size_t +packed_int_stream_length(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t result = private_data->packed ? private_data->packed->len : 0; + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + result += packed_int_stream_length(stream); + stream = private_data->is_last ? NULL : private_data->next; + } + + return result; +} + +/* Return the total size of all byte sequences data in STREAM, its siblings + * and all sub-streams. + */ +static apr_size_t +packed_byte_stream_length(svn_packed__byte_stream_t *stream) +{ + apr_size_t result = stream->packed->len; + + for (stream = stream->first_substream; stream; stream = stream->next) + result += packed_byte_stream_length(stream); + + return result; +} + +/* Append all packed data in STREAM, its siblings and all sub-streams to + * COMBINED. + */ +static void +append_int_stream(svn_packed__int_stream_t *stream, + svn_stringbuf_t *combined) +{ + packed_int_private_t *private_data = stream->private_data; + if (private_data->packed) + svn_stringbuf_appendstr(combined, private_data->packed); + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + append_int_stream(stream, combined); + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Append all byte sequences in STREAM, its siblings and all sub-streams + * to COMBINED. + */ +static void +append_byte_stream(svn_packed__byte_stream_t *stream, + svn_stringbuf_t *combined) +{ + svn_stringbuf_appendstr(combined, stream->packed); + + for (stream = stream->first_substream; stream; stream = stream->next) + append_byte_stream(stream, combined); +} + +/* Take the binary data in UNCOMPRESSED, zip it into COMPRESSED and write + * it to STREAM. COMPRESSED simply acts as a re-usable memory buffer. + * Clear all buffers (COMPRESSED, UNCOMPRESSED) at the end of the function. + */ +static svn_error_t * +write_stream_data(svn_stream_t *stream, + svn_stringbuf_t *uncompressed, + svn_stringbuf_t *compressed) +{ + SVN_ERR(svn__compress(uncompressed, + compressed, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); + + SVN_ERR(write_stream_uint(stream, compressed->len)); + SVN_ERR(svn_stream_write(stream, compressed->data, &compressed->len)); + + svn_stringbuf_setempty(uncompressed); + svn_stringbuf_setempty(compressed); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_packed__data_write(svn_stream_t *stream, + svn_packed__data_root_t *root, + apr_pool_t *scratch_pool) +{ + svn_packed__int_stream_t *int_stream; + svn_packed__byte_stream_t *byte_stream; + + /* re-usable data buffers */ + svn_stringbuf_t *compressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + svn_stringbuf_t *uncompressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + + /* write tree structure */ + svn_stringbuf_t *tree_struct + = svn_stringbuf_create_ensure(127, scratch_pool); + + write_packed_uint(tree_struct, root->int_stream_count); + write_int_stream_structure(tree_struct, root->first_int_stream); + + write_packed_uint(tree_struct, root->byte_stream_count); + write_byte_stream_structure(tree_struct, root->first_byte_stream); + + SVN_ERR(write_stream_uint(stream, tree_struct->len)); + SVN_ERR(svn_stream_write(stream, tree_struct->data, &tree_struct->len)); + + /* flatten sub-streams, zip them and write them to disk */ + + for (int_stream = root->first_int_stream; + int_stream; + int_stream = ((packed_int_private_t*)int_stream->private_data)->next) + { + apr_size_t len = packed_int_stream_length(int_stream); + svn_stringbuf_ensure(uncompressed, len); + + append_int_stream(int_stream, uncompressed); + SVN_ERR(write_stream_data(stream, uncompressed, compressed)); + } + + for (byte_stream = root->first_byte_stream; + byte_stream; + byte_stream = byte_stream->next) + { + apr_size_t len = packed_byte_stream_length(byte_stream); + svn_stringbuf_ensure(uncompressed, len); + + append_byte_stream(byte_stream, uncompressed); + SVN_ERR(write_stream_data(stream, uncompressed, compressed)); + } + + return SVN_NO_ERROR; +} + + +/* Read access. */ + +svn_packed__int_stream_t * +svn_packed__first_int_stream(svn_packed__data_root_t *root) +{ + return root->first_int_stream; +} + +svn_packed__byte_stream_t * +svn_packed__first_byte_stream(svn_packed__data_root_t *root) +{ + return root->first_byte_stream; +} + +svn_packed__int_stream_t * +svn_packed__next_int_stream(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->is_last ? NULL : private_data->next; +} + +svn_packed__byte_stream_t * +svn_packed__next_byte_stream(svn_packed__byte_stream_t *stream) +{ + return stream->next; +} + +svn_packed__int_stream_t * +svn_packed__first_int_substream(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->first_substream; +} + +apr_size_t +svn_packed__int_count(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + return private_data->item_count + stream->buffer_used; +} + +apr_size_t +svn_packed__byte_count(svn_packed__byte_stream_t *stream) +{ + return stream->packed->len; +} + +/* Read one 7b/8b encoded value from *P and return it in *RESULT. Returns + * the first position after the parsed data. + * + * Overflows will be detected in the sense that it will end parsing the + * input but the result is undefined. + */ +static unsigned char * +read_packed_uint_body(unsigned char *p, apr_uint64_t *result) +{ + if (*p < 0x80) + { + *result = *p; + } + else + { + apr_uint64_t shift = 0; + apr_uint64_t value = 0; + while (*p >= 0x80) + { + value += (apr_uint64_t)(*p & 0x7f) << shift; + ++p; + + shift += 7; + if (shift > 64) + { + /* a definite overflow. Note, that numbers of 65 .. 70 + bits will not be detected as an overflow as they don't + threaten to exceed the input buffer. */ + *result = 0; + return p; + } + } + + *result = value + ((apr_uint64_t)*p << shift); + } + + return ++p; +} + +/* Read one 7b/8b encoded value from STREAM and return it in *RESULT. + * + * Overflows will be detected in the sense that it will end parsing the + * input but the result is undefined. + */ +static svn_error_t * +read_stream_uint(svn_stream_t *stream, apr_uint64_t *result) +{ + apr_uint64_t shift = 0; + apr_uint64_t value = 0; + unsigned char c; + + do + { + apr_size_t len = 1; + SVN_ERR(svn_stream_read_full(stream, (char *)&c, &len)); + if (len != 1) + return svn_error_create(SVN_ERR_CORRUPT_PACKED_DATA, NULL, + _("Unexpected end of stream")); + + value += (apr_uint64_t)(c & 0x7f) << shift; + shift += 7; + if (shift > 64) + return svn_error_create(SVN_ERR_CORRUPT_PACKED_DATA, NULL, + _("Integer representation too long")); + } + while (c >= 0x80); + + *result = value; + return SVN_NO_ERROR; +} + +/* Extract and return the next integer from PACKED and make PACKED point + * to the next integer. + */ +static apr_uint64_t +read_packed_uint(svn_stringbuf_t *packed) +{ + apr_uint64_t result = 0; + unsigned char *p = (unsigned char *)packed->data; + apr_size_t read = read_packed_uint_body(p, &result) - p; + + if (read > packed->len) + read = packed->len; + + packed->data += read; + packed->blocksize -= read; + packed->len -= read; + + return result; +} + +/* Ensure that STREAM contains at least one item in its buffer. + */ +static void +svn_packed__data_fill_buffer(svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_size_t i; + apr_size_t end = MIN(SVN__PACKED_DATA_BUFFER_SIZE, + private_data->item_count); + + /* in case, some user calls us explicitly without a good reason ... */ + if (stream->buffer_used) + return; + + /* can we get data from the sub-streams or do we have to decode it from + our local packed container? */ + if (private_data->current_substream) + for (i = end; i > 0; --i) + { + packed_int_private_t *current_private_data + = private_data->current_substream->private_data; + stream->buffer[i-1] + = svn_packed__get_uint(private_data->current_substream); + private_data->current_substream = current_private_data->next; + } + else + { + /* use this local buffer only if the packed data is shorter than this. + The goal is that read_packed_uint_body doesn't need check for + overflows. */ + unsigned char local_buffer[10 * SVN__PACKED_DATA_BUFFER_SIZE]; + unsigned char *p; + unsigned char *start; + apr_size_t packed_read; + + if (private_data->packed->len < sizeof(local_buffer)) + { + apr_size_t trail = sizeof(local_buffer) - private_data->packed->len; + memcpy(local_buffer, + private_data->packed->data, + private_data->packed->len); + memset(local_buffer + private_data->packed->len, 0, MIN(trail, end)); + + p = local_buffer; + } + else + p = (unsigned char *)private_data->packed->data; + + /* unpack numbers */ + start = p; + for (i = end; i > 0; --i) + p = read_packed_uint_body(p, &stream->buffer[i-1]); + + /* adjust remaining packed data buffer */ + packed_read = p - start; + private_data->packed->data += packed_read; + private_data->packed->len -= packed_read; + private_data->packed->blocksize -= packed_read; + + /* undeltify numbers, if configured */ + if (private_data->diff) + { + apr_uint64_t last_value = private_data->last_value; + for (i = end; i > 0; --i) + { + last_value += unmap_uint(stream->buffer[i-1]); + stream->buffer[i-1] = last_value; + } + + private_data->last_value = last_value; + } + + /* handle signed values, if configured and not handled already */ + if (!private_data->diff && private_data->is_signed) + for (i = 0; i < end; ++i) + stream->buffer[i] = unmap_uint(stream->buffer[i]); + } + + stream->buffer_used = end; + private_data->item_count -= end; +} + +apr_uint64_t +svn_packed__get_uint(svn_packed__int_stream_t *stream) +{ + if (stream->buffer_used == 0) + svn_packed__data_fill_buffer(stream); + + return stream->buffer_used ? stream->buffer[--stream->buffer_used] : 0; +} + +apr_int64_t +svn_packed__get_int(svn_packed__int_stream_t *stream) +{ + return (apr_int64_t)svn_packed__get_uint(stream); +} + +const char * +svn_packed__get_bytes(svn_packed__byte_stream_t *stream, + apr_size_t *len) +{ + const char *result = stream->packed->data; + apr_size_t count = (apr_size_t)svn_packed__get_uint(stream->lengths_stream); + + if (count > stream->packed->len) + count = stream->packed->len; + + /* advance packed buffer */ + stream->packed->data += count; + stream->packed->len -= count; + stream->packed->blocksize -= count; + + *len = count; + return result; +} + +/* Read the integer stream structure and recreate it in STREAM, including + * sub-streams, from TREE_STRUCT. + */ +static void +read_int_stream_structure(svn_stringbuf_t *tree_struct, + svn_packed__int_stream_t *stream) +{ + packed_int_private_t *private_data = stream->private_data; + apr_uint64_t value = read_packed_uint(tree_struct); + apr_size_t substream_count; + apr_size_t i; + + /* extract local parameters */ + private_data->diff = (value & 1) != 0; + private_data->is_signed = (value & 2) != 0; + substream_count = (apr_size_t)(value >> 2); + + /* read item count & packed size; allocate packed data buffer */ + private_data->item_count = (apr_size_t)read_packed_uint(tree_struct); + value = read_packed_uint(tree_struct); + if (value) + { + private_data->packed = svn_stringbuf_create_ensure((apr_size_t)value, + private_data->pool); + private_data->packed->len = (apr_size_t)value; + } + + /* add sub-streams and read their config, too */ + for (i = 0; i < substream_count; ++i) + read_int_stream_structure(tree_struct, + svn_packed__create_int_substream(stream, + FALSE, + FALSE)); +} + +/* Read the integer stream structure and recreate it in STREAM, including + * sub-streams, from TREE_STRUCT. FIRST_INT_STREAM is the integer stream + * that would correspond to lengths_stream_index 0. + */ +static void +read_byte_stream_structure(svn_stringbuf_t *tree_struct, + svn_packed__byte_stream_t *stream, + svn_packed__int_stream_t *first_int_stream) +{ + apr_size_t lengths_stream_index; + apr_size_t packed_size; + apr_size_t i; + + /* read parameters from the TREE_STRUCT buffer */ + (void) (apr_size_t)read_packed_uint(tree_struct); /* discard first uint */ + lengths_stream_index = (apr_size_t)read_packed_uint(tree_struct); + packed_size = (apr_size_t)read_packed_uint(tree_struct); + + /* allocate byte sequence buffer size */ + svn_stringbuf_ensure(stream->packed, packed_size); + stream->packed->len = packed_size; + + /* navigate to the (already existing) lengths_stream */ + stream->lengths_stream_index = lengths_stream_index; + stream->lengths_stream = first_int_stream; + for (i = 0; i < lengths_stream_index; ++i) + { + packed_int_private_t *length_private + = stream->lengths_stream->private_data; + stream->lengths_stream = length_private->next; + } +} + +/* Read a compressed block from STREAM and uncompress it into UNCOMPRESSED. + * UNCOMPRESSED_LEN is the expected size of the stream. COMPRESSED is a + * re-used buffer for temporary data. + */ +static svn_error_t * +read_stream_data(svn_stream_t *stream, + apr_size_t uncompressed_len, + svn_stringbuf_t *uncompressed, + svn_stringbuf_t *compressed) +{ + apr_uint64_t len; + apr_size_t compressed_len; + + SVN_ERR(read_stream_uint(stream, &len)); + compressed_len = (apr_size_t)len; + + svn_stringbuf_ensure(compressed, compressed_len); + compressed->len = compressed_len; + SVN_ERR(svn_stream_read_full(stream, compressed->data, &compressed->len)); + compressed->data[compressed_len] = '\0'; + + SVN_ERR(svn__decompress(compressed, uncompressed, uncompressed_len)); + + return SVN_NO_ERROR; +} + +/* Read the packed contents from COMBINED, starting at *OFFSET and store + * it in STREAM. Update *OFFSET to point to the next stream's data and + * continue with the sub-streams. + */ +static void +unflatten_int_stream(svn_packed__int_stream_t *stream, + svn_stringbuf_t *combined, + apr_size_t *offset) +{ + packed_int_private_t *private_data = stream->private_data; + if (private_data->packed) + { + memcpy(private_data->packed->data, + combined->data + *offset, + private_data->packed->len); + + private_data->packed->data[private_data->packed->len] = '\0'; + *offset += private_data->packed->len; + } + + stream = private_data->first_substream; + while (stream) + { + private_data = stream->private_data; + unflatten_int_stream(stream, combined, offset); + stream = private_data->is_last ? NULL : private_data->next; + } +} + +/* Read the packed contents from COMBINED, starting at *OFFSET and store + * it in STREAM. Update *OFFSET to point to the next stream's data and + * continue with the sub-streams. + */ +static void +unflatten_byte_stream(svn_packed__byte_stream_t *stream, + svn_stringbuf_t *combined, + apr_size_t *offset) +{ + memcpy(stream->packed->data, + combined->data + *offset, + stream->packed->len); + stream->packed->data[stream->packed->len] = '\0'; + + *offset += stream->packed->len; + for (stream = stream->first_substream; stream; stream = stream->next) + unflatten_byte_stream(stream, combined, offset); +} + +svn_error_t * +svn_packed__data_read(svn_packed__data_root_t **root_p, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_uint64_t i; + apr_uint64_t count; + + svn_packed__int_stream_t *int_stream; + svn_packed__byte_stream_t *byte_stream; + svn_packed__data_root_t *root = svn_packed__data_create_root(result_pool); + + svn_stringbuf_t *compressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + svn_stringbuf_t *uncompressed + = svn_stringbuf_create_ensure(1024, scratch_pool); + + /* read tree structure */ + + apr_uint64_t tree_struct_size; + svn_stringbuf_t *tree_struct; + + SVN_ERR(read_stream_uint(stream, &tree_struct_size)); + tree_struct + = svn_stringbuf_create_ensure((apr_size_t)tree_struct_size, scratch_pool); + tree_struct->len = (apr_size_t)tree_struct_size; + + SVN_ERR(svn_stream_read_full(stream, tree_struct->data, &tree_struct->len)); + tree_struct->data[tree_struct->len] = '\0'; + + /* reconstruct tree structure */ + + count = read_packed_uint(tree_struct); + for (i = 0; i < count; ++i) + read_int_stream_structure(tree_struct, + svn_packed__create_int_stream(root, FALSE, + FALSE)); + + count = read_packed_uint(tree_struct); + for (i = 0; i < count; ++i) + read_byte_stream_structure(tree_struct, + create_bytes_stream_body(root), + root->first_int_stream); + + /* read sub-stream data from disk, unzip it and buffer it */ + + for (int_stream = root->first_int_stream; + int_stream; + int_stream = ((packed_int_private_t*)int_stream->private_data)->next) + { + apr_size_t offset = 0; + SVN_ERR(read_stream_data(stream, + packed_int_stream_length(int_stream), + uncompressed, compressed)); + unflatten_int_stream(int_stream, uncompressed, &offset); + } + + for (byte_stream = root->first_byte_stream; + byte_stream; + byte_stream = byte_stream->next) + { + apr_size_t offset = 0; + SVN_ERR(read_stream_data(stream, + packed_byte_stream_length(byte_stream), + uncompressed, compressed)); + unflatten_byte_stream(byte_stream, uncompressed, &offset); + } + + *root_p = root; + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_subr/path.c b/contrib/subversion/subversion/libsvn_subr/path.c index 84368f3bb..aaeee757a 100644 --- a/contrib/subversion/subversion/libsvn_subr/path.c +++ b/contrib/subversion/subversion/libsvn_subr/path.c @@ -1164,9 +1164,6 @@ svn_path_cstring_to_utf8(const char **path_utf8, } -/* Return a copy of PATH, allocated from POOL, for which control - characters have been escaped using the form \NNN (where NNN is the - octal representation of the byte's ordinal value). */ const char * svn_path_illegal_path_escape(const char *path, apr_pool_t *pool) { @@ -1228,8 +1225,7 @@ svn_path_check_valid(const char *path, apr_pool_t *pool) { if (svn_ctype_iscntrl(*c)) { - return svn_error_createf - (SVN_ERR_FS_PATH_SYNTAX, NULL, + 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(svn_dirent_local_style(path, pool), @@ -1256,7 +1252,7 @@ svn_path_splitext(const char **path_root, anything after it? We look for the "rightmost" period in the string. */ last_dot = strrchr(path, '.'); - if (last_dot && (last_dot + 1 != '\0')) + if (last_dot && (*(last_dot + 1) != '\0')) { /* If we have a period, we need to make sure it occurs in the final path component -- that there's no path separator @@ -1303,12 +1299,12 @@ svn_path_resolve_repos_relative_url(const char **absolute_url, _("Improper relative URL '%s'"), relative_url); - /* No assumptions are made about the canonicalization of the inut + /* No assumptions are made about the canonicalization of the input * arguments, it is presumed that the output will be canonicalized after * this function, which will remove any duplicate path separator. */ *absolute_url = apr_pstrcat(pool, repos_root_url, relative_url + 1, - (char *)NULL); + SVN_VA_NULL); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_subr/pool.c b/contrib/subversion/subversion/libsvn_subr/pool.c index 179ef7960..db57a6e9f 100644 --- a/contrib/subversion/subversion/libsvn_subr/pool.c +++ b/contrib/subversion/subversion/libsvn_subr/pool.c @@ -51,7 +51,7 @@ abort_on_pool_failure(int retcode) { /* Don't translate this string! It requires memory allocation to do so! And we don't have any of it... */ - printf("Out of memory - terminating application.\n"); + printf("libsvn: Out of memory - terminating application.\n"); abort(); return 0; /* not reached */ } diff --git a/contrib/subversion/subversion/libsvn_subr/prefix_string.c b/contrib/subversion/subversion/libsvn_subr/prefix_string.c new file mode 100644 index 000000000..fcf11bd2b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/prefix_string.c @@ -0,0 +1,315 @@ +/* prefix_string.c --- implement strings based on a prefix tree + * + * ==================================================================== + * 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 +#include "private/svn_string_private.h" + +/* A node in the tree represents a common prefix. The root node is the + * empty prefix. Nodes may have up to 256 sub-nodes, each starting with + * a different character (possibly '\0'). + * + * The nodes in the tree store only up to 8 chars of the respective common + * prefix, i.e. longer common prefixes must be drawn out over multiple + * hierarchy levels. This is a space <-> efficiency trade-off. + * + * Strings are the leaf nodes in the tree and use a specialized, smaller + * data structure. They may add 0 to 7 extra chars to the prefix. Both + * data types can be discerned by the last char in the data buffer. This + * must be 0 for strings (leaves) and non-0 otherwise. Please note that + * ordinary nodes have a length information so that no terminating 0 is + * required for them. + */ + +/* forward declaration */ +typedef struct node_t node_t; + +/* String type and tree leaf. + */ +struct svn_prefix_string__t +{ + /* mandatory prefix */ + node_t *prefix; + + /* 0 ..7 chars to add the the prefix. NUL-terminated. */ + char data[8]; +}; + +/* A node inside the tree, i.e. not a string and not a leaf (unless this is + * the root node). + * + * Note: keep the ordering to minimize size / alignment overhead on 64 bit + * machines. + */ +struct node_t +{ + /* pointer to the parent prefix plus the 1 .. 8 extra chars. + * Only the root will provide 0 extra chars. */ + svn_prefix_string__t key; + + /* Length of the prefix from the root down to and including this one. + * 0 for the root node. Only then will key.prefix be NULL. */ + apr_uint32_t length; + + /* Number of entries used in SUB_NODES. */ + apr_uint32_t sub_node_count; + + /* The sub-nodes, ordered by first char. node_t and svn_prefix_string__t + * may be mixed here. May be NULL. + * The number of allocated entries is always a power-of-two and only + * given implicitly by SUB_NODE_COUNT. */ + struct node_t **sub_nodes; +}; + +/* The actual tree structure. */ +struct svn_prefix_tree__t +{ + /* the common tree root (represents the empty prefix). */ + node_t *root; + + /* all sub-nodes & strings will be allocated from this pool */ + apr_pool_t *pool; +}; + +/* Return TRUE, iff NODE is a leaf node. + */ +static svn_boolean_t +is_leaf(node_t *node) +{ + return node->key.data[7] == 0; +} + +/* Ensure that the sub-nodes array of NODE within TREE has at least one + * unused entry. Re-allocate as necessary. + */ +static void +auto_realloc_sub_nodes(svn_prefix_tree__t *tree, + node_t *node) +{ + if (node->sub_node_count & (node->sub_node_count - 1)) + return; + + if (node->sub_node_count == 0) + { + node->sub_nodes = apr_pcalloc(tree->pool, sizeof(*node->sub_nodes)); + } + else + { + node_t **sub_nodes + = apr_pcalloc(tree->pool, + 2 * node->sub_node_count * sizeof(*sub_nodes)); + memcpy(sub_nodes, node->sub_nodes, + node->sub_node_count * sizeof(*sub_nodes)); + node->sub_nodes = sub_nodes; + } +} + +/* Given the COUNT pointers in the SUB_NODES array, return the location at + * which KEY is either located or would be inserted. + */ +static int +search_lower_bound(node_t **sub_nodes, + unsigned char key, + int count) +{ + int lower = 0; + int upper = count - 1; + + /* Binary search for the lowest position at which to insert KEY. */ + while (lower <= upper) + { + int current = lower + (upper - lower) / 2; + + if ((unsigned char)sub_nodes[current]->key.data[0] < key) + lower = current + 1; + else + upper = current - 1; + } + + return lower; +} + +svn_prefix_tree__t * +svn_prefix_tree__create(apr_pool_t *pool) +{ + svn_prefix_tree__t *tree = apr_pcalloc(pool, sizeof(*tree)); + tree->pool = pool; + + tree->root = apr_pcalloc(pool, sizeof(*tree->root)); + tree->root->key.data[7] = '\xff'; + + return tree; +} + +svn_prefix_string__t * +svn_prefix_string__create(svn_prefix_tree__t *tree, + const char *s) +{ + svn_prefix_string__t *new_string; + apr_size_t len = strlen(s); + node_t *node = tree->root; + node_t *new_node; + int idx; + + /* walk the existing tree until we either find S or the node at which S + * has to be inserted */ + while (TRUE) + { + node_t *sub_node; + int match = 1; + + /* index of the matching sub-node */ + idx = node->sub_node_count + ? search_lower_bound(node->sub_nodes, + (unsigned char)s[node->length], + node->sub_node_count) + : 0; + + /* any (partially) matching sub-nodes? */ + if (idx == (int)node->sub_node_count + || node->sub_nodes[idx]->key.data[0] != s[node->length]) + break; + + sub_node = node->sub_nodes[idx]; + + /* fully matching sub-node? */ + if (is_leaf(sub_node)) + { + if (strcmp(sub_node->key.data, s + node->length) == 0) + return &sub_node->key; + } + else + { + apr_size_t sub_node_len = sub_node->length - node->length; + if (strncmp(sub_node->key.data, s + node->length, + sub_node_len) == 0) + { + node = sub_node; + continue; + } + } + + /* partial match -> split */ + while (sub_node->key.data[match] == s[node->length + match]) + ++match; + + new_node = apr_pcalloc(tree->pool, sizeof(*new_node)); + new_node->key = sub_node->key; + new_node->length = node->length + match; + new_node->key.data[7] = '\xff'; + new_node->sub_node_count = 1; + new_node->sub_nodes = apr_palloc(tree->pool, sizeof(node_t *)); + new_node->sub_nodes[0] = sub_node; + + memmove(sub_node->key.data, sub_node->key.data + match, 8 - match); + + /* replace old sub-node with new one and continue lookup */ + sub_node->key.prefix = new_node; + node->sub_nodes[idx] = new_node; + node = new_node; + } + + /* add sub-node(s) and final string */ + while (node->length + 7 < len) + { + new_node = apr_pcalloc(tree->pool, sizeof(*new_node)); + new_node->key.prefix = node; + new_node->length = node->length + 8; + memcpy(new_node->key.data, s + node->length, 8); + + auto_realloc_sub_nodes(tree, node); + memmove(node->sub_nodes + idx + 1, node->sub_nodes + idx, + (node->sub_node_count - idx) * sizeof(node_t *)); + + /* replace old sub-node with new one and continue lookup */ + node->sub_nodes[idx] = new_node; + node->sub_node_count++; + node = new_node; + idx = 0; + } + + new_string = apr_pcalloc(tree->pool, sizeof(*new_string)); + new_string->prefix = node; + memcpy(new_string->data, s + node->length, len - node->length); + + auto_realloc_sub_nodes(tree, node); + memmove(node->sub_nodes + idx + 1, node->sub_nodes + idx, + (node->sub_node_count - idx) * sizeof(node_t *)); + + node->sub_nodes[idx] = (node_t *)new_string; + node->sub_node_count++; + return new_string; +} + +svn_string_t * +svn_prefix_string__expand(const svn_prefix_string__t *s, + apr_pool_t *pool) +{ + apr_size_t s_len = strlen(s->data); + apr_size_t len = s->prefix->length + s_len; + char *buffer = apr_palloc(pool, len + 1); + + svn_string_t *result = apr_pcalloc(pool, sizeof(*result)); + result->data = buffer; + result->len = len; + buffer[len] = '\0'; + + while (s->prefix) + { + memcpy(buffer + s->prefix->length, s->data, len - s->prefix->length); + len = s->prefix->length; + s = &s->prefix->key; + } + + return result; +} + +int +svn_prefix_string__compare(const svn_prefix_string__t *lhs, + const svn_prefix_string__t *rhs) +{ + const node_t *lhs_parent = lhs->prefix; + const node_t *rhs_parent = rhs->prefix; + + if (lhs == rhs) + return 0; + + /* find the common root */ + while (lhs_parent != rhs_parent) + { + if (lhs_parent->length <= rhs_parent->length) + { + rhs = &rhs_parent->key; + rhs_parent = rhs_parent->key.prefix; + } + else if (rhs_parent->length <= lhs_parent->length) + { + lhs = &lhs_parent->key; + lhs_parent = lhs_parent->key.prefix; + } + + /* same tree? */ + assert(lhs_parent && rhs_parent); + } + + /* at the common root, strings will differ in the first follow-up char */ + return (int)(unsigned char)lhs->data[0] - (int)(unsigned char)rhs->data[0]; +} diff --git a/contrib/subversion/subversion/libsvn_subr/prompt.c b/contrib/subversion/subversion/libsvn_subr/prompt.c index d0c29d025..8f24b4240 100644 --- a/contrib/subversion/subversion/libsvn_subr/prompt.c +++ b/contrib/subversion/subversion/libsvn_subr/prompt.c @@ -177,7 +177,7 @@ terminal_open(terminal_handle_t **terminal, svn_boolean_t noecho, and stderr for prompting. */ apr_file_t *tmpfd; status = apr_file_open(&tmpfd, "/dev/tty", - APR_READ | APR_WRITE, + APR_FOPEN_READ | APR_FOPEN_WRITE, APR_OS_DEFAULT, pool); *terminal = apr_palloc(pool, sizeof(terminal_handle_t)); if (!status) @@ -236,7 +236,6 @@ terminal_puts(const char *string, terminal_handle_t *terminal, apr_pool_t *pool) { svn_error_t *err; - apr_status_t status; const char *converted; err = svn_cmdline_cstring_from_utf8(&converted, string, pool); @@ -255,13 +254,10 @@ terminal_puts(const char *string, terminal_handle_t *terminal, } #endif - status = apr_file_write_full(terminal->outfd, converted, - strlen(converted), NULL); - if (!status) - status = apr_file_flush(terminal->outfd); - if (status) - return svn_error_wrap_apr(status, _("Can't write to terminal")); - return SVN_NO_ERROR; + SVN_ERR(svn_io_file_write_full(terminal->outfd, converted, + strlen(converted), NULL, pool)); + + return svn_error_trace(svn_io_file_flush(terminal->outfd, pool)); } /* These codes can be returned from terminal_getc instead of a character. */ @@ -835,9 +831,8 @@ plaintext_prompt_helper(svn_boolean_t *may_save_plaintext, { if (err->apr_err == SVN_ERR_CANCELLED) { - svn_error_clear(err); *may_save_plaintext = FALSE; - return SVN_NO_ERROR; + return err; } else return err; diff --git a/contrib/subversion/subversion/libsvn_subr/pseudo_md5.c b/contrib/subversion/subversion/libsvn_subr/pseudo_md5.c deleted file mode 100644 index 8c194f7dd..000000000 --- a/contrib/subversion/subversion/libsvn_subr/pseudo_md5.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * This is work is derived from material Copyright RSA Data Security, Inc. - * - * The RSA copyright statement and Licence for that original material is - * included below. This is followed by the Apache copyright statement and - * licence for the modifications made to that material. - */ - -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All - rights reserved. - - License to copy and use this software is granted provided that it - is identified as the "RSA Data Security, Inc. MD5 Message-Digest - Algorithm" in all material mentioning or referencing this software - or this function. - - License is also granted to make and use derivative works provided - that such works are identified as "derived from the RSA Data - Security, Inc. MD5 Message-Digest Algorithm" in all material - mentioning or referencing the derived work. - - RSA Data Security, Inc. makes no representations concerning either - the merchantability of this software or the suitability of this - software for any particular purpose. It is provided "as is" - without express or implied warranty of any kind. - - These notices must be retained in any copies of any part of this - documentation and/or software. - */ - -/* 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 apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 - * MD5 crypt() function, which is licenced as follows: - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - * ---------------------------------------------------------------------------- - */ - -/* - * pseudo_md5.c: md5-esque hash sum calculation for short data blocks. - * Code taken and adapted from the APR (see licenses above). - */ -#include "private/svn_pseudo_md5.h" - -/* Constants for MD5 calculation. - */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -/* F, G, H and I are basic MD5 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. - */ -#if defined(_MSC_VER) && _MSC_VER >= 1310 -#pragma intrinsic(_rotl) -#define ROTATE_LEFT(x, n) (_rotl(x,n)) -#else -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) -#endif - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (apr_uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (apr_uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (apr_uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (apr_uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - -/* The idea of the functions below is as follows: - * - * - The core MD5 algorithm does not assume that the "important" data - * is at the begin of the encryption block, followed by e.g. 0. - * Instead, all bits are equally relevant. - * - * - If some bytes in the input are known to be 0, we may hard-code them. - * With the previous property, it is safe to move them to the upper end - * of the encryption block to maximize the number of steps that can be - * pre-calculated. - * - * - Variable-length streams will use the upper 8 byte of the last - * encryption block to store the stream length in bits (to make 0, 00, - * 000, ... etc. produce different hash sums). - * - * - We will hash at most 63 bytes, i.e. 504 bits. In the standard stream - * implementation, the upper 6 bytes of the last encryption block would - * be 0. We will put at least one non-NULL value in the last 4 bytes. - * Therefore, our input will always be different to a standard MD5 stream - * implementation in either block count, content or both. - * - * - Our length indicator also varies with the number bytes in the input. - * Hence, different pseudo-MD5 input length produces different output - * (with "cryptographic probability") even if the content is all 0 or - * otherwise identical. - * - * - Collisions between pseudo-MD5 and pseudo-MD5 as well as pseudo-MD5 - * and standard MD5 are as likely as any other MD5 collision. - */ - -void svn__pseudo_md5_15(apr_uint32_t digest[4], - const apr_uint32_t x[4]) -{ - apr_uint32_t a = 0x67452301; - apr_uint32_t b = 0xefcdab89; - apr_uint32_t c = 0x98badcfe; - apr_uint32_t d = 0x10325476; - - /* make sure byte 63 gets the marker independently of BE / LE */ - apr_uint32_t x3n = x[3] ^ 0xffffffff; - - /* Round 1 */ - FF(a, b, c, d, 0, S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, 0, S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, 0, S13, 0x242070db); /* 3 */ - FF(b, c, d, a, 0, S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, 0, S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, 0, S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, 0, S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, 0, S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, 0, S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, 0, S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, 0, S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, 0, S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[0], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[1], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[2], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x3n, S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, 0, S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, 0, S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, 0, S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, 0, S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, 0, S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, 0, S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x3n, S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, 0, S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, 0, S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[2], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, 0, S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, 0, S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[1], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, 0, S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, 0, S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[0], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, 0, S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, 0, S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, 0, S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[2], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, 0, S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, 0, S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, 0, S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, 0, S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[1], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, 0, S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, 0, S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, 0, S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, 0, S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[0], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x3n, S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, 0, S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, 0, S41, 0xf4292244); /* 49 */ - II(d, a, b, c, 0, S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[2], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, 0, S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[0], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, 0, S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, 0, S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, 0, S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, 0, S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x3n, S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, 0, S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[1], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, 0, S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, 0, S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, 0, S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, 0, S44, 0xeb86d391); /* 64 */ - - digest[0] = a; - digest[1] = b; - digest[2] = c; - digest[3] = d; -} - -void svn__pseudo_md5_31(apr_uint32_t digest[4], - const apr_uint32_t x[8]) -{ - apr_uint32_t a = 0x67452301; - apr_uint32_t b = 0xefcdab89; - apr_uint32_t c = 0x98badcfe; - apr_uint32_t d = 0x10325476; - - /* make sure byte 63 gets the marker independently of BE / LE */ - apr_uint32_t x7n = x[7] ^ 0xfefefefe; - - /* Round 1 */ - FF(a, b, c, d, 0, S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, 0, S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, 0, S13, 0x242070db); /* 3 */ - FF(b, c, d, a, 0, S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, 0, S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, 0, S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, 0, S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, 0, S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, x[0], S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, x[1], S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, x[2], S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, x[3], S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[4], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[5], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[6], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x7n, S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, 0, S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, 0, S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, x[3], S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, 0, S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, 0, S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, x[2], S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x7n, S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, 0, S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, x[1], S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[6], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, 0, S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, x[0], S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[5], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, 0, S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, 0, S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[4], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, 0, S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, x[0], S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, x[3], S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[6], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, 0, S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, 0, S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, 0, S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, x[2], S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[5], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, 0, S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, 0, S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, 0, S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, x[1], S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[4], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x7n, S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, 0, S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, 0, S41, 0xf4292244); /* 49 */ - II(d, a, b, c, 0, S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[6], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, 0, S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[4], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, 0, S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, x[2], S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, 0, S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, x[0], S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x7n, S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, 0, S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[5], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, 0, S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, x[3], S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, 0, S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, x[1], S44, 0xeb86d391); /* 64 */ - - digest[0] = a; - digest[1] = b; - digest[2] = c; - digest[3] = d; -} - -void svn__pseudo_md5_63(apr_uint32_t digest[4], - const apr_uint32_t x[16]) -{ - apr_uint32_t a = 0x67452301; - apr_uint32_t b = 0xefcdab89; - apr_uint32_t c = 0x98badcfe; - apr_uint32_t d = 0x10325476; - - /* make sure byte 63 gets the marker independently of BE / LE */ - apr_uint32_t x15n = x[15] ^ 0xfcfcfcfc; - - /* Round 1 */ - FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ - FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x15n, S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x15n, S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x15n, S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ - II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x15n, S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ - - digest[0] = a; - digest[1] = b; - digest[2] = c; - digest[3] = d; -} diff --git a/contrib/subversion/subversion/libsvn_subr/root_pools.c b/contrib/subversion/subversion/libsvn_subr/root_pools.c new file mode 100644 index 000000000..12096d9f4 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/root_pools.c @@ -0,0 +1,110 @@ +/* + * root_pools.c : Implement svn_root_pools__* API + * + * ==================================================================== + * 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_pools.h" + +#include "private/svn_subr_private.h" +#include "private/svn_mutex.h" + +struct svn_root_pools__t +{ + /* unused pools. + * Use MUTEX to serialize access to this collection. + */ + apr_array_header_t *unused_pools; + + /* Mutex to serialize access to UNUSED_POOLS */ + svn_mutex__t *mutex; + +}; + +svn_error_t * +svn_root_pools__create(svn_root_pools__t **pools) +{ + /* the collection of root pools must be managed independently from + any other pool */ + apr_pool_t *pool + = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); + + /* construct result object */ + svn_root_pools__t *result = apr_pcalloc(pool, sizeof(*result)); + SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool)); + result->unused_pools = apr_array_make(pool, 16, sizeof(apr_pool_t *)); + + /* done */ + *pools = result; + + return SVN_NO_ERROR; +} + +/* Return a currently unused connection pool in *POOL. If no such pool + * exists, create a new root pool and return that in *POOL. + */ +static svn_error_t * +acquire_pool_internal(apr_pool_t **pool, + svn_root_pools__t *pools) +{ + SVN_ERR(svn_mutex__lock(pools->mutex)); + *pool = pools->unused_pools->nelts + ? *(apr_pool_t **)apr_array_pop(pools->unused_pools) + : apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); + SVN_ERR(svn_mutex__unlock(pools->mutex, SVN_NO_ERROR)); + + return SVN_NO_ERROR; +} + +apr_pool_t * +svn_root_pools__acquire_pool(svn_root_pools__t *pools) +{ + apr_pool_t *pool; + svn_error_t *err = acquire_pool_internal(&pool, pools); + if (err) + { + /* Mutex failure?! Well, try to continue with unrecycled data. */ + svn_error_clear(err); + pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); + } + + return pool; +} + +void +svn_root_pools__release_pool(apr_pool_t *pool, + svn_root_pools__t *pools) +{ + svn_error_t *err; + + svn_pool_clear(pool); + + err = svn_mutex__lock(pools->mutex); + if (err) + { + svn_error_clear(err); + svn_pool_destroy(pool); + } + else + { + APR_ARRAY_PUSH(pools->unused_pools, apr_pool_t *) = pool; + svn_error_clear(svn_mutex__unlock(pools->mutex, SVN_NO_ERROR)); + } +} diff --git a/contrib/subversion/subversion/libsvn_subr/sha1.c b/contrib/subversion/subversion/libsvn_subr/sha1.c deleted file mode 100644 index 45470cb10..000000000 --- a/contrib/subversion/subversion/libsvn_subr/sha1.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * sha1.c: SHA1 checksum routines - * - * ==================================================================== - * 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 - -#include "sha1.h" - - - -/* The SHA1 digest for the empty string. */ -static const unsigned char svn_sha1__empty_string_digest_array[] = { - 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, - 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 -}; - -const unsigned char * -svn_sha1__empty_string_digest(void) -{ - return svn_sha1__empty_string_digest_array; -} - - -const char * -svn_sha1__digest_to_cstring_display(const unsigned char digest[], - apr_pool_t *pool) -{ - static const char *hex = "0123456789abcdef"; - char *str = apr_palloc(pool, (APR_SHA1_DIGESTSIZE * 2) + 1); - int i; - - for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) - { - str[i*2] = hex[digest[i] >> 4]; - str[i*2+1] = hex[digest[i] & 0x0f]; - } - str[i*2] = '\0'; - - return str; -} - - -const char * -svn_sha1__digest_to_cstring(const unsigned char digest[], apr_pool_t *pool) -{ - static const unsigned char zeros_digest[APR_SHA1_DIGESTSIZE] = { 0 }; - - if (memcmp(digest, zeros_digest, APR_SHA1_DIGESTSIZE) != 0) - return svn_sha1__digest_to_cstring_display(digest, pool); - else - return NULL; -} - - -svn_boolean_t -svn_sha1__digests_match(const unsigned char d1[], const unsigned char d2[]) -{ - static const unsigned char zeros[APR_SHA1_DIGESTSIZE] = { 0 }; - - return ((memcmp(d1, zeros, APR_SHA1_DIGESTSIZE) == 0) - || (memcmp(d2, zeros, APR_SHA1_DIGESTSIZE) == 0) - || (memcmp(d1, d2, APR_SHA1_DIGESTSIZE) == 0)); -} diff --git a/contrib/subversion/subversion/libsvn_subr/sha1.h b/contrib/subversion/subversion/libsvn_subr/sha1.h deleted file mode 100644 index 976810b4d..000000000 --- a/contrib/subversion/subversion/libsvn_subr/sha1.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * sha1.h: Converting and comparing SHA1 checksums - * - * ==================================================================== - * 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_SUBR_SHA1_H -#define SVN_LIBSVN_SUBR_SHA1_H - -#include -#include "svn_types.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - - -/* The SHA1 digest for the empty string. */ -const unsigned char * -svn_sha1__empty_string_digest(void); - - -/* Return the hex representation of DIGEST, which must be - * APR_SHA1_DIGESTSIZE bytes long, allocating the string in POOL. - */ -const char * -svn_sha1__digest_to_cstring_display(const unsigned char digest[], - apr_pool_t *pool); - - -/* Return the hex representation of DIGEST, which must be - * APR_SHA1_DIGESTSIZE bytes long, allocating the string in POOL. - * If DIGEST is all zeros, then return NULL. - */ -const char * -svn_sha1__digest_to_cstring(const unsigned char digest[], - apr_pool_t *pool); - - -/** Compare digests D1 and D2, each APR_SHA1_DIGESTSIZE bytes long. - * If neither is all zeros, and they do not match, then return FALSE; - * else return TRUE. - */ -svn_boolean_t -svn_sha1__digests_match(const unsigned char d1[], - const unsigned char d2[]); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SVN_LIBSVN_SUBR_SHA1_H */ diff --git a/contrib/subversion/subversion/libsvn_subr/simple_providers.c b/contrib/subversion/subversion/libsvn_subr/simple_providers.c index e70770a2c..7e29be792 100644 --- a/contrib/subversion/subversion/libsvn_subr/simple_providers.c +++ b/contrib/subversion/subversion/libsvn_subr/simple_providers.c @@ -47,12 +47,6 @@ /* File provider */ /*-----------------------------------------------------------------------*/ -/* The keys that will be stored on disk. These serve the same role as - similar constants in other providers. */ -#define AUTHN_USERNAME_KEY "username" -#define AUTHN_PASSWORD_KEY "password" -#define AUTHN_PASSTYPE_KEY "passtype" - /* Baton type for the simple provider. */ typedef struct simple_provider_baton_t { @@ -81,10 +75,10 @@ svn_auth__simple_password_get(svn_boolean_t *done, *done = FALSE; - str = svn_hash_gets(creds, AUTHN_USERNAME_KEY); + str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY); if (str && username && strcmp(str->data, username) == 0) { - str = svn_hash_gets(creds, AUTHN_PASSWORD_KEY); + str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY); if (str && str->data) { *password = str->data; @@ -107,7 +101,8 @@ svn_auth__simple_password_set(svn_boolean_t *done, svn_boolean_t non_interactive, apr_pool_t *pool) { - svn_hash_sets(creds, AUTHN_PASSWORD_KEY, svn_string_create(password, pool)); + svn_hash_sets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY, + svn_string_create(password, pool)); *done = TRUE; return SVN_NO_ERROR; @@ -122,7 +117,7 @@ simple_username_get(const char **username, svn_boolean_t non_interactive) { svn_string_t *str; - str = svn_hash_gets(creds, AUTHN_USERNAME_KEY); + str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY); if (str && str->data) { *username = str->data; @@ -184,7 +179,7 @@ svn_auth__simple_creds_cache_get(void **credentials, /* The password type in the auth data must match the mangler's type, otherwise the password must be interpreted by another provider. */ - str = svn_hash_gets(creds_hash, AUTHN_PASSTYPE_KEY); + str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY); if (str && str->data) if (passtype && (0 == strcmp(str->data, passtype))) have_passtype = TRUE; @@ -333,7 +328,7 @@ svn_auth__simple_creds_cache_set(svn_boolean_t *saved, /* Put the username into the credentials hash. */ creds_hash = apr_hash_make(pool); - svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY, svn_string_create(creds->username, pool)); /* Don't store passwords in any form if the user has told @@ -461,7 +456,7 @@ svn_auth__simple_creds_cache_set(svn_boolean_t *saved, if (*saved && passtype) /* Store the password type with the auth data, so that we know which provider owns the password. */ - svn_hash_sets(creds_hash, AUTHN_PASSTYPE_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY, svn_string_create(passtype, pool)); } } @@ -600,7 +595,7 @@ prompt_for_simple_creds(svn_auth_cred_simple_t **cred_p, svn_error_clear(err); if (! err && creds_hash) { - str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY); + str = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY); if (str && str->data) default_username = str->data; } diff --git a/contrib/subversion/subversion/libsvn_subr/sorts.c b/contrib/subversion/subversion/libsvn_subr/sorts.c index bdec8e47d..06a4964e1 100644 --- a/contrib/subversion/subversion/libsvn_subr/sorts.c +++ b/contrib/subversion/subversion/libsvn_subr/sorts.c @@ -32,6 +32,7 @@ #include "svn_path.h" #include "svn_sorts.h" #include "svn_error.h" +#include "private/svn_sorts_private.h" @@ -133,6 +134,14 @@ svn_sort_compare_ranges(const void *a, const void *b) return item1->start < item2->start ? -1 : 1; } +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); +} + apr_array_header_t * svn_sort__hash(apr_hash_t *ht, int (*comparison_func)(const svn_sort__item_t *, @@ -171,7 +180,7 @@ svn_sort__hash(apr_hash_t *ht, /* quicksort the array if it isn't already sorted. */ if (!sorted) - qsort(ary->elts, ary->nelts, ary->elt_size, + svn_sort__array(ary, (int (*)(const void *, const void *))comparison_func); return ary; @@ -213,8 +222,8 @@ bsearch_lower_bound(const void *key, } int -svn_sort__bsearch_lower_bound(const void *key, - const apr_array_header_t *array, +svn_sort__bsearch_lower_bound(const apr_array_header_t *array, + const void *key, int (*compare_func)(const void *, const void *)) { return bsearch_lower_bound(key, @@ -222,9 +231,77 @@ svn_sort__bsearch_lower_bound(const void *key, compare_func); } +void * +svn_sort__array_lookup(const apr_array_header_t *array, + const void *key, + int *hint, + int (*compare_func)(const void *, const void *)) +{ + void *result; + int idx; + + /* If provided, try the index following *HINT (i.e. probably the last + * hit location) first. This speeds up linear scans. */ + if (hint) + { + /* We intend to insert right behind *HINT. + * Exit this function early, if we actually can. */ + idx = *hint + 1; + if (idx >= array->nelts) + { + /* We intend to insert after the last entry. + * That is only allowed if that last entry is smaller than KEY. + * In that case, there will be no current entry, i.e. we must + * return NULL. */ + apr_size_t offset; + + *hint = array->nelts; + if (array->nelts == 0) + return NULL; + + offset = (array->nelts - 1) * array->elt_size; + if (compare_func(array->elts + offset, key) < 0) + return NULL; + } + else if (idx > 0) + { + /* Intend to insert at a position inside the array, i.e. not + * at one of the boundaries. The predecessor must be smaller + * and the current entry at IDX must be larger than KEY. */ + void *previous; + + *hint = idx; + previous = array->elts + (idx-1) * array->elt_size; + result = array->elts + idx * array->elt_size; + if (compare_func(previous, key) && !compare_func(result, key)) + return result; + } + else if (idx <= 0) + { + /* Intend to insert at the beginning of an non-empty array. + * That requires the first entry to be larger than KEY. */ + *hint = 0; + if (!compare_func(array->elts, key)) + return array->elts; + } + + /* The HINT did not help. */ + } + + idx = bsearch_lower_bound(key, array->elts, array->nelts, array->elt_size, + compare_func); + if (hint) + *hint = idx; + if (idx >= array->nelts) + return NULL; + + result = array->elts + idx * array->elt_size; + return compare_func(result, key) ? NULL : result; +} + void -svn_sort__array_insert(const void *new_element, - apr_array_header_t *array, +svn_sort__array_insert(apr_array_header_t *array, + const void *new_element, int insert_index) { int elements_to_move; @@ -307,3 +384,149 @@ svn_sort__array_reverse(apr_array_header_t *array, } } } + +/* Our priority queue data structure: + * Simply remember the constructor parameters. + */ +struct svn_priority_queue__t +{ + /* the queue elements, ordered as a heap according to COMPARE_FUNC */ + apr_array_header_t *elements; + + /* predicate used to order the heap */ + int (*compare_func)(const void *, const void *); +}; + +/* Return TRUE, if heap element number LHS in QUEUE is smaller than element + * number RHS according to QUEUE->COMPARE_FUNC + */ +static int +heap_is_less(svn_priority_queue__t *queue, + apr_size_t lhs, + apr_size_t rhs) +{ + char *lhs_value = queue->elements->elts + lhs * queue->elements->elt_size; + char *rhs_value = queue->elements->elts + rhs * queue->elements->elt_size; + + /* nelts is never negative */ + assert(lhs < (apr_size_t)queue->elements->nelts); + assert(rhs < (apr_size_t)queue->elements->nelts); + return queue->compare_func(lhs_value, rhs_value) < 0; +} + +/* Exchange elements number LHS and RHS in QUEUE. + */ +static void +heap_swap(svn_priority_queue__t *queue, + apr_size_t lhs, + apr_size_t rhs) +{ + int i; + char *lhs_value = queue->elements->elts + lhs * queue->elements->elt_size; + char *rhs_value = queue->elements->elts + rhs * queue->elements->elt_size; + + for (i = 0; i < queue->elements->elt_size; ++i) + { + char temp = lhs_value[i]; + lhs_value[i] = rhs_value[i]; + rhs_value[i] = temp; + } +} + +/* Move element number IDX to lower indexes until the heap criterion is + * fulfilled again. + */ +static void +heap_bubble_down(svn_priority_queue__t *queue, + int idx) +{ + while (idx > 0 && heap_is_less(queue, idx, (idx - 1) / 2)) + { + heap_swap(queue, idx, (idx - 1) / 2); + idx = (idx - 1) / 2; + } +} + +/* Move element number IDX to higher indexes until the heap criterion is + * fulfilled again. + */ +static void +heap_bubble_up(svn_priority_queue__t *queue, + int idx) +{ + while (2 * idx + 2 < queue->elements->nelts) + { + int child = heap_is_less(queue, 2 * idx + 1, 2 * idx + 2) + ? 2 * idx + 1 + : 2 * idx + 2; + + if (heap_is_less(queue, idx, child)) + return; + + heap_swap(queue, idx, child); + idx = child; + } + + if ( 2 * idx + 1 < queue->elements->nelts + && heap_is_less(queue, 2 * idx + 1, idx)) + heap_swap(queue, 2 * idx + 1, idx); +} + +svn_priority_queue__t * +svn_priority_queue__create(apr_array_header_t *elements, + int (*compare_func)(const void *, const void *)) +{ + int i; + + svn_priority_queue__t *queue = apr_pcalloc(elements->pool, sizeof(*queue)); + queue->elements = elements; + queue->compare_func = compare_func; + + for (i = elements->nelts / 2; i >= 0; --i) + heap_bubble_up(queue, i); + + return queue; +} + +apr_size_t +svn_priority_queue__size(svn_priority_queue__t *queue) +{ + return queue->elements->nelts; +} + +void * +svn_priority_queue__peek(svn_priority_queue__t *queue) +{ + return queue->elements->nelts ? queue->elements->elts : NULL; +} + +void +svn_priority_queue__update(svn_priority_queue__t *queue) +{ + heap_bubble_up(queue, 0); +} + +void +svn_priority_queue__pop(svn_priority_queue__t *queue) +{ + if (queue->elements->nelts) + { + memmove(queue->elements->elts, + queue->elements->elts + + (queue->elements->nelts - 1) * queue->elements->elt_size, + queue->elements->elt_size); + --queue->elements->nelts; + heap_bubble_up(queue, 0); + } +} + +void +svn_priority_queue__push(svn_priority_queue__t *queue, + const void *element) +{ + /* we cannot duplicate elements due to potential array re-allocs */ + assert(element && element != queue->elements->elts); + + memcpy(apr_array_push(queue->elements), element, queue->elements->elt_size); + heap_bubble_down(queue, queue->elements->nelts - 1); +} diff --git a/contrib/subversion/subversion/libsvn_subr/spillbuf.c b/contrib/subversion/subversion/libsvn_subr/spillbuf.c index e02874162..6b8d15b58 100644 --- a/contrib/subversion/subversion/libsvn_subr/spillbuf.c +++ b/contrib/subversion/subversion/libsvn_subr/spillbuf.c @@ -73,12 +73,26 @@ struct svn_spillbuf_t { /* How much content remains in SPILL. */ svn_filesize_t spill_size; + + /* When false, do not delete the spill file when it is closed. */ + svn_boolean_t delete_on_close; + + /* When true, and the amount of data written to the spillbuf is + larger than MAXSIZE, all spillbuf contents will be written to the + spill file. */ + svn_boolean_t spill_all_contents; + + /* The directory in which the spill file is created. */ + const char *dirpath; + + /* The name of the temporary spill file. */ + const char *filename; }; struct svn_spillbuf_reader_t { /* Embed the spill-buffer within the reader. */ - struct svn_spillbuf_t buf; + struct svn_spillbuf_t *buf; /* When we read content from the underlying spillbuf, these fields store the ptr/len pair. The ptr will be incremented as we "read" out of this @@ -99,28 +113,86 @@ struct svn_spillbuf_reader_t { }; +/* Extended spillbuf initialization. */ +static void +init_spillbuf_extended(svn_spillbuf_t *buf, + apr_size_t blocksize, + apr_size_t maxsize, + svn_boolean_t delete_on_close, + svn_boolean_t spill_all_contents, + const char *dirpath, + apr_pool_t *result_pool) +{ + buf->pool = result_pool; + buf->blocksize = blocksize; + buf->maxsize = maxsize; + buf->delete_on_close = delete_on_close; + buf->spill_all_contents = spill_all_contents; + buf->dirpath = dirpath; +} + +/* Common constructor for initializing spillbufs. + Used by svn_spillbuf__create, svn_spilbuff__reader_create. */ +static void +init_spillbuf(svn_spillbuf_t *buf, + apr_size_t blocksize, + apr_size_t maxsize, + apr_pool_t *result_pool) +{ + init_spillbuf_extended(buf, blocksize, maxsize, + TRUE, FALSE, NULL, + result_pool); +} + svn_spillbuf_t * svn_spillbuf__create(apr_size_t blocksize, apr_size_t maxsize, apr_pool_t *result_pool) { svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf)); + init_spillbuf(buf, blocksize, maxsize, result_pool); + return buf; +} - buf->pool = result_pool; - buf->blocksize = blocksize; - buf->maxsize = maxsize; - /* Note: changes here should also go into svn_spillbuf__reader_create() */ +svn_spillbuf_t * +svn_spillbuf__create_extended(apr_size_t blocksize, + apr_size_t maxsize, + svn_boolean_t delete_on_close, + svn_boolean_t spill_all_contents, + const char *dirpath, + apr_pool_t *result_pool) +{ + svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf)); + init_spillbuf_extended(buf, blocksize, maxsize, + delete_on_close, spill_all_contents, dirpath, + result_pool); return buf; } - svn_filesize_t svn_spillbuf__get_size(const svn_spillbuf_t *buf) { return buf->memory_size + buf->spill_size; } +svn_filesize_t +svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf) +{ + return buf->memory_size; +} + +const char * +svn_spillbuf__get_filename(const svn_spillbuf_t *buf) +{ + return buf->filename; +} + +apr_file_t * +svn_spillbuf__get_file(const svn_spillbuf_t *buf) +{ + return buf->spill; +} /* Get a memblock from the spill-buffer. It will be the block that we passed out for reading, come from the free list, or allocated. */ @@ -173,10 +245,32 @@ svn_spillbuf__write(svn_spillbuf_t *buf, && (buf->memory_size + len) > buf->maxsize) { SVN_ERR(svn_io_open_unique_file3(&buf->spill, - NULL /* temp_path */, - NULL /* dirpath */, - svn_io_file_del_on_close, + &buf->filename, + buf->dirpath, + (buf->delete_on_close + ? svn_io_file_del_on_close + : svn_io_file_del_none), buf->pool, scratch_pool)); + + /* Optionally write the memory contents into the file. */ + if (buf->spill_all_contents) + { + mem = buf->head; + while (mem != NULL) + { + SVN_ERR(svn_io_file_write_full(buf->spill, mem->data, mem->size, + NULL, scratch_pool)); + mem = mem->next; + } + + /* Adjust the start offset for reading from the spill file. + + ### FIXME: Instead, we should simply discard the memory + buffers; but currently some tests expect to read data in + the same chunk sizes as were written, so we'll leave this + change for later.*/ + buf->spill_start = buf->memory_size; + } } /* Once a spill file has been constructed, then we need to put all @@ -440,16 +534,10 @@ svn_spillbuf__reader_create(apr_size_t blocksize, apr_pool_t *result_pool) { svn_spillbuf_reader_t *sbr = apr_pcalloc(result_pool, sizeof(*sbr)); - - /* See svn_spillbuf__create() */ - sbr->buf.pool = result_pool; - sbr->buf.blocksize = blocksize; - sbr->buf.maxsize = maxsize; - + sbr->buf = svn_spillbuf__create(blocksize, maxsize, result_pool); return sbr; } - svn_error_t * svn_spillbuf__reader_read(apr_size_t *amt, svn_spillbuf_reader_t *reader, @@ -488,7 +576,7 @@ svn_spillbuf__reader_read(apr_size_t *amt, if (reader->sb_len == 0) { SVN_ERR(svn_spillbuf__read(&reader->sb_ptr, &reader->sb_len, - &reader->buf, + reader->buf, scratch_pool)); /* We've run out of content, so return with whatever has @@ -547,7 +635,8 @@ svn_spillbuf__reader_write(svn_spillbuf_reader_t *reader, if (reader->sb_len > 0) { if (reader->save_ptr == NULL) - reader->save_ptr = apr_palloc(reader->buf.pool, reader->buf.blocksize); + reader->save_ptr = apr_palloc(reader->buf->pool, + reader->buf->blocksize); memcpy(reader->save_ptr, reader->sb_ptr, reader->sb_len); reader->save_len = reader->sb_len; @@ -557,7 +646,7 @@ svn_spillbuf__reader_write(svn_spillbuf_reader_t *reader, reader->sb_len = 0; } - return svn_error_trace(svn_spillbuf__write(&reader->buf, data, len, + return svn_error_trace(svn_spillbuf__write(reader->buf, data, len, scratch_pool)); } @@ -596,19 +685,20 @@ write_handler_spillbuf(void *baton, const char *data, apr_size_t *len) svn_stream_t * -svn_stream__from_spillbuf(apr_size_t blocksize, - apr_size_t maxsize, +svn_stream__from_spillbuf(svn_spillbuf_t *buf, apr_pool_t *result_pool) { svn_stream_t *stream; struct spillbuf_baton *sb = apr_palloc(result_pool, sizeof(*sb)); - sb->reader = svn_spillbuf__reader_create(blocksize, maxsize, result_pool); + sb->reader = apr_pcalloc(result_pool, sizeof(*sb->reader)); + sb->reader->buf = buf; sb->scratch_pool = svn_pool_create(result_pool); stream = svn_stream_create(sb, result_pool); - svn_stream_set_read(stream, read_handler_spillbuf); + svn_stream_set_read2(stream, NULL /* only full read support */, + read_handler_spillbuf); svn_stream_set_write(stream, write_handler_spillbuf); return stream; diff --git a/contrib/subversion/subversion/libsvn_subr/sqlite.c b/contrib/subversion/subversion/libsvn_subr/sqlite.c index 295a11c76..18d1c4928 100644 --- a/contrib/subversion/subversion/libsvn_subr/sqlite.c +++ b/contrib/subversion/subversion/libsvn_subr/sqlite.c @@ -37,6 +37,15 @@ #include "private/svn_atomic.h" #include "private/svn_skel.h" #include "private/svn_token.h" +#ifdef WIN32 +#include "private/svn_io_private.h" +#include "private/svn_utf_private.h" +#endif + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +#include "private/svn_utf_private.h" +#include "private/svn_string_private.h" +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ #ifdef SQLITE3_DEBUG #include "private/svn_debug.h" @@ -60,6 +69,17 @@ extern int (*const svn_sqlite3__api_config)(int, ...); #error SQLite is too old -- version 3.7.12 is the minimum required version #endif +#ifndef SQLITE_DETERMINISTIC +#define SQLITE_DETERMINISTIC 0 +#endif + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +/* Limit the length of a GLOB or LIKE pattern. */ +#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH +# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 +#endif +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ + const char * svn_sqlite__compiled_version(void) { @@ -97,6 +117,23 @@ sqlite_profiler(void *data, const char *sql, sqlite3_uint64 duration) } #endif +#if defined(SVN_DEBUG) && defined(SQLITE_CONFIG_LOG) +static void +sqlite_error_log(void* baton, int err, const char* msg) +{ + fprintf(SVN_DBG_OUTPUT, "DBG: sqlite[S%d]: %s\n", err, msg); +} +#endif + +void +svn_sqlite__dbg_enable_errorlog(void) +{ +#if defined(SVN_DEBUG) && defined(SQLITE_CONFIG_LOG) + sqlite3_config(SQLITE_CONFIG_LOG, sqlite_error_log, (void*)NULL /* baton */); +#endif +} + + struct svn_sqlite__db_t { sqlite3 *db3; @@ -104,6 +141,13 @@ struct svn_sqlite__db_t int nbr_statements; svn_sqlite__stmt_t **prepared_stmts; apr_pool_t *state_pool; + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES + /* Buffers for SQLite extensoins. */ + svn_membuf_t sqlext_buf1; + svn_membuf_t sqlext_buf2; + svn_membuf_t sqlext_buf3; +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ }; struct svn_sqlite__stmt_t @@ -145,6 +189,21 @@ struct svn_sqlite__value_t sqlite3_errmsg((db)->db3)); \ } while (0) +#define SQLITE_ERR_CLOSE(x, db, pool) do \ +{ \ + int sqlite_err__temp = (x); \ + if (sqlite_err__temp != SQLITE_OK) \ + { \ + const char *sqlite_err__msg \ + = apr_pstrdup(pool, sqlite3_errmsg((db)->db3)); \ + return svn_error_compose_create( \ + svn_error_createf(SQLITE_ERROR_CODE(sqlite_err__temp), \ + NULL, "sqlite[S%d]: %s", \ + sqlite_err__temp, sqlite_err__msg), \ + svn_sqlite__close(db)); \ + } \ +} while (0) + #define SQLITE_ERR_MSG(x, msg) do \ { \ int sqlite_err__temp = (x); \ @@ -154,6 +213,13 @@ struct svn_sqlite__value_t sqlite_err__temp, msg); \ } while (0) +#define SVN_ERR_CLOSE(x, db) do \ +{ \ + svn_error_t *svn__err = (x); \ + if (svn__err) \ + return svn_error_compose_create(svn__err, svn_sqlite__close(db)); \ +} while (0) + /* Time (in milliseconds) to wait for sqlite locks before giving up. */ #define BUSY_TIMEOUT 10000 @@ -688,9 +754,20 @@ svn_sqlite__finalize(svn_sqlite__stmt_t *stmt) svn_error_t * svn_sqlite__reset(svn_sqlite__stmt_t *stmt) { - SQLITE_ERR(sqlite3_reset(stmt->s3stmt), stmt->db); - SQLITE_ERR(sqlite3_clear_bindings(stmt->s3stmt), stmt->db); + /* No need to reset again after a first attempt */ stmt->needs_reset = FALSE; + + /* Clear bindings first, as there are no documented reasons + why this would ever fail, but keeping variable bindings + when reset is not what we expect. */ + SQLITE_ERR(sqlite3_clear_bindings(stmt->s3stmt), stmt->db); + + /* Reset last, as this *will* fail if the statement failed since + the last time it was reset, while reporting just the same failure. + (In this case the statement is also properly reset). + + See the sqlite3_reset() documentation for more details. */ + SQLITE_ERR(sqlite3_reset(stmt->s3stmt), stmt->db); return SVN_NO_ERROR; } @@ -754,8 +831,8 @@ init_sqlite(void *baton, apr_pool_t *pool) } static svn_error_t * -internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, - apr_pool_t *scratch_pool) +internal_open(svn_sqlite__db_t *db, const char *path, svn_sqlite__mode_t mode, + apr_int32_t timeout, apr_pool_t *scratch_pool) { { int flags; @@ -789,36 +866,59 @@ internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, We simply want umask permissions. */ SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); if (kind == svn_node_none) - SVN_ERR(svn_io_file_create(path, "", scratch_pool)); + { + /* Another thread may have created the file, that's OK. */ + svn_error_t *err = svn_io_file_create_empty(path, scratch_pool); + if (err && !APR_STATUS_IS_EEXIST(err->apr_err)) + return svn_error_trace(err); + svn_error_clear(err); + } } #endif /* Open the database. Note that a handle is returned, even when an error occurs (except for out-of-memory); thus, we can safely use it to - extract an error message and construct an svn_error_t. */ + extract an error message and construct an svn_error_t. SQLite always + requires sqlite3_close() after sqlite3_open_v2() while Subversion + typically does not require close() after an open() that returns an + error. So we must ensure we close the handle if this function, or + the caller svn_sqlite__open, returns an error to the application. */ { - /* We'd like to use SQLITE_ERR here, but we can't since it would - just return an error and leave the database open. So, we need to - do this manually. */ - /* ### SQLITE_CANTOPEN */ - int err_code = sqlite3_open_v2(path, db3, flags, NULL); - if (err_code != SQLITE_OK) + const char *vFs = NULL; + +#if defined(WIN32) && SQLITE_VERSION_AT_LEAST(3, 8, 1) + if (strlen(path) > 248) { - /* Save the error message before closing the SQLite handle. */ - char *msg = apr_pstrdup(scratch_pool, sqlite3_errmsg(*db3)); + WCHAR *win_path; + vFs = "win32-longpath"; /* Enable long paths in sqlite */ - /* We don't catch the error here, since we care more about the open - error than the close error at this point. */ - sqlite3_close(*db3); + /* Long paths must be absolute */ + if (!svn_dirent_is_absolute(path)) + SVN_ERR(svn_dirent_get_absolute(&path, path, scratch_pool)); - SQLITE_ERR_MSG(err_code, msg); + /* Convert the path to a properly canonicalized \\?\C:\long\path */ + SVN_ERR(svn_io__utf8_to_unicode_longpath(&win_path, path, + scratch_pool)); + + /* And convert it back to UTF-8 because there is no + sqlite3_open16_v2() yet */ + SVN_ERR(svn_utf__win32_utf16_to_utf8(&path, win_path, NULL, + scratch_pool)); } +#endif + + /* ### SQLITE_CANTOPEN */ + SQLITE_ERR_CLOSE(sqlite3_open_v2(path, &db->db3, flags, vFs), + db, scratch_pool); } } + if (timeout <= 0) + timeout = BUSY_TIMEOUT; + /* Retry until timeout when database is busy. */ - SQLITE_ERR_MSG(sqlite3_busy_timeout(*db3, BUSY_TIMEOUT), - sqlite3_errmsg(*db3)); + SQLITE_ERR_CLOSE(sqlite3_busy_timeout(db->db3, timeout), + db, scratch_pool); return SVN_NO_ERROR; } @@ -838,35 +938,29 @@ close_apr(void *data) if (db->db3 == NULL) return APR_SUCCESS; - /* Finalize any existing prepared statements. */ - for (i = 0; i < db->nbr_statements; i++) + /* Finalize any prepared statements. */ + if (db->prepared_stmts) { - if (db->prepared_stmts[i]) + for (i = 0; i < db->nbr_statements + STMT_INTERNAL_LAST; i++) { - if (db->prepared_stmts[i]->needs_reset) + if (db->prepared_stmts[i]) { + if (i < db->nbr_statements + && db->prepared_stmts[i]->needs_reset) + { #ifdef SVN_DEBUG - const char *stmt_text = db->statement_strings[i]; - stmt_text = stmt_text; /* Provide value for debugger */ + const char *stmt_text = db->statement_strings[i]; + SVN_UNUSED(stmt_text); - SVN_ERR_MALFUNCTION_NO_RETURN(); + SVN_ERR_MALFUNCTION_NO_RETURN(); #else - err = svn_error_compose_create( - err, + err = svn_error_compose_create(err, svn_sqlite__reset(db->prepared_stmts[i])); #endif - } - err = svn_error_compose_create( - svn_sqlite__finalize(db->prepared_stmts[i]), err); - } - } - /* And finalize any used internal statements */ - for (; i < db->nbr_statements + STMT_INTERNAL_LAST; i++) - { - if (db->prepared_stmts[i]) - { - err = svn_error_compose_create( + } + err = svn_error_compose_create( svn_sqlite__finalize(db->prepared_stmts[i]), err); + } } } @@ -888,11 +982,105 @@ close_apr(void *data) return APR_SUCCESS; } +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +/* Unicode normalizing collation for WC paths */ +static int +collate_ucs_nfd(void *baton, + int len1, const void *key1, + int len2, const void *key2) +{ + svn_sqlite__db_t *db = baton; + int result; + + if (svn_utf__normcmp(key1, len1, key2, len2, + &db->sqlext_buf1, &db->sqlext_buf2, &result)) + { + /* There is really nothing we can do here if an error occurs + during Unicode normalizetion, and attempting to recover could + result in the wc.db index being corrupted. Presumably this + can only happen if the index already contains invalid UTF-8 + strings, which should never happen in any case ... */ + SVN_ERR_MALFUNCTION_NO_RETURN(); + } + + return result; +} + +static void +glob_like_ucs_nfd_common(sqlite3_context *context, + int argc, sqlite3_value **argv, + svn_boolean_t sql_like) +{ + svn_sqlite__db_t *const db = sqlite3_user_data(context); + + const char *const pattern = (void*)sqlite3_value_text(argv[0]); + const apr_size_t pattern_len = sqlite3_value_bytes(argv[0]); + const char *const string = (void*)sqlite3_value_text(argv[1]); + const apr_size_t string_len = sqlite3_value_bytes(argv[1]); + + const char *escape = NULL; + apr_size_t escape_len = 0; + + svn_boolean_t match; + svn_error_t *err; + + if (pattern_len > SQLITE_MAX_LIKE_PATTERN_LENGTH) + { + sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); + return; + } + + if (argc == 3 && sql_like) + { + escape = (void*)sqlite3_value_text(argv[2]); + escape_len = sqlite3_value_bytes(argv[2]); + } + + if (pattern && string) + { + err = svn_utf__glob(pattern, pattern_len, string, string_len, + escape, escape_len, sql_like, + &db->sqlext_buf1, &db->sqlext_buf2, &db->sqlext_buf3, + &match); + + if (err) + { + const char *errmsg; + svn_membuf__ensure(&db->sqlext_buf1, 512); + errmsg = svn_err_best_message(err, + db->sqlext_buf1.data, + db->sqlext_buf1.size - 1); + svn_error_clear(err); + sqlite3_result_error(context, errmsg, -1); + return; + } + + sqlite3_result_int(context, match); + } +} + +/* Unicode normalizing implementation of GLOB */ +static void +glob_ucs_nfd(sqlite3_context *context, + int argc, sqlite3_value **argv) +{ + glob_like_ucs_nfd_common(context, argc, argv, FALSE); +} + +/* Unicode normalizing implementation of LIKE */ +static void +like_ucs_nfd(sqlite3_context *context, + int argc, sqlite3_value **argv) +{ + glob_like_ucs_nfd_common(context, argc, argv, TRUE); +} +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ svn_error_t * svn_sqlite__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], int unused1, const char * const *unused2, + apr_int32_t timeout, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { SVN_ERR(svn_atomic__init_once(&sqlite_init_state, @@ -900,7 +1088,7 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, *db = apr_pcalloc(result_pool, sizeof(**db)); - SVN_ERR(internal_open(&(*db)->db3, path, mode, scratch_pool)); + SVN_ERR(internal_open(*db, path, mode, timeout, scratch_pool)); #if SQLITE_VERSION_NUMBER >= 3008000 && SQLITE_VERSION_NUMBER < 3009000 /* disable SQLITE_ENABLE_STAT3/4 from 3.8.1 - 3.8.3 (but not 3.8.3.1+) @@ -914,6 +1102,38 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, } #endif +#ifdef SVN_UNICODE_NORMALIZATION_FIXES + /* Create extension buffers with space for 200 UCS-4 characters. */ + svn_membuf__create(&(*db)->sqlext_buf1, 800, result_pool); + svn_membuf__create(&(*db)->sqlext_buf2, 800, result_pool); + svn_membuf__create(&(*db)->sqlext_buf3, 800, result_pool); + + /* Register collation and LIKE and GLOB operator replacements. */ + SQLITE_ERR_CLOSE(sqlite3_create_collation((*db)->db3, + "svn-ucs-nfd", SQLITE_UTF8, + *db, collate_ucs_nfd), + db, scratch_pool); + /* ### Is it really necessary to override these functions? + I would assume the default implementation to be collation agnostic? + And otherwise our implementation should be... + + The default implementation is in some cases index backed, while our + implementation can't be. With an index based on the collation it could + be. */ + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "glob", 2, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, glob_ucs_nfd, NULL, NULL), + db, scratch_pool); + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "like", 2, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, like_ucs_nfd, NULL, NULL), + db, scratch_pool); + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "like", 3, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, like_ucs_nfd, NULL, NULL), + db, scratch_pool); +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ + #ifdef SQLITE3_DEBUG sqlite3_trace((*db)->db3, sqlite_tracer, (*db)->db3); #endif @@ -921,14 +1141,14 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, sqlite3_profile((*db)->db3, sqlite_profiler, (*db)->db3); #endif - /* ### simplify this. remnants of some old SQLite compat code. */ - { - int ignored_err = SQLITE_OK; - - SVN_ERR(exec_sql2(*db, "PRAGMA case_sensitive_like=1;", ignored_err)); - } - - SVN_ERR(exec_sql(*db, + SVN_ERR_CLOSE(exec_sql(*db, + /* The default behavior of the LIKE operator is to ignore case + for ASCII characters. Hence, by default 'a' LIKE 'A' is true. + The case_sensitive_like pragma installs a new application- + defined LIKE function that is either case sensitive or + insensitive depending on the value of the case_sensitive_like + pragma. */ + "PRAGMA case_sensitive_like=1;" /* Disable synchronization to disable the explicit disk flushes that make Sqlite up to 50 times slower; especially on small transactions. @@ -951,13 +1171,17 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, affects application(read: Subversion) performance/behavior. */ "PRAGMA foreign_keys=OFF;" /* SQLITE_DEFAULT_FOREIGN_KEYS*/ "PRAGMA locking_mode = NORMAL;" /* SQLITE_DEFAULT_LOCKING_MODE */ - )); + /* Testing shows TRUNCATE is faster than DELETE on Windows. */ + "PRAGMA journal_mode = TRUNCATE;" + ), + *db); #if defined(SVN_DEBUG) /* When running in debug mode, enable the checking of foreign key constraints. This has possible performance implications, so we don't bother to do it for production...for now. */ - SVN_ERR(exec_sql(*db, "PRAGMA foreign_keys=ON;")); + SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA foreign_keys=ON;"), + *db); #endif #ifdef SVN_SQLITE_REVERSE_UNORDERED_SELECTS @@ -965,7 +1189,8 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, clause to emit their results in the reverse order of what they normally would. This can help detecting invalid assumptions about the result order.*/ - SVN_ERR(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;")); + SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;"), + *db); #endif /* Store temporary tables in RAM instead of in temporary files, but don't @@ -1203,7 +1428,7 @@ svn_sqlite__hotcopy(const char *src_path, svn_sqlite__db_t *src_db; SVN_ERR(svn_sqlite__open(&src_db, src_path, svn_sqlite__mode_readonly, - NULL, 0, NULL, + NULL, 0, NULL, 0, scratch_pool, scratch_pool)); { @@ -1212,7 +1437,7 @@ svn_sqlite__hotcopy(const char *src_path, int rc1, rc2; SVN_ERR(svn_sqlite__open(&dst_db, dst_path, svn_sqlite__mode_rwcreate, - NULL, 0, NULL, scratch_pool, scratch_pool)); + NULL, 0, NULL, 0, scratch_pool, scratch_pool)); backup = sqlite3_backup_init(dst_db->db3, "main", src_db->db3, "main"); if (!backup) return svn_error_createf(SVN_ERR_SQLITE_ERROR, NULL, @@ -1244,6 +1469,8 @@ svn_sqlite__hotcopy(const char *src_path, SVN_ERR(svn_sqlite__close(src_db)); + SVN_ERR(svn_io_copy_perms(src_path, dst_path, scratch_pool)); + return SVN_NO_ERROR; } @@ -1251,8 +1478,6 @@ struct function_wrapper_baton_t { svn_sqlite__func_t func; void *baton; - - apr_pool_t *scratch_pool; }; static void @@ -1262,22 +1487,12 @@ wrapped_func(sqlite3_context *context, { struct function_wrapper_baton_t *fwb = sqlite3_user_data(context); svn_sqlite__context_t sctx; - svn_sqlite__value_t **local_vals = - apr_palloc(fwb->scratch_pool, - sizeof(svn_sqlite__value_t *) * argc); svn_error_t *err; - int i; + void *void_values = values; sctx.context = context; - for (i = 0; i < argc; i++) - { - local_vals[i] = apr_palloc(fwb->scratch_pool, sizeof(*local_vals[i])); - local_vals[i]->value = values[i]; - } - - err = fwb->func(&sctx, argc, local_vals, fwb->scratch_pool); - svn_pool_clear(fwb->scratch_pool); + err = fwb->func(&sctx, argc, void_values, fwb->baton); if (err) { @@ -1289,21 +1504,27 @@ wrapped_func(sqlite3_context *context, } } + svn_error_t * svn_sqlite__create_scalar_function(svn_sqlite__db_t *db, const char *func_name, int argc, + svn_boolean_t deterministic, svn_sqlite__func_t func, void *baton) { + int eTextRep; struct function_wrapper_baton_t *fwb = apr_pcalloc(db->state_pool, sizeof(*fwb)); - fwb->scratch_pool = svn_pool_create(db->state_pool); fwb->func = func; fwb->baton = baton; - SQLITE_ERR(sqlite3_create_function(db->db3, func_name, argc, SQLITE_ANY, + eTextRep = SQLITE_ANY; + if (deterministic) + eTextRep |= SQLITE_DETERMINISTIC; + + SQLITE_ERR(sqlite3_create_function(db->db3, func_name, argc, eTextRep, fwb, wrapped_func, NULL, NULL), db); @@ -1313,13 +1534,15 @@ svn_sqlite__create_scalar_function(svn_sqlite__db_t *db, int svn_sqlite__value_type(svn_sqlite__value_t *val) { - return sqlite3_value_type(val->value); + void *v = val; + return sqlite3_value_type(v); } const char * svn_sqlite__value_text(svn_sqlite__value_t *val) { - return (const char *) sqlite3_value_text(val->value); + void *v = val; + return (const char *) sqlite3_value_text(v); } void @@ -1333,3 +1556,9 @@ svn_sqlite__result_int64(svn_sqlite__context_t *sctx, apr_int64_t val) { sqlite3_result_int64(sctx->context, val); } + +void +svn_sqlite__result_error(svn_sqlite__context_t *sctx, const char *msg, int num) +{ + sqlite3_result_error(sctx->context, msg, num); +} diff --git a/contrib/subversion/subversion/libsvn_subr/sqlite3wrapper.c b/contrib/subversion/subversion/libsvn_subr/sqlite3wrapper.c index 4bbb61983..76e182ff5 100644 --- a/contrib/subversion/subversion/libsvn_subr/sqlite3wrapper.c +++ b/contrib/subversion/subversion/libsvn_subr/sqlite3wrapper.c @@ -26,18 +26,19 @@ #ifdef SVN_SQLITE_INLINE # 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 +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) # 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__ +# if defined(__APPLE_CC__) || defined(__clang__) # pragma GCC diagnostic ignored "-Wshorten-64-to-32" # endif +# if defined(__clang__) +# pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" +# pragma clang diagnostic ignored "-Wmissing-variable-declarations" +# endif # endif # ifdef __APPLE__ # include @@ -52,9 +53,6 @@ # endif # define SQLITE_DEFAULT_FILE_PERMISSIONS 0666 # include -# 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; diff --git a/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_providers.c b/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_providers.c index cf86fa178..7fd81d027 100644 --- a/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_providers.c +++ b/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_providers.c @@ -79,13 +79,12 @@ ssl_client_cert_file_first_credentials(void **credentials_p, } -static const svn_auth_provider_t ssl_client_cert_file_provider = - { - SVN_AUTH_CRED_SSL_CLIENT_CERT, - ssl_client_cert_file_first_credentials, - NULL, - NULL - }; +static const svn_auth_provider_t ssl_client_cert_file_provider = { + SVN_AUTH_CRED_SSL_CLIENT_CERT, + ssl_client_cert_file_first_credentials, + NULL, + NULL +}; /*** Public API to SSL file providers. ***/ diff --git a/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c b/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c index 6c1bcf17c..1626750fa 100644 --- a/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c +++ b/contrib/subversion/subversion/libsvn_subr/ssl_client_cert_pw_providers.c @@ -39,16 +39,6 @@ /* File provider */ /*-----------------------------------------------------------------------*/ -/* The keys that will be stored on disk. These serve the same role as - * similar constants in other providers. - * - * AUTHN_PASSTYPE_KEY just records the passphrase type next to the - * passphrase, so that anyone who is manually editing their authn - * files can know which provider owns the password. - */ -#define AUTHN_PASSPHRASE_KEY "passphrase" -#define AUTHN_PASSTYPE_KEY "passtype" - /* Baton type for the ssl client cert passphrase provider. */ typedef struct ssl_client_cert_pw_file_provider_baton_t { @@ -75,7 +65,7 @@ svn_auth__ssl_client_cert_pw_get(svn_boolean_t *done, apr_pool_t *pool) { svn_string_t *str; - str = svn_hash_gets(creds, AUTHN_PASSPHRASE_KEY); + str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSPHRASE_KEY); if (str && str->data) { *passphrase = str->data; @@ -98,7 +88,7 @@ svn_auth__ssl_client_cert_pw_set(svn_boolean_t *done, svn_boolean_t non_interactive, apr_pool_t *pool) { - svn_hash_sets(creds, AUTHN_PASSPHRASE_KEY, + svn_hash_sets(creds, SVN_CONFIG_AUTHN_PASSPHRASE_KEY, svn_string_create(passphrase, pool)); *done = TRUE; return SVN_NO_ERROR; @@ -308,7 +298,7 @@ svn_auth__ssl_client_cert_pw_cache_set(svn_boolean_t *saved, if (*saved && passtype) { - svn_hash_sets(creds_hash, AUTHN_PASSTYPE_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY, svn_string_create(passtype, pool)); } diff --git a/contrib/subversion/subversion/libsvn_subr/ssl_server_trust_providers.c b/contrib/subversion/subversion/libsvn_subr/ssl_server_trust_providers.c index c69be772e..a8d441459 100644 --- a/contrib/subversion/subversion/libsvn_subr/ssl_server_trust_providers.c +++ b/contrib/subversion/subversion/libsvn_subr/ssl_server_trust_providers.c @@ -35,12 +35,6 @@ /* File provider */ /*-----------------------------------------------------------------------*/ -/* The keys that will be stored on disk. These serve the same role as - similar constants in other providers. */ -#define AUTHN_ASCII_CERT_KEY "ascii_cert" -#define AUTHN_FAILURES_KEY "failures" - - /* retrieve ssl server CA failure overrides (if any) from servers config */ static svn_error_t * @@ -73,18 +67,12 @@ ssl_server_trust_file_first_credentials(void **credentials, svn_string_t *trusted_cert, *this_cert, *failstr; apr_uint32_t last_failures = 0; - trusted_cert = svn_hash_gets(creds_hash, AUTHN_ASCII_CERT_KEY); + trusted_cert = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_ASCII_CERT_KEY); this_cert = svn_string_create(cert_info->ascii_cert, pool); - failstr = svn_hash_gets(creds_hash, AUTHN_FAILURES_KEY); + failstr = svn_hash_gets(creds_hash, SVN_CONFIG_AUTHN_FAILURES_KEY); if (failstr) - { - char *endptr; - unsigned long tmp_ulong = strtoul(failstr->data, &endptr, 10); - - if (*endptr == '\0') - last_failures = (apr_uint32_t) tmp_ulong; - } + SVN_ERR(svn_cstring_atoui(&last_failures, failstr->data)); /* If the cert is trusted and there are no new failures, we * accept it by clearing all failures. */ @@ -130,10 +118,9 @@ ssl_server_trust_file_save_credentials(svn_boolean_t *saved, cert_info = svn_hash_gets(parameters, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO); creds_hash = apr_hash_make(pool); - svn_hash_sets(creds_hash, AUTHN_ASCII_CERT_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_ASCII_CERT_KEY, svn_string_create(cert_info->ascii_cert, pool)); - svn_hash_sets(creds_hash, - AUTHN_FAILURES_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_FAILURES_KEY, svn_string_createf(pool, "%lu", (unsigned long)creds->accepted_failures)); @@ -149,9 +136,9 @@ ssl_server_trust_file_save_credentials(svn_boolean_t *saved, static const svn_auth_provider_t ssl_server_trust_file_provider = { SVN_AUTH_CRED_SSL_SERVER_TRUST, - &ssl_server_trust_file_first_credentials, + ssl_server_trust_file_first_credentials, NULL, - &ssl_server_trust_file_save_credentials, + ssl_server_trust_file_save_credentials, }; diff --git a/contrib/subversion/subversion/libsvn_subr/stream.c b/contrib/subversion/subversion/libsvn_subr/stream.c index 93a4c423c..2f803b872 100644 --- a/contrib/subversion/subversion/libsvn_subr/stream.c +++ b/contrib/subversion/subversion/libsvn_subr/stream.c @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include #include @@ -41,20 +42,24 @@ #include "svn_checksum.h" #include "svn_path.h" #include "svn_private_config.h" +#include "private/svn_atomic.h" #include "private/svn_error_private.h" #include "private/svn_eol_private.h" #include "private/svn_io_private.h" #include "private/svn_subr_private.h" +#include "private/svn_utf_private.h" struct svn_stream_t { void *baton; svn_read_fn_t read_fn; + svn_read_fn_t read_full_fn; svn_stream_skip_fn_t skip_fn; svn_write_fn_t write_fn; svn_close_fn_t close_fn; svn_stream_mark_fn_t mark_fn; svn_stream_seek_fn_t seek_fn; + svn_stream_data_available_fn_t data_available_fn; svn_stream__is_buffered_fn_t is_buffered_fn; apr_file_t *file; /* Maybe NULL */ }; @@ -63,7 +68,7 @@ struct svn_stream_t { /*** Forward declarations. ***/ static svn_error_t * -skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn); +skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn); /*** Generic streams. ***/ @@ -73,16 +78,8 @@ svn_stream_create(void *baton, apr_pool_t *pool) { svn_stream_t *stream; - stream = apr_palloc(pool, sizeof(*stream)); + stream = apr_pcalloc(pool, sizeof(*stream)); stream->baton = baton; - stream->read_fn = NULL; - stream->skip_fn = NULL; - stream->write_fn = NULL; - stream->close_fn = NULL; - stream->mark_fn = NULL; - stream->seek_fn = NULL; - stream->is_buffered_fn = NULL; - stream->file = NULL; return stream; } @@ -95,9 +92,12 @@ svn_stream_set_baton(svn_stream_t *stream, void *baton) void -svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn) +svn_stream_set_read2(svn_stream_t *stream, + svn_read_fn_t read_fn, + svn_read_fn_t read_full_fn) { stream->read_fn = read_fn; + stream->read_full_fn = read_full_fn; } void @@ -130,6 +130,13 @@ svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn) stream->seek_fn = seek_fn; } +void +svn_stream_set_data_available(svn_stream_t *stream, + svn_stream_data_available_fn_t data_available_fn) +{ + stream->data_available_fn = data_available_fn; +} + void svn_stream__set_is_buffered(svn_stream_t *stream, svn_stream__is_buffered_fn_t is_buffered_fn) @@ -137,20 +144,61 @@ svn_stream__set_is_buffered(svn_stream_t *stream, stream->is_buffered_fn = is_buffered_fn; } +/* Standard implementation for svn_stream_read_full() based on + multiple svn_stream_read2() calls (in separate function to make + it more likely for svn_stream_read_full to be inlined) */ +static svn_error_t * +full_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len) +{ + apr_size_t remaining = *len; + while (remaining > 0) + { + apr_size_t length = remaining; + SVN_ERR(svn_stream_read2(stream, buffer, &length)); + + if (length == 0) + { + *len -= remaining; + return SVN_NO_ERROR; + } + + remaining -= length; + buffer += length; + } + + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_stream_supports_partial_read(svn_stream_t *stream) +{ + return stream->read_fn != NULL; +} + svn_error_t * -svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len) +svn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len) { - SVN_ERR_ASSERT(stream->read_fn != NULL); + if (stream->read_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + return svn_error_trace(stream->read_fn(stream->baton, buffer, len)); } +svn_error_t * +svn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len) +{ + if (stream->read_full_fn == NULL) + return svn_error_trace(full_read_fallback(stream, buffer, len)); + + return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len)); +} svn_error_t * svn_stream_skip(svn_stream_t *stream, apr_size_t len) { if (stream->skip_fn == NULL) return svn_error_trace( - skip_default_handler(stream->baton, len, stream->read_fn)); + skip_default_handler(stream->baton, len, stream->read_full_fn)); return svn_error_trace(stream->skip_fn(stream->baton, len)); } @@ -159,7 +207,9 @@ svn_stream_skip(svn_stream_t *stream, apr_size_t len) svn_error_t * svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len) { - SVN_ERR_ASSERT(stream->write_fn != NULL); + if (stream->write_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + return svn_error_trace(stream->write_fn(stream->baton, data, len)); } @@ -196,6 +246,17 @@ svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark) return svn_error_trace(stream->seek_fn(stream->baton, mark)); } +svn_error_t * +svn_stream_data_available(svn_stream_t *stream, + svn_boolean_t *data_available) +{ + if (stream->data_available_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + + return svn_error_trace(stream->data_available_fn(stream->baton, + data_available)); +} + svn_boolean_t svn_stream__is_buffered(svn_stream_t *stream) { @@ -259,11 +320,6 @@ svn_stream_printf_from_utf8(svn_stream_t *stream, return svn_error_trace(svn_stream_puts(stream, translated)); } -/* Size that 90% of the lines we encounter will be not longer than. - used by stream_readline_bytewise() and stream_readline_chunky(). - */ -#define LINE_CHUNK_SIZE 80 - /* Guts of svn_stream_readline(). * Returns the line read from STREAM in *STRINGBUF, and indicates * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator @@ -286,14 +342,14 @@ stream_readline_bytewise(svn_stringbuf_t **stringbuf, optimize for the 90% case. 90% of the time, we can avoid the stringbuf ever having to realloc() itself if we start it out at 80 chars. */ - str = svn_stringbuf_create_ensure(LINE_CHUNK_SIZE, pool); + str = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool); /* Read into STR up to and including the next EOL sequence. */ match = eol; while (*match) { numbytes = 1; - SVN_ERR(svn_stream_read(stream, &c, &numbytes)); + SVN_ERR(svn_stream_read_full(stream, &c, &numbytes)); if (numbytes != 1) { /* a 'short' read means the stream has run out. */ @@ -330,7 +386,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, * larger value because filling the buffer from the stream takes * time as well. */ - char buffer[LINE_CHUNK_SIZE+1]; + char buffer[SVN__LINE_CHUNK_SIZE+1]; /* variables */ svn_stream_mark_t *mark; @@ -347,8 +403,8 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, SVN_ERR(svn_stream_mark(stream, &mark, pool)); /* Read the first chunk. */ - numbytes = LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, buffer, &numbytes)); + numbytes = SVN__LINE_CHUNK_SIZE; + SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes)); buffer[numbytes] = '\0'; /* Look for the EOL in this first chunk. If we find it, we are done here. @@ -359,7 +415,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool); total_parsed = eol_pos - buffer + eol_len; } - else if (numbytes < LINE_CHUNK_SIZE) + else if (numbytes < SVN__LINE_CHUNK_SIZE) { /* We hit EOF but not EOL. */ @@ -371,7 +427,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, { /* A larger buffer for the string is needed. */ svn_stringbuf_t *str; - str = svn_stringbuf_create_ensure(2*LINE_CHUNK_SIZE, pool); + str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool); svn_stringbuf_appendbytes(str, buffer, numbytes); *stringbuf = str; @@ -381,9 +437,9 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, { /* Append the next chunk to the string read so far. */ - svn_stringbuf_ensure(str, str->len + LINE_CHUNK_SIZE); - numbytes = LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, str->data + str->len, &numbytes)); + svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE); + numbytes = SVN__LINE_CHUNK_SIZE; + SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes)); str->len += numbytes; str->data[str->len] = '\0'; @@ -393,7 +449,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, */ eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol); - if ((numbytes < LINE_CHUNK_SIZE) && (eol_pos == NULL)) + if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL)) { /* We hit EOF instead of EOL. */ *eof = TRUE; @@ -495,7 +551,7 @@ svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to, break; } - err = svn_stream_read(from, buf, &len); + err = svn_stream_read_full(from, buf, &len); if (err) break; @@ -528,10 +584,10 @@ svn_stream_contents_same2(svn_boolean_t *same, while (bytes_read1 == SVN__STREAM_CHUNK_SIZE && bytes_read2 == SVN__STREAM_CHUNK_SIZE) { - err = svn_stream_read(stream1, buf1, &bytes_read1); + err = svn_stream_read_full(stream1, buf1, &bytes_read1); if (err) break; - err = svn_stream_read(stream2, buf2, &bytes_read2); + err = svn_stream_read_full(stream2, buf2, &bytes_read2); if (err) break; @@ -554,7 +610,7 @@ svn_stream_contents_same2(svn_boolean_t *same, /* Skip data from any stream by reading and simply discarding it. */ static svn_error_t * -skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn) +skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn) { apr_size_t bytes_read = 1; char buffer[4096]; @@ -563,7 +619,7 @@ skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn) while ((to_read > 0) && (bytes_read > 0)) { bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read; - SVN_ERR(read_fn(baton, buffer, &bytes_read)); + SVN_ERR(read_full_fn(baton, buffer, &bytes_read)); to_read -= bytes_read; } @@ -613,7 +669,7 @@ svn_stream_empty(apr_pool_t *pool) svn_stream_t *stream; stream = svn_stream_create(NULL, pool); - svn_stream_set_read(stream, read_handler_empty); + svn_stream_set_read2(stream, read_handler_empty, read_handler_empty); svn_stream_set_write(stream, write_handler_empty); svn_stream_set_mark(stream, mark_handler_empty); svn_stream_set_seek(stream, seek_handler_empty); @@ -685,7 +741,13 @@ svn_stream_tee(svn_stream_t *out1, static svn_error_t * read_handler_disown(void *baton, char *buffer, apr_size_t *len) { - return svn_error_trace(svn_stream_read(baton, buffer, len)); + return svn_error_trace(svn_stream_read2(baton, buffer, len)); +} + +static svn_error_t * +read_full_handler_disown(void *baton, char *buffer, apr_size_t *len) +{ + return svn_error_trace(svn_stream_read_full(baton, buffer, len)); } static svn_error_t * @@ -712,6 +774,12 @@ seek_handler_disown(void *baton, const svn_stream_mark_t *mark) return svn_error_trace(svn_stream_seek(baton, mark)); } +static svn_error_t * +data_available_disown(void *baton, svn_boolean_t *data_available) +{ + return svn_error_trace(svn_stream_data_available(baton, data_available)); +} + static svn_boolean_t is_buffered_handler_disown(void *baton) { @@ -723,11 +791,12 @@ svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool) { svn_stream_t *s = svn_stream_create(stream, pool); - svn_stream_set_read(s, read_handler_disown); + svn_stream_set_read2(s, read_handler_disown, read_full_handler_disown); svn_stream_set_skip(s, skip_handler_disown); svn_stream_set_write(s, write_handler_disown); svn_stream_set_mark(s, mark_handler_disown); svn_stream_set_seek(s, seek_handler_disown); + svn_stream_set_data_available(s, data_available_disown); svn_stream__set_is_buffered(s, is_buffered_handler_disown); return s; @@ -748,6 +817,38 @@ struct mark_apr { static svn_error_t * read_handler_apr(void *baton, char *buffer, apr_size_t *len) +{ + struct baton_apr *btn = baton; + svn_error_t *err; + + if (*len == 1) + { + err = svn_io_file_getc(buffer, btn->file, btn->pool); + if (err) + { + *len = 0; + if (APR_STATUS_IS_EOF(err->apr_err)) + { + svn_error_clear(err); + err = SVN_NO_ERROR; + } + } + } + else + { + err = svn_io_file_read(btn->file, buffer, len, btn->pool); + if (err && APR_STATUS_IS_EOF(err->apr_err)) + { + svn_error_clear(err); + err = NULL; + } + } + + return svn_error_trace(err); +} + +static svn_error_t * +read_full_handler_apr(void *baton, char *buffer, apr_size_t *len) { struct baton_apr *btn = baton; svn_error_t *err; @@ -833,6 +934,62 @@ seek_handler_apr(void *baton, const svn_stream_mark_t *mark) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_apr(void *baton, svn_boolean_t *data_available) +{ + struct baton_apr *btn = baton; + apr_status_t status; +#if !defined(WIN32) || APR_FILES_AS_SOCKETS + apr_pollfd_t pfd; + int n; + + pfd.desc_type = APR_POLL_FILE; + pfd.desc.f = btn->file; + pfd.p = btn->pool; /* If we had a scratch pool... Luckily apr doesn't + store anything in this pool at this time */ + pfd.reqevents = APR_POLLIN; + + status = apr_poll(&pfd, 1, &n, 0); + + if (status == APR_SUCCESS) + { + *data_available = (n > 0); + return SVN_NO_ERROR; + } + else if (APR_STATUS_IS_EOF(status) || APR_STATUS_IS_TIMEUP(status)) + { + *data_available = FALSE; + return SVN_NO_ERROR; + } + else + { + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, + svn_error_wrap_apr( + status, + _("Polling for available data on filestream " + "failed")), + NULL); + } +#else + HANDLE h; + DWORD dwAvail; + status = apr_os_file_get(&h, btn->file); + + if (status) + return svn_error_wrap_apr(status, NULL); + + if (PeekNamedPipe(h, NULL, 0, NULL, &dwAvail, NULL)) + { + *data_available = (dwAvail > 0); + return SVN_NO_ERROR; + } + + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, + svn_error_wrap_apr(apr_get_os_error(), NULL), + _("Windows doesn't support polling on files")); +#endif +} + static svn_boolean_t is_buffered_handler_apr(void *baton) { @@ -894,10 +1051,12 @@ svn_stream_open_unique(svn_stream_t **stream, } -svn_stream_t * -svn_stream_from_aprfile2(apr_file_t *file, - svn_boolean_t disown, - apr_pool_t *pool) +/* Helper function that creates a stream from an APR file. */ +static svn_stream_t * +make_stream_from_apr_file(apr_file_t *file, + svn_boolean_t disown, + svn_boolean_t supports_seek, + apr_pool_t *pool) { struct baton_apr *baton; svn_stream_t *stream; @@ -909,11 +1068,17 @@ svn_stream_from_aprfile2(apr_file_t *file, baton->file = file; baton->pool = pool; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_apr); + svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr); svn_stream_set_write(stream, write_handler_apr); - svn_stream_set_skip(stream, skip_handler_apr); - svn_stream_set_mark(stream, mark_handler_apr); - svn_stream_set_seek(stream, seek_handler_apr); + + if (supports_seek) + { + svn_stream_set_skip(stream, skip_handler_apr); + svn_stream_set_mark(stream, mark_handler_apr); + svn_stream_set_seek(stream, seek_handler_apr); + } + + svn_stream_set_data_available(stream, data_available_handler_apr); svn_stream__set_is_buffered(stream, is_buffered_handler_apr); stream->file = file; @@ -923,6 +1088,14 @@ svn_stream_from_aprfile2(apr_file_t *file, return stream; } +svn_stream_t * +svn_stream_from_aprfile2(apr_file_t *file, + svn_boolean_t disown, + apr_pool_t *pool) +{ + return make_stream_from_apr_file(file, disown, TRUE, pool); +} + apr_file_t * svn_stream__aprfile(svn_stream_t *stream) { @@ -941,16 +1114,13 @@ svn_stream__aprfile(svn_stream_t *stream) struct zbaton { z_stream *in; /* compressed stream for reading */ z_stream *out; /* compressed stream for writing */ - svn_read_fn_t read; /* substream's read function */ - svn_write_fn_t write; /* substream's write function */ - svn_close_fn_t close; /* substream's close function */ + void *substream; /* The substream */ void *read_buffer; /* buffer used for reading from substream */ int read_flush; /* what flush mode to use while reading */ apr_pool_t *pool; /* The pool this baton is allocated on */ - void *subbaton; /* The substream's baton */ }; /* zlib alloc function. opaque is the pool we need. */ @@ -971,8 +1141,7 @@ zfree(voidpf opaque, voidpf address) /* Helper function to figure out the sync mode */ static svn_error_t * -read_helper_gz(svn_read_fn_t read_fn, - void *baton, +read_helper_gz(svn_stream_t *substream, char *buffer, uInt *len, int *zflush) { @@ -982,7 +1151,7 @@ read_helper_gz(svn_read_fn_t read_fn, uInt, but Subversion's API requires apr_size_t. */ apr_size_t apr_len = (apr_size_t) *len; - SVN_ERR((*read_fn)(baton, buffer, &apr_len)); + SVN_ERR(svn_stream_read_full(substream, buffer, &apr_len)); /* Type cast back to uInt type that zlib uses. On LP64 platforms apr_size_t will be bigger than uInt. */ @@ -1012,7 +1181,7 @@ read_handler_gz(void *baton, char *buffer, apr_size_t *len) btn->in->next_in = btn->read_buffer; btn->in->avail_in = ZBUFFER_SIZE; - SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, + SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer, &btn->in->avail_in, &btn->read_flush)); zerr = inflateInit(btn->in); @@ -1028,7 +1197,7 @@ read_handler_gz(void *baton, char *buffer, apr_size_t *len) { btn->in->avail_in = ZBUFFER_SIZE; btn->in->next_in = btn->read_buffer; - SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, + SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer, &btn->in->avail_in, &btn->read_flush)); } @@ -1090,7 +1259,7 @@ write_handler_gz(void *baton, const char *buffer, apr_size_t *len) SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg)); write_len = buf_size - btn->out->avail_out; if (write_len > 0) - SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len)); + SVN_ERR(svn_stream_write(btn->substream, write_buf, &write_len)); } svn_pool_destroy(subpool); @@ -1129,7 +1298,7 @@ close_handler_gz(void *baton) btn->out->msg)); write_len = ZBUFFER_SIZE - btn->out->avail_out; if (write_len > 0) - SVN_ERR(btn->write(btn->subbaton, buf, &write_len)); + SVN_ERR(svn_stream_write(btn->substream, buf, &write_len)); if (zerr == Z_STREAM_END) break; } @@ -1138,10 +1307,7 @@ close_handler_gz(void *baton) SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg)); } - if (btn->close != NULL) - return svn_error_trace(btn->close(btn->subbaton)); - else - return SVN_NO_ERROR; + return svn_error_trace(svn_stream_close(btn->substream)); } @@ -1155,16 +1321,14 @@ svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool) baton = apr_palloc(pool, sizeof(*baton)); baton->in = baton->out = NULL; - baton->read = stream->read_fn; - baton->write = stream->write_fn; - baton->close = stream->close_fn; - baton->subbaton = stream->baton; + baton->substream = stream; baton->pool = pool; baton->read_buffer = NULL; baton->read_flush = Z_SYNC_FLUSH; zstream = svn_stream_create(baton, pool); - svn_stream_set_read(zstream, read_handler_gz); + svn_stream_set_read2(zstream, NULL /* only full read support */, + read_handler_gz); svn_stream_set_write(zstream, write_handler_gz); svn_stream_set_close(zstream, close_handler_gz); @@ -1190,11 +1354,24 @@ struct checksum_stream_baton static svn_error_t * read_handler_checksum(void *baton, char *buffer, apr_size_t *len) +{ + struct checksum_stream_baton *btn = baton; + + SVN_ERR(svn_stream_read2(btn->proxy, buffer, len)); + + if (btn->read_checksum) + SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +read_full_handler_checksum(void *baton, char *buffer, apr_size_t *len) { struct checksum_stream_baton *btn = baton; apr_size_t saved_len = *len; - SVN_ERR(svn_stream_read(btn->proxy, buffer, len)); + SVN_ERR(svn_stream_read_full(btn->proxy, buffer, len)); if (btn->read_checksum) SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); @@ -1217,6 +1394,14 @@ write_handler_checksum(void *baton, const char *buffer, apr_size_t *len) return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); } +static svn_error_t * +data_available_handler_checksum(void *baton, svn_boolean_t *data_available) +{ + struct checksum_stream_baton *btn = baton; + + return svn_error_trace(svn_stream_data_available(btn->proxy, + data_available)); +} static svn_error_t * close_handler_checksum(void *baton) @@ -1232,7 +1417,7 @@ close_handler_checksum(void *baton) do { - SVN_ERR(read_handler_checksum(baton, buf, &len)); + SVN_ERR(read_full_handler_checksum(baton, buf, &len)); } while (btn->read_more); } @@ -1279,106 +1464,45 @@ svn_stream_checksummed2(svn_stream_t *stream, baton->pool = pool; s = svn_stream_create(baton, pool); - svn_stream_set_read(s, read_handler_checksum); + svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum); svn_stream_set_write(s, write_handler_checksum); + svn_stream_set_data_available(s, data_available_handler_checksum); svn_stream_set_close(s, close_handler_checksum); return s; } -struct md5_stream_baton -{ - const unsigned char **read_digest; - const unsigned char **write_digest; - svn_checksum_t *read_checksum; - svn_checksum_t *write_checksum; - svn_stream_t *proxy; - apr_pool_t *pool; -}; - -static svn_error_t * -read_handler_md5(void *baton, char *buffer, apr_size_t *len) -{ - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_read(btn->proxy, buffer, len)); -} - -static svn_error_t * -skip_handler_md5(void *baton, apr_size_t len) -{ - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_skip(btn->proxy, len)); -} +/* Miscellaneous stream functions. */ -static svn_error_t * -write_handler_md5(void *baton, const char *buffer, apr_size_t *len) +svn_error_t * +svn_stringbuf_from_stream(svn_stringbuf_t **str, + svn_stream_t *stream, + apr_size_t len_hint, + apr_pool_t *result_pool) { - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); -} +#define MIN_READ_SIZE 64 -static svn_error_t * -close_handler_md5(void *baton) -{ - struct md5_stream_baton *btn = baton; + apr_size_t to_read = 0; + svn_stringbuf_t *text + = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE, + result_pool); - SVN_ERR(svn_stream_close(btn->proxy)); + do + { + to_read = text->blocksize - 1 - text->len; + SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read)); + text->len += to_read; - if (btn->read_digest) - *btn->read_digest - = apr_pmemdup(btn->pool, btn->read_checksum->digest, - APR_MD5_DIGESTSIZE); + if (to_read && text->blocksize < text->len + MIN_READ_SIZE) + svn_stringbuf_ensure(text, text->blocksize * 2); + } + while (to_read); - if (btn->write_digest) - *btn->write_digest - = apr_pmemdup(btn->pool, btn->write_checksum->digest, - APR_MD5_DIGESTSIZE); + text->data[text->len] = '\0'; + *str = text; return SVN_NO_ERROR; } - -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) -{ - svn_stream_t *s; - struct md5_stream_baton *baton; - - if (! read_digest && ! write_digest) - return stream; - - baton = apr_palloc(pool, sizeof(*baton)); - baton->read_digest = read_digest; - baton->write_digest = write_digest; - baton->pool = pool; - - /* Set BATON->proxy to a stream that will fill in BATON->read_checksum - * and BATON->write_checksum (if we want them) when it is closed. */ - baton->proxy - = svn_stream_checksummed2(stream, - read_digest ? &baton->read_checksum : NULL, - write_digest ? &baton->write_checksum : NULL, - svn_checksum_md5, - read_all, pool); - - /* Create a stream that will forward its read/write/close operations to - * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we - * want them) after it closes BATON->proxy. */ - s = svn_stream_create(baton, pool); - svn_stream_set_read(s, read_handler_md5); - svn_stream_set_skip(s, skip_handler_md5); - svn_stream_set_write(s, write_handler_md5); - svn_stream_set_close(s, close_handler_md5); - return s; -} - - - - -/* Miscellaneous stream functions. */ struct stringbuf_stream_baton { svn_stringbuf_t *str; @@ -1454,6 +1578,15 @@ seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_stringbuf(void *baton, svn_boolean_t *data_available) +{ + struct stringbuf_stream_baton *btn = baton; + + *data_available = ((btn->str->len - btn->amt_read) > 0); + return SVN_NO_ERROR; +} + static svn_boolean_t is_buffered_handler_stringbuf(void *baton) { @@ -1474,11 +1607,12 @@ svn_stream_from_stringbuf(svn_stringbuf_t *str, baton->str = str; baton->amt_read = 0; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_stringbuf); + svn_stream_set_read2(stream, read_handler_stringbuf, read_handler_stringbuf); svn_stream_set_skip(stream, skip_handler_stringbuf); svn_stream_set_write(stream, write_handler_stringbuf); svn_stream_set_mark(stream, mark_handler_stringbuf); svn_stream_set_seek(stream, seek_handler_stringbuf); + svn_stream_set_data_available(stream, data_available_handler_stringbuf); svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf); return stream; } @@ -1549,6 +1683,15 @@ skip_handler_string(void *baton, apr_size_t len) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_string(void *baton, svn_boolean_t *data_available) +{ + struct string_stream_baton *btn = baton; + + *data_available = ((btn->str->len - btn->amt_read) > 0); + return SVN_NO_ERROR; +} + static svn_boolean_t is_buffered_handler_string(void *baton) { @@ -1569,10 +1712,11 @@ svn_stream_from_string(const svn_string_t *str, baton->str = str; baton->amt_read = 0; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_string); + svn_stream_set_read2(stream, read_handler_string, read_handler_string); svn_stream_set_mark(stream, mark_handler_string); svn_stream_set_seek(stream, seek_handler_string); svn_stream_set_skip(stream, skip_handler_string); + svn_stream_set_data_available(stream, data_available_handler_string); svn_stream__set_is_buffered(stream, is_buffered_handler_string); return stream; } @@ -1588,7 +1732,11 @@ svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stdin"); - *in = svn_stream_from_aprfile2(stdin_file, TRUE, pool); + /* STDIN may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1604,7 +1752,11 @@ svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stdout"); - *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool); + /* STDOUT may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1620,7 +1772,11 @@ svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stderr"); - *err = svn_stream_from_aprfile2(stderr_file, TRUE, pool); + /* STDERR may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1640,7 +1796,7 @@ svn_string_from_stream(svn_string_t **result, { apr_size_t len = SVN__STREAM_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, buffer, &len)); + SVN_ERR(svn_stream_read_full(stream, buffer, &len)); svn_stringbuf_appendbytes(work, buffer, len); if (len < SVN__STREAM_CHUNK_SIZE) @@ -1657,7 +1813,7 @@ svn_string_from_stream(svn_string_t **result, } -/* These are somewhat arbirary, if we ever get good empirical data as to +/* These are somewhat arbitrary, if we ever get good empirical data as to actually valid values, feel free to update them. */ #define BUFFER_BLOCK_SIZE 1024 #define BUFFER_MAX_SIZE 100000 @@ -1665,7 +1821,9 @@ svn_string_from_stream(svn_string_t **result, svn_stream_t * svn_stream_buffered(apr_pool_t *result_pool) { - return svn_stream__from_spillbuf(BUFFER_BLOCK_SIZE, BUFFER_MAX_SIZE, + return svn_stream__from_spillbuf(svn_spillbuf__create(BUFFER_BLOCK_SIZE, + BUFFER_MAX_SIZE, + result_pool), result_pool); } @@ -1721,7 +1879,21 @@ read_handler_lazyopen(void *baton, lazyopen_baton_t *b = baton; SVN_ERR(lazyopen_if_unopened(b)); - SVN_ERR(svn_stream_read(b->real_stream, buffer, len)); + SVN_ERR(svn_stream_read2(b->real_stream, buffer, len)); + + return SVN_NO_ERROR; +} + +/* Implements svn_read_fn_t */ +static svn_error_t * +read_full_handler_lazyopen(void *baton, + char *buffer, + apr_size_t *len) +{ + lazyopen_baton_t *b = baton; + + SVN_ERR(lazyopen_if_unopened(b)); + SVN_ERR(svn_stream_read_full(b->real_stream, buffer, len)); return SVN_NO_ERROR; } @@ -1794,6 +1966,17 @@ seek_handler_lazyopen(void *baton, return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_lazyopen(void *baton, + svn_boolean_t *data_available) +{ + lazyopen_baton_t *b = baton; + + SVN_ERR(lazyopen_if_unopened(b)); + return svn_error_trace(svn_stream_data_available(b->real_stream, + data_available)); +} + /* Implements svn_stream__is_buffered_fn_t */ static svn_boolean_t is_buffered_lazyopen(void *baton) @@ -1823,13 +2006,300 @@ svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func, lob->open_on_close = open_on_close; stream = svn_stream_create(lob, result_pool); - svn_stream_set_read(stream, read_handler_lazyopen); + svn_stream_set_read2(stream, read_handler_lazyopen, + read_full_handler_lazyopen); svn_stream_set_skip(stream, skip_handler_lazyopen); svn_stream_set_write(stream, write_handler_lazyopen); svn_stream_set_close(stream, close_handler_lazyopen); svn_stream_set_mark(stream, mark_handler_lazyopen); svn_stream_set_seek(stream, seek_handler_lazyopen); + svn_stream_set_data_available(stream, data_available_handler_lazyopen); svn_stream__set_is_buffered(stream, is_buffered_lazyopen); return stream; } + +/* Baton for install streams */ +struct install_baton_t +{ + struct baton_apr baton_apr; + const char *tmp_path; +}; + +#ifdef WIN32 + +/* Create and open a tempfile in DIRECTORY. Return its handle and path */ +static svn_error_t * +create_tempfile(HANDLE *hFile, + const char **file_path, + const char *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *unique_name; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + static svn_atomic_t tempname_counter; + int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter) + + GetCurrentProcessId(); + int i = 0; + HANDLE h; + + /* Shares common idea with io.c's temp_file_create */ + + do + { + apr_uint32_t unique_nr; + WCHAR *w_name; + + /* 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 than + disk io) */ + unique_nr = baseNr + 7 * i++; + + + svn_pool_clear(iterpool); + unique_name = svn_dirent_join(directory, + apr_psprintf(iterpool, "svn-%X", + unique_nr), + iterpool); + + SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name, + iterpool)); + + /* Create a completely not-sharable file to avoid indexers, and other + filesystem watchers locking the file while we are still writing. + + We need DELETE privileges to move the file. */ + h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */, + NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + apr_status_t status = apr_get_os_error(); + if (i > 1000) + return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, + svn_error_wrap_apr(status, NULL), + _("Unable to make name in '%s'"), + svn_dirent_local_style(directory, scratch_pool)); + + if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status)) + return svn_error_wrap_apr(status, NULL); + } + } + while (h == INVALID_HANDLE_VALUE); + + *hFile = h; + *file_path = apr_pstrdup(result_pool, unique_name); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Implements svn_close_fn_t */ +static svn_error_t * +install_close(void *baton) +{ + struct install_baton_t *ib = baton; + + /* Flush the data cached in APR, but don't close the file yet */ + SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool)); + + return SVN_NO_ERROR; +} + +#endif /* WIN32 */ + +svn_error_t * +svn_stream__create_for_install(svn_stream_t **install_stream, + const char *tmp_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *file; + struct install_baton_t *ib; + const char *tmp_path; + +#ifdef WIN32 + HANDLE hInstall; + apr_status_t status; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath)); + + SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath, + scratch_pool, scratch_pool)); + + /* Wrap as a standard APR file to allow sharing implementation. + + But do note that some file functions (such as retrieving the name) + don't work on this wrapper. */ + /* ### Buffered, or not? */ + status = apr_os_file_put(&file, &hInstall, + APR_WRITE | APR_BINARY | APR_BUFFERED, + result_pool); + + if (status) + { + CloseHandle(hInstall); + return svn_error_wrap_apr(status, NULL); + } + + tmp_path = svn_dirent_internal_style(tmp_path, result_pool); +#else + + SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath)); + + SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath, + svn_io_file_del_none, + result_pool, scratch_pool)); +#endif + *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool); + + ib = apr_pcalloc(result_pool, sizeof(*ib)); + ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton; + + assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */ + + (*install_stream)->baton = ib; + + ib->tmp_path = tmp_path; + +#ifdef WIN32 + /* Don't close the file on stream close; flush instead */ + svn_stream_set_close(*install_stream, install_close); +#else + /* ### Install pool cleanup handler for tempfile? */ +#endif + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_stream(svn_stream_t *install_stream, + const char *final_abspath, + svn_boolean_t make_parents, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + svn_error_t *err; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath)); +#ifdef WIN32 + err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path, + final_abspath, scratch_pool); + if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_t *err2; + + err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath, + scratch_pool), + scratch_pool); + + if (err2) + return svn_error_trace(svn_error_compose_create(err, err2)); + else + svn_error_clear(err); + + err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path, + final_abspath, scratch_pool); + } + + /* ### rhuijben: I wouldn't be surprised if we later find out that we + have to fall back to close+rename on some specific + error values here, to support some non standard NAS + and filesystem scenarios. */ + if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) + { + /* Rename open files is not supported on this platform: fallback to + svn_io_file_rename2(). */ + svn_error_clear(err); + err = SVN_NO_ERROR; + + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); + } + else + { + return svn_error_compose_create(err, + svn_io_file_close(ib->baton_apr.file, + scratch_pool)); + } +#endif + + err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool); + + /* A missing directory is too common to not cover here. */ + if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_t *err2; + + err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath, + scratch_pool), + scratch_pool); + + if (err2) + /* Creating directory didn't work: Return all errors */ + return svn_error_trace(svn_error_compose_create(err, err2)); + else + /* We could create a directory: retry install */ + svn_error_clear(err); + + SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool)); + } + else + SVN_ERR(err); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_get_info(apr_finfo_t *finfo, + svn_stream_t *install_stream, + apr_int32_t wanted, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + +#ifdef WIN32 + /* On WIN32 the file is still open, so we can obtain the information + from the handle without race conditions */ + apr_status_t status; + + status = apr_file_info_get(finfo, wanted, ib->baton_apr.file); + + if (status) + return svn_error_wrap_apr(status, NULL); +#else + SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool)); +#endif + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_delete(svn_stream_t *install_stream, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + +#ifdef WIN32 + svn_error_t *err; + + /* Mark the file as delete on close to avoid having to reopen + the file as part of the delete handling. */ + err = svn_io__win_delete_file_on_close(ib->baton_apr.file, ib->tmp_path, + scratch_pool); + if (err == SVN_NO_ERROR) + { + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); + return SVN_NO_ERROR; /* File is already gone */ + } + + /* Deleting file on close may be unsupported, so ignore errors and + fallback to svn_io_remove_file2(). */ + svn_error_clear(err); + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); +#endif + + return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE, + scratch_pool)); +} diff --git a/contrib/subversion/subversion/libsvn_subr/string.c b/contrib/subversion/subversion/libsvn_subr/string.c index c3d7fecce..43a1a4ec1 100644 --- a/contrib/subversion/subversion/libsvn_subr/string.c +++ b/contrib/subversion/subversion/libsvn_subr/string.c @@ -26,6 +26,7 @@ #include +#include #include /* for memcpy(), memcmp(), strlen() */ #include @@ -53,9 +54,9 @@ membuf_create(void **data, apr_size_t *size, /* 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. */ + * use some other alignment or none at all. */ minimum_size = APR_ALIGN_DEFAULT(minimum_size); - *data = (!minimum_size ? NULL : apr_palloc(pool, minimum_size)); + *data = apr_palloc(pool, minimum_size); *size = minimum_size; } @@ -78,14 +79,10 @@ membuf_ensure(void **data, apr_size_t *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; @@ -121,7 +118,10 @@ svn_membuf__resize(svn_membuf_t *membuf, apr_size_t size) 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) + + /* If we re-allocated MEMBUF->DATA, it cannot be NULL. + * Statically initialized membuffers (OLD_DATA) may be NULL, though. */ + if (old_data && old_data != membuf->data) memcpy(membuf->data, old_data, old_size); } @@ -151,7 +151,7 @@ string_compare(const char *str1, if (len1 != len2) return FALSE; - /* now the strings must have identical lenghths */ + /* now the strings must have identical lengths */ if ((memcmp(str1, str2, len1)) == 0) return TRUE; @@ -240,7 +240,9 @@ svn_string_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool) new_string->data = data; new_string->len = size; - memcpy(data, bytes, size); + /* If SIZE is 0, NULL is valid for BYTES. */ + if (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 @@ -299,8 +301,9 @@ svn_string_isempty(const svn_string_t *str) 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)); + return (original_string ? svn_string_ncreate(original_string->data, + original_string->len, pool) + : NULL); } @@ -393,7 +396,10 @@ 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); + + /* If SIZE is 0, NULL is valid for BYTES. */ + if (size) + 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 @@ -418,6 +424,17 @@ 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_create_wrap(char *str, apr_pool_t *pool) +{ + svn_stringbuf_t *result = apr_palloc(pool, sizeof(*result)); + result->pool = pool; + result->data = str; + result->len = strlen(str); + result->blocksize = result->len + 1; + + return result; +} svn_stringbuf_t * svn_stringbuf_createv(apr_pool_t *pool, const char *fmt, va_list ap) @@ -579,6 +596,10 @@ svn_stringbuf_appendbytes(svn_stringbuf_t *str, const char *bytes, apr_size_t total_len; void *start_address; + if (!count) + /* Allow BYTES to be NULL by avoiding passing it to memcpy. */ + return; + total_len = str->len + count; /* total size needed */ /* svn_stringbuf_ensure adds 1 for null terminator. */ @@ -595,6 +616,21 @@ svn_stringbuf_appendbytes(svn_stringbuf_t *str, const char *bytes, to null-terminate. */ } +void +svn_stringbuf_appendfill(svn_stringbuf_t *str, + char byte, + apr_size_t count) +{ + apr_size_t new_len = str->len + count; + svn_stringbuf_ensure(str, new_len); + + memset(str->data + str->len, byte, count); + + /* update buffer length and always NUL-terminate it */ + str->len = new_len; + str->data[new_len] = '\0'; +} + void svn_stringbuf_appendstr(svn_stringbuf_t *targetstr, @@ -616,23 +652,22 @@ svn_stringbuf_insert(svn_stringbuf_t *str, const char *bytes, apr_size_t count) { + /* For COUNT==0, we allow BYTES to be NULL. It's a no-op in that case. */ + if (count == 0) + return; + + /* special case: BYTES overlaps with this string -> copy the source */ if (bytes + count > str->data && bytes < str->data + str->blocksize) - { - /* special case: BYTES overlaps with this string -> copy the source */ - const char *temp = apr_pmemdup(str->pool, bytes, count); - svn_stringbuf_insert(str, pos, temp, count); - } - else - { - if (pos > str->len) - pos = str->len; + bytes = apr_pmemdup(str->pool, bytes, count); - 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); + if (pos > str->len) + pos = str->len; - str->len += count; - } + 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 @@ -642,7 +677,7 @@ svn_stringbuf_remove(svn_stringbuf_t *str, { if (pos > str->len) pos = str->len; - if (pos + count > str->len) + if (count > str->len - pos) count = str->len - pos; memmove(str->data + pos, str->data + pos + count, str->len - pos - count + 1); @@ -656,32 +691,35 @@ svn_stringbuf_replace(svn_stringbuf_t *str, const char *bytes, apr_size_t new_count) { - if (bytes + new_count > str->data && bytes < str->data + str->blocksize) + /* For COUNT==0, we allow BYTES to be NULL. + * In that case, this is just a substring removal. */ + if (new_count == 0) { - /* special case: BYTES overlaps with this string -> copy the source */ - const char *temp = apr_pmemdup(str->pool, bytes, new_count); - svn_stringbuf_replace(str, pos, old_count, temp, new_count); + svn_stringbuf_remove(str, pos, old_count); + return; } - 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); - } + /* special case: BYTES overlaps with this string -> copy the source */ + if (bytes + new_count > str->data && bytes < str->data + str->blocksize) + bytes = apr_pmemdup(str->pool, bytes, new_count); - if (old_count != new_count) - memmove(str->data + pos + new_count, str->data + pos + old_count, - str->len - pos - old_count + 1); + if (pos > str->len) + pos = str->len; + if (old_count > str->len - pos) + old_count = str->len - pos; - memcpy(str->data + pos, bytes, new_count); - str->len += new_count - old_count; + 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; } @@ -832,7 +870,7 @@ char * svn_cstring_tokenize(const char *sep, char **str) { char *token; - const char * next; + char *next; char csep; /* check parameters */ @@ -862,8 +900,8 @@ svn_cstring_tokenize(const char *sep, char **str) } else { - *(char *)next = '\0'; - *str = (char *)next + 1; + *next = '\0'; + *str = next + 1; } return token; @@ -1014,17 +1052,33 @@ svn_cstring_atoi(int *n, const char *str) return SVN_NO_ERROR; } - -apr_status_t -svn__strtoff(apr_off_t *offset, const char *buf, char **end, int base) +unsigned long +svn__strtoul(const char* buffer, const char** end) { -#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 + unsigned long result = 0; + + /* this loop will execute in just 2 CPU cycles, confirmed by measurement: + 7 macro-ops (max 4 / cycle => 2 cycles) + 1 load (max 1 / cycle) + 1 jumps (compare + conditional jump == 1 macro op; max 1 / cycle) + 2 arithmetic ops (subtract, increment; max 3 / cycle) + 2 scale-and-add AGU ops (max 3 / cycle) + 1 compiler-generated move operation + dependency chain: temp = result * 4 + result; result = temp * 2 + c + (2 ops with latency 1 => 2 cycles) + */ + while (1) + { + unsigned long c = (unsigned char)*buffer - (unsigned char)'0'; + if (c > 9) + break; + + result = result * 10 + c; + ++buffer; + } + + *end = buffer; + return result; } /* "Precalculated" itoa values for 2 places (including leading zeros). @@ -1123,11 +1177,11 @@ svn__i64toa(char * dest, apr_int64_t number) return svn__ui64toa(dest, (apr_uint64_t)number); *dest = '-'; - return svn__ui64toa(dest + 1, (apr_uint64_t)(0-number)) + 1; + return svn__ui64toa(dest + 1, 0 - (apr_uint64_t)number) + 1; } static void -ui64toa_sep(apr_uint64_t number, char seperator, char *buffer) +ui64toa_sep(apr_uint64_t number, char separator, char *buffer) { apr_size_t length = svn__ui64toa(buffer, number); apr_size_t i; @@ -1135,7 +1189,7 @@ ui64toa_sep(apr_uint64_t number, char seperator, char *buffer) for (i = length; i > 3; i -= 3) { memmove(&buffer[i - 2], &buffer[i - 3], length - i + 3); - buffer[i-3] = seperator; + buffer[i-3] = separator; length++; } @@ -1143,30 +1197,110 @@ ui64toa_sep(apr_uint64_t number, char seperator, char *buffer) } char * -svn__ui64toa_sep(apr_uint64_t number, char seperator, apr_pool_t *pool) +svn__ui64toa_sep(apr_uint64_t number, char separator, apr_pool_t *pool) { char buffer[2 * SVN_INT64_BUFFER_SIZE]; - ui64toa_sep(number, seperator, buffer); + ui64toa_sep(number, separator, buffer); return apr_pstrdup(pool, buffer); } char * -svn__i64toa_sep(apr_int64_t number, char seperator, apr_pool_t *pool) +svn__i64toa_sep(apr_int64_t number, char separator, 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]); + ui64toa_sep((apr_uint64_t)(-number), separator, &buffer[1]); } else - ui64toa_sep((apr_uint64_t)(number), seperator, buffer); + ui64toa_sep((apr_uint64_t)(number), separator, buffer); return apr_pstrdup(pool, buffer); } -unsigned int +apr_size_t +svn__ui64tobase36(char *dest, apr_uint64_t value) +{ + char *dest_start = dest; + if (value < 10) + { + /* pretty frequent and trivial case. Make it fast. */ + *(dest++) = (char)(value) + '0'; + } + else + { + char buffer[SVN_INT64_BUFFER_SIZE]; + char *p = buffer; + + /* write result as little-endian to buffer */ + while (value > 0) + { + char c = (char)(value % 36); + value /= 36; + + *p = (c <= 9) ? (c + '0') : (c - 10 + 'a'); + ++p; + } + + /* copy as big-endian to DEST */ + while (p > buffer) + *(dest++) = *(--p); + } + + *dest = '\0'; + return dest - dest_start; +} + +apr_uint64_t +svn__base36toui64(const char **next, const char *source) +{ + apr_uint64_t result = 0; + apr_uint64_t factor = 1; + int i = 0; + char digits[SVN_INT64_BUFFER_SIZE]; + + /* convert digits to numerical values and count the number of places. + * Also, prevent buffer overflow. */ + while (i < sizeof(digits)) + { + char c = *source; + if (c < 'a') + { + /* includes detection of NUL terminator */ + if (c < '0' || c > '9') + break; + + c -= '0'; + } + else + { + if (c < 'a' || c > 'z') + break; + + c -= 'a' - 10; + } + + digits[i++] = c; + source++; + } + + /* fold digits into the result */ + while (i > 0) + { + result += factor * (apr_uint64_t)digits[--i]; + factor *= 36; + } + + if (next) + *next = source; + + return result; +} + + +apr_size_t svn_cstring__similarity(const char *stra, const char *strb, svn_membuf_t *buffer, apr_size_t *rlcs) { @@ -1178,7 +1312,7 @@ svn_cstring__similarity(const char *stra, const char *strb, return svn_string__similarity(&stringa, &stringb, buffer, rlcs); } -unsigned int +apr_size_t svn_string__similarity(const svn_string_t *stringa, const svn_string_t *stringb, svn_membuf_t *buffer, apr_size_t *rlcs) @@ -1242,7 +1376,7 @@ svn_string__similarity(const svn_string_t *stringa, /* Calculate LCS length of the remainder */ for (pstr = stra; pstr < enda; ++pstr) { - int i; + apr_size_t i; for (i = 1; i <= slots; ++i) { if (*pstr == strb[i-1]) @@ -1267,7 +1401,84 @@ svn_string__similarity(const svn_string_t *stringa, /* Return similarity ratio rounded to 4 significant digits */ if (total) - return(unsigned int)((2000 * lcs + total/2) / total); + return ((2 * SVN_STRING__SIM_RANGE_MAX * lcs + total/2) / total); else - return 1000; + return SVN_STRING__SIM_RANGE_MAX; +} + +apr_size_t +svn_cstring__match_length(const char *a, + const char *b, + apr_size_t max_len) +{ + apr_size_t pos = 0; + +#if SVN_UNALIGNED_ACCESS_IS_OK + + /* Chunky processing is so much faster ... + * + * We can't make this work on architectures that require aligned access + * because A and B will probably have different alignment. So, skipping + * the first few chars until alignment is reached is not an option. + */ + for (; pos + sizeof(apr_size_t) <= max_len; pos += sizeof(apr_size_t)) + if (*(const apr_size_t*)(a + pos) != *(const apr_size_t*)(b + pos)) + break; + +#endif + + for (; pos < max_len; ++pos) + if (a[pos] != b[pos]) + break; + + return pos; +} + +apr_size_t +svn_cstring__reverse_match_length(const char *a, + const char *b, + apr_size_t max_len) +{ + apr_size_t pos = 0; + +#if SVN_UNALIGNED_ACCESS_IS_OK + + /* Chunky processing is so much faster ... + * + * We can't make this work on architectures that require aligned access + * because A and B will probably have different alignment. So, skipping + * the first few chars until alignment is reached is not an option. + */ + for (pos = sizeof(apr_size_t); pos <= max_len; pos += sizeof(apr_size_t)) + if (*(const apr_size_t*)(a - pos) != *(const apr_size_t*)(b - pos)) + break; + + pos -= sizeof(apr_size_t); + +#endif + + /* If we find a mismatch at -pos, pos-1 characters matched. + */ + while (++pos <= max_len) + if (a[0-pos] != b[0-pos]) + return pos - 1; + + /* No mismatch found -> at least MAX_LEN matching chars. + */ + return max_len; +} + +const char * +svn_cstring_skip_prefix(const char *str, const char *prefix) +{ + apr_size_t len = strlen(prefix); + + if (strncmp(str, prefix, len) == 0) + { + return str + len; + } + else + { + return NULL; + } } diff --git a/contrib/subversion/subversion/libsvn_subr/subst.c b/contrib/subversion/subversion/libsvn_subr/subst.c index 223b269a7..388c31a8f 100644 --- a/contrib/subversion/subversion/libsvn_subr/subst.c +++ b/contrib/subversion/subversion/libsvn_subr/subst.c @@ -50,6 +50,7 @@ #include "svn_private_config.h" #include "private/svn_string_private.h" +#include "private/svn_eol_private.h" /** * The textual elements of a detranslated special file. One of these @@ -127,7 +128,7 @@ svn_subst_translation_required(svn_subst_eol_style_t style, * * Important API note: This function is the core of the implementation of * svn_subst_build_keywords (all versions), and as such must implement the - * tolerance of NULL and zero inputs that that function's documention + * tolerance of NULL and zero inputs that that function's documentation * stipulates. * * The format codes: @@ -158,7 +159,7 @@ keyword_printf(const char *fmt, const char *author, apr_pool_t *pool) { - svn_stringbuf_t *value = svn_stringbuf_ncreate("", 0, pool); + svn_stringbuf_t *value = svn_stringbuf_create_empty(pool); const char *cur; size_t n; @@ -1122,28 +1123,42 @@ translate_chunk(svn_stream_t *dst, /* skip current EOL */ len += b->eol_str_len; - /* Check 4 bytes at once to allow for efficient pipelining - and to reduce loop condition overhead. */ - while ((p + len + 4) <= end) + if (b->keywords) { - if (interesting[(unsigned char)p[len]] - || interesting[(unsigned char)p[len+1]] - || interesting[(unsigned char)p[len+2]] - || interesting[(unsigned char)p[len+3]]) - break; - - len += 4; + /* Check 4 bytes at once to allow for efficient pipelining + and to reduce loop condition overhead. */ + while ((p + len + 4) <= end) + { + if (interesting[(unsigned char)p[len]] + || interesting[(unsigned char)p[len+1]] + || interesting[(unsigned char)p[len+2]] + || interesting[(unsigned char)p[len+3]]) + break; + + len += 4; + } + + /* Found an interesting char or EOF in the next 4 bytes. + Find its exact position. */ + while ((p + len) < end + && !interesting[(unsigned char)p[len]]) + ++len; } + else + { + /* use our optimized sub-routine to find the next EOL */ + const char *start = p + len; + const char *eol + = svn_eol__find_eol_start((char *)start, end - start); - /* Found an interesting char or EOF in the next 4 bytes. - Find its exact position. */ - while ((p + len) < end && !interesting[(unsigned char)p[len]]) - ++len; + /* EOL will be NULL if we did not find a line ending */ + len += (eol ? eol : end) - start; + } } while (b->nl_translation_skippable == svn_tristate_true && /* can potentially skip EOLs */ p + len + 2 < end && /* not too close to EOF */ - eol_unchanged (b, p + len)); /* EOL format already ok */ + eol_unchanged(b, p + len)); /* EOL format already ok */ while ((p + len) < end && !interesting[(unsigned char)p[len]]) len++; @@ -1276,7 +1291,7 @@ translated_stream_read(void *baton, svn_stringbuf_setempty(b->readbuf); b->readbuf_off = 0; - SVN_ERR(svn_stream_read(b->stream, b->buf, &readlen)); + SVN_ERR(svn_stream_read_full(b->stream, b->buf, &readlen)); buf_stream = svn_stream_from_stringbuf(b->readbuf, b->iterpool); SVN_ERR(translate_chunk(buf_stream, b->in_baton, b->buf, @@ -1527,7 +1542,8 @@ stream_translated(svn_stream_t *stream, baton->buf = apr_palloc(result_pool, SVN__TRANSLATION_BUF_SIZE); /* Setup the stream methods */ - svn_stream_set_read(s, translated_stream_read); + svn_stream_set_read2(s, NULL /* only full read support */, + translated_stream_read); svn_stream_set_write(s, translated_stream_write); svn_stream_set_close(s, translated_stream_close); svn_stream_set_mark(s, translated_stream_mark); @@ -1697,27 +1713,27 @@ create_special_file_from_stream(svn_stream_t *source, const char *dst, } /* If nothing else worked, write out the internal representation to - a file that can be edited by the user. - - ### this only writes the first line! - */ + a file that can be edited by the user. */ if (create_using_internal_representation) { - apr_file_t *new_file; - SVN_ERR(svn_io_open_unique_file3(&new_file, &dst_tmp, - svn_dirent_dirname(dst, pool), - svn_io_file_del_none, - pool, pool)); - - SVN_ERR(svn_io_file_write_full(new_file, - contents->data, contents->len, NULL, - pool)); - - SVN_ERR(svn_io_file_close(new_file, pool)); + svn_stream_t *new_stream; + apr_size_t len; + + SVN_ERR(svn_stream_open_unique(&new_stream, &dst_tmp, + svn_dirent_dirname(dst, pool), + svn_io_file_del_none, + pool, pool)); + + if (!eof) + svn_stringbuf_appendcstr(contents, "\n"); + len = contents->len; + SVN_ERR(svn_stream_write(new_stream, contents->data, &len)); + SVN_ERR(svn_stream_copy3(svn_stream_disown(source, pool), new_stream, + NULL, NULL, pool)); } /* Do the atomic rename from our temporary location. */ - return svn_io_file_rename(dst_tmp, dst, pool); + return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool)); } @@ -1765,8 +1781,9 @@ svn_subst_copy_and_translate4(const char *src, SVN_ERR(svn_stream_open_readonly(&src_stream, src, pool, pool)); } - return svn_error_trace(create_special_file_from_stream(src_stream, - dst, pool)); + SVN_ERR(create_special_file_from_stream(src_stream, dst, pool)); + + return svn_error_trace(svn_stream_close(src_stream)); } /* else !expand */ @@ -1835,10 +1852,10 @@ read_handler_special(void *baton, char *buffer, apr_size_t *len) if (btn->read_stream) /* We actually found a file to read from */ - return svn_stream_read(btn->read_stream, buffer, len); + return svn_stream_read_full(btn->read_stream, buffer, len); else return svn_error_createf(APR_ENOENT, NULL, - "Can't read special file: File '%s' not found", + _("Can't read special file: File '%s' not found"), svn_dirent_local_style(btn->path, btn->pool)); } @@ -1925,7 +1942,8 @@ svn_subst_stream_from_specialfile(svn_stream_t **stream, baton->write_stream = svn_stream_from_stringbuf(baton->write_content, pool); *stream = svn_stream_create(baton, pool); - svn_stream_set_read(*stream, read_handler_special); + svn_stream_set_read2(*stream, NULL /* only full read support */, + read_handler_special); svn_stream_set_write(*stream, write_handler_special); svn_stream_set_close(*stream, close_handler_special); @@ -1954,7 +1972,7 @@ svn_subst_translate_string2(svn_string_t **new_value, return SVN_NO_ERROR; } - if (encoding && !strcmp(encoding, "UTF-8")) + if (encoding && !strcmp(encoding, "UTF-8")) { val_utf8 = value->data; } diff --git a/contrib/subversion/subversion/libsvn_subr/sysinfo.c b/contrib/subversion/subversion/libsvn_subr/sysinfo.c index f20050cdb..067867224 100644 --- a/contrib/subversion/subversion/libsvn_subr/sysinfo.c +++ b/contrib/subversion/subversion/libsvn_subr/sysinfo.c @@ -23,14 +23,6 @@ -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#define PSAPI_VERSION 1 -#include -#include -#include -#endif - #define APR_WANT_STRFUNC #include @@ -53,6 +45,8 @@ #include "svn_version.h" #include "private/svn_sqlite.h" +#include "private/svn_subr_private.h" +#include "private/svn_utf_private.h" #include "sysinfo.h" #include "svn_private_config.h" @@ -63,6 +57,10 @@ #ifdef SVN_HAVE_MACOS_PLIST #include +#include +# ifndef MAC_OS_X_VERSION_10_6 +# define MAC_OS_X_VERSION_10_6 1060 +# endif #endif #ifdef SVN_HAVE_MACHO_ITERATE @@ -129,7 +127,7 @@ const apr_array_header_t * svn_sysinfo__linked_libs(apr_pool_t *pool) { svn_version_ext_linked_lib_t *lib; - apr_array_header_t *array = apr_array_make(pool, 3, sizeof(*lib)); + apr_array_header_t *array = apr_array_make(pool, 6, sizeof(*lib)); lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); lib->name = "APR"; @@ -145,6 +143,11 @@ svn_sysinfo__linked_libs(apr_pool_t *pool) lib->runtime_version = apr_pstrdup(pool, apu_version_string()); #endif + lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); + lib->name = "Expat"; + lib->compiled_version = apr_pstrdup(pool, svn_xml__compiled_version()); + lib->runtime_version = apr_pstrdup(pool, svn_xml__runtime_version()); + lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); lib->name = "SQLite"; lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version()); @@ -154,6 +157,16 @@ svn_sysinfo__linked_libs(apr_pool_t *pool) lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version()); #endif + lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); + lib->name = "Utf8proc"; + lib->compiled_version = apr_pstrdup(pool, svn_utf__utf8proc_compiled_version()); + lib->runtime_version = apr_pstrdup(pool, svn_utf__utf8proc_runtime_version()); + + lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); + lib->name = "ZLib"; + lib->compiled_version = apr_pstrdup(pool, svn_zlib__compiled_version()); + lib->runtime_version = apr_pstrdup(pool, svn_zlib__runtime_version()); + return array; } @@ -278,7 +291,7 @@ release_name_from_uname(apr_pool_t *pool) #if __linux__ /* Split a stringbuf into a key/value pair. - Return the key, leaving the striped value in the stringbuf. */ + Return the key, leaving the stripped value in the stringbuf. */ static const char * stringbuf_split_key(svn_stringbuf_t *buffer, char delim) { @@ -414,6 +427,63 @@ lsb_release(apr_pool_t *pool) return NULL; } +/* Read /etc/os-release, as documented here: + * http://www.freedesktop.org/software/systemd/man/os-release.html + */ +static const char * +systemd_release(apr_pool_t *pool) +{ + svn_error_t *err; + svn_stream_t *stream; + + /* Open the file. */ + err = svn_stream_open_readonly(&stream, "/etc/os-release", pool, pool); + if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_clear(err); + err = svn_stream_open_readonly(&stream, "/usr/lib/os-release", pool, + pool); + } + if (err) + { + svn_error_clear(err); + return NULL; + } + + /* Look for the PRETTY_NAME line. */ + while (TRUE) + { + svn_stringbuf_t *line; + svn_boolean_t eof; + + err = svn_stream_readline(stream, &line, "\n", &eof, pool); + if (err) + { + svn_error_clear(err); + return NULL; + } + + if (!strncmp(line->data, "PRETTY_NAME=", 12)) + { + svn_stringbuf_t *release_name; + + /* The value may or may not be enclosed by double quotes. We don't + * attempt to strip them. */ + release_name = svn_stringbuf_create(line->data + 12, pool); + svn_error_clear(svn_stream_close(stream)); + svn_stringbuf_strip_whitespace(release_name); + return release_name->data; + } + + if (eof) + break; + } + + /* The file did not contain a PRETTY_NAME line. */ + svn_error_clear(svn_stream_close(stream)); + return NULL; +} + /* Read the whole contents of a file. */ static svn_stringbuf_t * read_file_contents(const char *filename, apr_pool_t *pool) @@ -518,7 +588,7 @@ debian_release(apr_pool_t *pool) return NULL; stringbuf_first_line_only(buffer); - return apr_pstrcat(pool, "Debian ", buffer->data, NULL); + return apr_pstrcat(pool, "Debian ", buffer->data, SVN_VA_NULL); } /* Try to find the Linux distribution name, or return info from uname. */ @@ -531,6 +601,10 @@ linux_release_name(apr_pool_t *pool) Covers, for example, Debian, Ubuntu and SuSE. */ const char *release_name = lsb_release(pool); + /* Try the systemd way (covers Arch). */ + if (!release_name) + release_name = systemd_release(pool); + /* Try RHEL/Fedora/CentOS */ if (!release_name) release_name = redhat_release(pool); @@ -558,22 +632,46 @@ linux_release_name(apr_pool_t *pool) typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO); typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD); -/* Get system and version info, and try to tell the difference - between the native system type and the runtime environment of the - current process. Populate results in SYSINFO, LOCAL_SYSINFO - (optional) and OSINFO. */ +svn_boolean_t +svn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info) +{ + memset(version_info, 0, sizeof(*version_info)); + + version_info->dwOSVersionInfoSize = sizeof(*version_info); + + /* Kill warnings with the Windows 8 and later platform SDK */ +#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000 + /* Windows 8 deprecated the API to retrieve the Windows version to avoid + backwards compatibility problems... It might return a constant version + in future Windows versions... But let's kill the warning. + + We can implementation this using a different function later. */ +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + + /* Prototype supports OSVERSIONINFO */ + return GetVersionExW((LPVOID)version_info); +#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000 +#pragma warning(pop) +#pragma warning(disable: 4996) +#endif +} + +/* Get system info, and try to tell the difference between the native + system type and the runtime environment of the current process. + Populate results in SYSINFO and LOCAL_SYSINFO (optional). */ static BOOL system_info(SYSTEM_INFO *sysinfo, - SYSTEM_INFO *local_sysinfo, - OSVERSIONINFOEXW *osinfo) + SYSTEM_INFO *local_sysinfo) { FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO) GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo"); - ZeroMemory(sysinfo, sizeof *sysinfo); + memset(sysinfo, 0, sizeof *sysinfo); if (local_sysinfo) { - ZeroMemory(local_sysinfo, sizeof *local_sysinfo); + memset(local_sysinfo, 0, sizeof *local_sysinfo); GetSystemInfo(local_sysinfo); if (GetNativeSystemInfo_) GetNativeSystemInfo_(sysinfo); @@ -583,11 +681,6 @@ system_info(SYSTEM_INFO *sysinfo, else GetSystemInfo(sysinfo); - ZeroMemory(osinfo, sizeof *osinfo); - osinfo->dwOSVersionInfoSize = sizeof *osinfo; - if (!GetVersionExW((LPVOID)osinfo)) - return FALSE; - return TRUE; } @@ -620,7 +713,8 @@ win32_canonical_host(apr_pool_t *pool) SYSTEM_INFO local_sysinfo; OSVERSIONINFOEXW osinfo; - if (system_info(&sysinfo, &local_sysinfo, &osinfo)) + if (system_info(&sysinfo, &local_sysinfo) + && svn_sysinfo___fill_windows_version(&osinfo)) { const char *arch = processor_name(&local_sysinfo); const char *machine = processor_name(&sysinfo); @@ -684,7 +778,8 @@ win32_release_name(apr_pool_t *pool) OSVERSIONINFOEXW osinfo; HKEY hkcv; - if (!system_info(&sysinfo, NULL, &osinfo)) + if (!system_info(&sysinfo, NULL) + || !svn_sysinfo___fill_windows_version(&osinfo)) return NULL; if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, @@ -888,71 +983,95 @@ win32_shared_libs(apr_pool_t *pool) #ifdef SVN_HAVE_MACOS_PLIST +/* implements svn_write_fn_t to copy the data into a CFMutableDataRef that's + * in the baton. */ +static svn_error_t * +write_to_cfmutabledata(void *baton, const char *data, apr_size_t *len) +{ + CFMutableDataRef *resource = (CFMutableDataRef *) baton; + + CFDataAppendBytes(*resource, (UInt8 *)data, *len); + + return SVN_NO_ERROR; +} + /* Load the SystemVersion.plist or ServerVersion.plist file into a property list. Set SERVER to TRUE if the file read was ServerVersion.plist. */ static CFDictionaryRef system_version_plist(svn_boolean_t *server, apr_pool_t *pool) { - static const UInt8 server_version[] = + static const char server_version[] = "/System/Library/CoreServices/ServerVersion.plist"; - static const UInt8 system_version[] = + static const char system_version[] = "/System/Library/CoreServices/SystemVersion.plist"; - + svn_stream_t *read_stream, *write_stream; + svn_error_t *err; CFPropertyListRef plist = NULL; - CFDataRef resource = NULL; - CFStringRef errstr = NULL; - CFURLRef url = NULL; - SInt32 errcode; - - url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, - server_version, - sizeof(server_version) - 1, - FALSE); - if (!url) + CFMutableDataRef resource = CFDataCreateMutable(kCFAllocatorDefault, 0); + + /* failed getting the CFMutableDataRef, shouldn't happen */ + if (!resource) return NULL; - if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, - url, &resource, - NULL, NULL, &errcode)) + /* Try to open the plist files to get the data */ + err = svn_stream_open_readonly(&read_stream, server_version, pool, pool); + if (err) { - CFRelease(url); - url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, - system_version, - sizeof(system_version) - 1, - FALSE); - if (!url) - return NULL; - - if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, - url, &resource, - NULL, NULL, &errcode)) + if (!APR_STATUS_IS_ENOENT(err->apr_err)) { - CFRelease(url); + svn_error_clear(err); + CFRelease(resource); return NULL; } else { - CFRelease(url); + svn_error_clear(err); + err = svn_stream_open_readonly(&read_stream, system_version, + pool, pool); + if (err) + { + svn_error_clear(err); + CFRelease(resource); + return NULL; + } + *server = FALSE; } } else { - CFRelease(url); *server = TRUE; } - /* ### CFPropertyListCreateFromXMLData is obsolete, but its - replacement CFPropertyListCreateWithData is only available - from Mac OS 1.6 onward. */ + /* copy the data onto the CFMutableDataRef to allow us to provide it to + * the CoreFoundation functions that parse proprerty lists */ + write_stream = svn_stream_create(&resource, pool); + svn_stream_set_write(write_stream, write_to_cfmutabledata); + err = svn_stream_copy3(read_stream, write_stream, NULL, NULL, pool); + if (err) + { + svn_error_clear(err); + return NULL; + } + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + /* This function is only available from Mac OS 10.6 onward. */ + plist = CFPropertyListCreateWithData(kCFAllocatorDefault, resource, + kCFPropertyListImmutable, + NULL, NULL); +#else /* Mac OS 10.5 or earlier */ + /* This function obsolete and deprecated since Mac OS 10.10. */ plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable, - &errstr); + NULL); +#endif /* MAC_OS_X_VERSION_10_6 */ + if (resource) CFRelease(resource); - if (errstr) - CFRelease(errstr); + + if (!plist) + return NULL; if (CFDictionaryGetTypeID() != CFGetTypeID(plist)) { @@ -1013,16 +1132,17 @@ release_name_from_version(const char *osver) /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */ switch(num) { - case 0: return "Cheetah"; - case 1: return "Puma"; - case 2: return "Jaguar"; - case 3: return "Panther"; - case 4: return "Tiger"; - case 5: return "Leopard"; - case 6: return "Snow Leopard"; - case 7: return "Lion"; - case 8: return "Mountain Lion"; - case 9: return "Mavericks"; + case 0: return "Cheetah"; + case 1: return "Puma"; + case 2: return "Jaguar"; + case 3: return "Panther"; + case 4: return "Tiger"; + case 5: return "Leopard"; + case 6: return "Snow Leopard"; + case 7: return "Lion"; + case 8: return "Mountain Lion"; + case 9: return "Mavericks"; + case 10: return "Yosemite"; } return NULL; diff --git a/contrib/subversion/subversion/libsvn_subr/sysinfo.h b/contrib/subversion/subversion/libsvn_subr/sysinfo.h index 6a6e74d87..5493e4b2b 100644 --- a/contrib/subversion/subversion/libsvn_subr/sysinfo.h +++ b/contrib/subversion/subversion/libsvn_subr/sysinfo.h @@ -62,6 +62,17 @@ const apr_array_header_t *svn_sysinfo__linked_libs(apr_pool_t *pool); */ const apr_array_header_t *svn_sysinfo__loaded_libs(apr_pool_t *pool); +#ifdef WIN32 +/* Obtain the Windows version information as OSVERSIONINFOEXW structure. + * + * !!! Unlike other apis the caller is expected to pre-allocate the buffer + * !!! to allow using this api from the crash handler. + */ +svn_boolean_t +svn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info); +#endif + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/libsvn_subr/temp_serializer.c b/contrib/subversion/subversion/libsvn_subr/temp_serializer.c index 261267a37..c6286a65d 100644 --- a/contrib/subversion/subversion/libsvn_subr/temp_serializer.c +++ b/contrib/subversion/subversion/libsvn_subr/temp_serializer.c @@ -279,6 +279,26 @@ svn_temp_serializer__pop(svn_temp_serializer__context_t *context) context->recycler = old; } +void +svn_temp_serializer__add_leaf(svn_temp_serializer__context_t *context, + const void * const * source_struct, + apr_size_t struct_size) +{ + const void *source = *source_struct; + + /* the serialized structure must be properly aligned */ + if (source) + align_buffer_end(context); + + /* Store the offset at which the struct data that will the appended. + * Write 0 for NULL pointers. */ + store_current_end_pointer(context, source_struct); + + /* finally, actually append the struct contents */ + if (*source_struct) + svn_stringbuf_appendbytes(context->buffer, source, struct_size); +} + /* Serialize a string referenced from the current structure within the * serialization CONTEXT. S must be a reference to the char* pointer in * the original structure so that the correspondence in the serialized @@ -292,7 +312,7 @@ svn_temp_serializer__add_string(svn_temp_serializer__context_t *context, /* Store the offset at which the string data that will the appended. * Write 0 for NULL pointers. Strings don't need special alignment. */ - store_current_end_pointer(context, (const void **)s); + store_current_end_pointer(context, (const void *const *)s); /* append the string data */ if (string) diff --git a/contrib/subversion/subversion/libsvn_subr/time.c b/contrib/subversion/subversion/libsvn_subr/time.c index ccd62691d..7fd6da007 100644 --- a/contrib/subversion/subversion/libsvn_subr/time.c +++ b/contrib/subversion/subversion/libsvn_subr/time.c @@ -34,6 +34,8 @@ #include "svn_error.h" #include "svn_private_config.h" +#include "private/svn_string_private.h" + /*** Code. ***/ @@ -82,7 +84,7 @@ /* Machine parseable part, generated by apr_snprintf. */ #define HUMAN_TIMESTAMP_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %+.2d%.2d" /* Human explanatory part, generated by apr_strftime as "Sat, 01 Jan 2000" */ -#define human_timestamp_format_suffix _(" (%a, %d %b %Y)") +#define HUMAN_TIMESTAMP_FORMAT_SUFFIX _(" (%a, %d %b %Y)") const char * svn_time_to_cstring(apr_time_t when, apr_pool_t *pool) @@ -135,24 +137,24 @@ svn_time_from_cstring(apr_time_t *when, const char *data, apr_pool_t *pool) apr_time_exp_t exploded_time; apr_status_t apr_err; char wday[4], month[4]; - char *c; + const char *c; /* Open-code parsing of the new timestamp format, as this is a hot path for reading the entries file. This format looks like: "2001-08-31T04:24:14.966996Z" */ - exploded_time.tm_year = (apr_int32_t) strtol(data, &c, 10); + exploded_time.tm_year = (apr_int32_t) svn__strtoul(data, &c); if (*c++ != '-') goto fail; - exploded_time.tm_mon = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_mon = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != '-') goto fail; - exploded_time.tm_mday = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_mday = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != 'T') goto fail; - exploded_time.tm_hour = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_hour = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != ':') goto fail; - exploded_time.tm_min = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_min = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != ':') goto fail; - exploded_time.tm_sec = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_sec = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != '.') goto fail; - exploded_time.tm_usec = (apr_int32_t) strtol(c, &c, 10); + exploded_time.tm_usec = (apr_int32_t) svn__strtoul(c, &c); if (*c++ != 'Z') goto fail; exploded_time.tm_year -= 1900; @@ -245,7 +247,7 @@ svn_time_to_human_cstring(apr_time_t when, apr_pool_t *pool) ret = apr_strftime(human_datestr, &retlen, SVN_TIME__MAX_LENGTH - len, - human_timestamp_format_suffix, + HUMAN_TIMESTAMP_FORMAT_SUFFIX, &exploded_time); /* If there was an error, ensure that the string is zero-terminated. */ diff --git a/contrib/subversion/subversion/libsvn_subr/types.c b/contrib/subversion/subversion/libsvn_subr/types.c index 30ebb6585..dba5ee8c2 100644 --- a/contrib/subversion/subversion/libsvn_subr/types.c +++ b/contrib/subversion/subversion/libsvn_subr/types.c @@ -31,36 +31,48 @@ #include "svn_props.h" #include "svn_private_config.h" +#include "private/svn_dep_compat.h" +#include "private/svn_string_private.h" + svn_error_t * svn_revnum_parse(svn_revnum_t *rev, const char *str, const char **endptr) { - char *end; + const char *end; - svn_revnum_t result = strtol(str, &end, 10); + svn_revnum_t result = (svn_revnum_t)svn__strtoul(str, &end); if (endptr) - *endptr = end; + *endptr = str; if (str == end) - return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL, - _("Invalid revision number found parsing '%s'"), - str); - - if (result < 0) + return svn_error_createf + (SVN_ERR_REVNUM_PARSE_FAILURE, NULL, + *str == '-' ? _("Negative revision number found parsing '%s'") + : _("Invalid revision number found parsing '%s'"), + str); + + /* a revision number with more than 9 digits is suspicious. + Have a closer look at those. */ + if (str + 10 <= end) { - /* The end pointer from strtol() is valid, but a negative revision - number is invalid, so move the end pointer back to the - beginning of the string. */ - if (endptr) - *endptr = str; - - return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL, - _("Negative revision number found parsing '%s'"), - str); + /* we support 32 bit revision numbers only. check for overflows */ + if (str + 10 < end) + return svn_error_createf + (SVN_ERR_REVNUM_PARSE_FAILURE, NULL, + _("Revision number longer than 10 digits '%s'"), str); + + /* we support 32 bit revision numbers only. check for overflows */ + if (*str > '2' || (apr_uint32_t)result > APR_INT32_MAX) + return svn_error_createf + (SVN_ERR_REVNUM_PARSE_FAILURE, NULL, + _("Revision number too large '%s'"), str); } + if (endptr) + *endptr = end; + *rev = result; return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_subr/username_providers.c b/contrib/subversion/subversion/libsvn_subr/username_providers.c index a6ef5b3ea..7a4095bca 100644 --- a/contrib/subversion/subversion/libsvn_subr/username_providers.c +++ b/contrib/subversion/subversion/libsvn_subr/username_providers.c @@ -40,12 +40,6 @@ /* File provider */ /*-----------------------------------------------------------------------*/ -/* The key that will be stored on disk. Serves the same role as similar - constants in other providers. */ -#define AUTHN_USERNAME_KEY "username" - - - /*** Username-only Provider ***/ static svn_error_t * username_first_creds(void **credentials, @@ -77,7 +71,8 @@ username_first_creds(void **credentials, svn_error_clear(err); if (! err && creds_hash) { - svn_string_t *str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY); + svn_string_t *str = svn_hash_gets(creds_hash, + SVN_CONFIG_AUTHN_USERNAME_KEY); if (str && str->data) username = str->data; } @@ -125,7 +120,7 @@ username_save_creds(svn_boolean_t *saved, /* Put the credentials in a hash and save it to disk */ creds_hash = apr_hash_make(pool); - svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY, + svn_hash_sets(creds_hash, SVN_CONFIG_AUTHN_USERNAME_KEY, svn_string_create(creds->username, pool)); err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME, realmstring, config_dir, pool); diff --git a/contrib/subversion/subversion/libsvn_subr/utf.c b/contrib/subversion/subversion/libsvn_subr/utf.c index 4f9102d29..7d20d24db 100644 --- a/contrib/subversion/subversion/libsvn_subr/utf.c +++ b/contrib/subversion/subversion/libsvn_subr/utf.c @@ -59,6 +59,12 @@ static const char *SVN_APR_UTF8_CHARSET = "UTF-8"; static svn_mutex__t *xlate_handle_mutex = NULL; static svn_boolean_t assume_native_charset_is_utf8 = FALSE; +#if defined(WIN32) +typedef svn_subr__win32_xlate_t xlate_handle_t; +#else +typedef apr_xlate_t xlate_handle_t; +#endif + /* The xlate handle cache is a global hash table with linked lists of xlate * handles. In multi-threaded environments, a thread "borrows" an xlate * handle from the cache during a translation and puts it back afterwards. @@ -69,7 +75,7 @@ static svn_boolean_t assume_native_charset_is_utf8 = FALSE; * is the number of simultanous handles in use for that key. */ typedef struct xlate_handle_node_t { - apr_xlate_t *handle; + xlate_handle_t *handle; /* FALSE if the handle is not valid, since its pool is being destroyed. */ svn_boolean_t valid; @@ -172,7 +178,7 @@ get_xlate_key(const char *topage, topage = "APR_DEFAULT_CHARSET"; return apr_pstrcat(pool, "svn-utf-", frompage, "to", topage, - "-xlate-handle", (char *)NULL); + "-xlate-handle", SVN_VA_NULL); } /* Atomically replace the content in *MEM with NEW_VALUE and return @@ -184,15 +190,9 @@ static APR_INLINE void* atomic_swap(void * volatile * mem, void *new_value) { #if APR_HAS_THREADS -#if APR_VERSION_AT_LEAST(1,3,0) /* Cast is necessary because of APR bug: https://issues.apache.org/bugzilla/show_bug.cgi?id=50731 */ return apr_atomic_xchgptr((volatile void **)mem, new_value); -#else - /* old APRs don't support atomic swaps. Simply return the - * input to the caller for further proccessing. */ - return new_value; -#endif #else /* no threads - no sync. necessary */ void *old_value = (void*)*mem; @@ -211,7 +211,7 @@ xlate_alloc_handle(xlate_handle_node_t **ret, apr_pool_t *pool) { apr_status_t apr_err; - apr_xlate_t *handle; + xlate_handle_t *handle; const char *name; /* The error handling doesn't support the following cases, since we don't @@ -223,7 +223,7 @@ xlate_alloc_handle(xlate_handle_node_t **ret, /* Try to create a handle. */ #if defined(WIN32) - apr_err = svn_subr__win32_xlate_open((win32_xlate_t **)&handle, topage, + apr_err = svn_subr__win32_xlate_open(&handle, topage, frompage, pool); name = "win32-xlate: "; #else @@ -257,7 +257,7 @@ xlate_alloc_handle(xlate_handle_node_t **ret, later. APR_STRERR will be in the local encoding, not in UTF-8, though. */ svn_strerror(apr_err, apr_strerr, sizeof(apr_strerr)); - return svn_error_createf(SVN_ERR_PLUGIN_LOAD_FAILURE, + return svn_error_createf(SVN_ERR_PLUGIN_LOAD_FAILURE, svn_error_create(apr_err, NULL, apr_strerr), "%s%s", name, errstr); } @@ -480,58 +480,6 @@ get_uton_xlate_handle_node(xlate_handle_node_t **ret, apr_pool_t *pool) } -/* Copy LEN bytes of SRC, converting non-ASCII and zero bytes to ?\nnn - sequences, allocating the result in POOL. */ -static const char * -fuzzy_escape(const char *src, apr_size_t len, apr_pool_t *pool) -{ - const char *src_orig = src, *src_end = src + len; - apr_size_t new_len = 0; - char *new; - const char *new_orig; - - /* First count how big a dest string we'll need. */ - while (src < src_end) - { - if (! svn_ctype_isascii(*src) || *src == '\0') - new_len += 5; /* 5 slots, for "?\XXX" */ - else - new_len += 1; /* one slot for the 7-bit char */ - - src++; - } - - /* Allocate that amount, plus one slot for '\0' character. */ - new = apr_palloc(pool, new_len + 1); - - new_orig = new; - - /* And fill it up. */ - while (src_orig < src_end) - { - if (! svn_ctype_isascii(*src_orig) || src_orig == '\0') - { - /* This is the same format as svn_xml_fuzzy_escape uses, but that - function escapes different characters. Please keep in sync! - ### If we add another fuzzy escape somewhere, we should abstract - ### this out to a common function. */ - apr_snprintf(new, 6, "?\\%03u", (unsigned char) *src_orig); - new += 5; - } - else - { - *new = *src_orig; - new += 1; - } - - src_orig++; - } - - *new = '\0'; - - return new_orig; -} - /* Convert SRC_LENGTH bytes of SRC_DATA in NODE->handle, store the result in *DEST, which is allocated in POOL. */ static svn_error_t * @@ -544,9 +492,8 @@ convert_to_stringbuf(xlate_handle_node_t *node, #ifdef WIN32 apr_status_t apr_err; - apr_err = svn_subr__win32_xlate_to_stringbuf((win32_xlate_t *) node->handle, - src_data, src_length, - dest, pool); + apr_err = svn_subr__win32_xlate_to_stringbuf(node->handle, src_data, + src_length, dest, pool); #else apr_size_t buflen = src_length * 2; apr_status_t apr_err; @@ -609,8 +556,8 @@ convert_to_stringbuf(xlate_handle_node_t *node, (pool, _("Can't convert string from '%s' to '%s':"), node->frompage, node->topage); - err = svn_error_create(apr_err, NULL, fuzzy_escape(src_data, - src_length, pool)); + err = svn_error_create( + apr_err, NULL, svn_utf__fuzzy_escape(src_data, src_length, pool)); return svn_error_create(apr_err, err, errstr); } /* Else, exited due to success. Trim the result buffer down to the @@ -691,7 +638,7 @@ invalid_utf8(const char *data, apr_size_t len, apr_pool_t *pool) valid_txt = apr_pstrcat(pool, valid_txt, apr_psprintf(pool, " %02x", (unsigned char)last[i-valid]), - (char *)NULL); + SVN_VA_NULL); /* 4 invalid octets will guarantee that the faulty octet is displayed */ invalid = data + len - last; @@ -701,7 +648,7 @@ invalid_utf8(const char *data, apr_size_t len, apr_pool_t *pool) invalid_txt = apr_pstrcat(pool, invalid_txt, apr_psprintf(pool, " %02x", (unsigned char)last[i]), - (char *)NULL); + SVN_VA_NULL); return svn_error_createf(APR_EINVAL, NULL, _("Valid UTF-8 data\n(hex:%s)\n" @@ -986,18 +933,6 @@ svn_utf_cstring_from_utf8_ex2(const char **dest, return err; } - -svn_error_t * -svn_utf_cstring_from_utf8_ex(const char **dest, - const char *src, - const char *topage, - const char *convset_key, - apr_pool_t *pool) -{ - return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool); -} - - const char * svn_utf__cstring_from_utf8_fuzzy(const char *src, apr_pool_t *pool, @@ -1007,7 +942,7 @@ svn_utf__cstring_from_utf8_fuzzy(const char *src, const char *escaped, *converted; svn_error_t *err; - escaped = fuzzy_escape(src, strlen(src), pool); + escaped = svn_utf__fuzzy_escape(src, strlen(src), pool); /* Okay, now we have a *new* UTF-8 string, one that's guaranteed to contain only 7-bit bytes :-). Recode to native... */ @@ -1084,3 +1019,240 @@ svn_utf_cstring_from_utf8_string(const char **dest, return err; } + + +/* Insert the given UCS-4 VALUE into BUF at the given OFFSET. */ +static void +membuf_insert_ucs4(svn_membuf_t *buf, apr_size_t offset, apr_int32_t value) +{ + svn_membuf__resize(buf, (offset + 1) * sizeof(value)); + ((apr_int32_t*)buf->data)[offset] = value; +} + +/* TODO: Use compiler intrinsics for byte swaps. */ +#define SWAP_SHORT(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) +#define SWAP_LONG(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) \ + | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff)) + +#define IS_UTF16_LEAD_SURROGATE(c) ((c) >= 0xd800 && (c) <= 0xdbff) +#define IS_UTF16_TRAIL_SURROGATE(c) ((c) >= 0xdc00 && (c) <= 0xdfff) + +svn_error_t * +svn_utf__utf16_to_utf8(const svn_string_t **result, + const apr_uint16_t *utf16str, + apr_size_t utf16len, + svn_boolean_t big_endian, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + static const apr_uint16_t endiancheck = 0xa55a; + const svn_boolean_t arch_big_endian = + (((const char*)&endiancheck)[sizeof(endiancheck) - 1] == '\x5a'); + const svn_boolean_t swap_order = (!big_endian != !arch_big_endian); + + apr_uint16_t lead_surrogate; + apr_size_t length; + apr_size_t offset; + svn_membuf_t ucs4buf; + svn_membuf_t resultbuf; + svn_string_t *res; + + if (utf16len == SVN_UTF__UNKNOWN_LENGTH) + { + const apr_uint16_t *endp = utf16str; + while (*endp++) + ; + utf16len = (endp - utf16str); + } + + svn_membuf__create(&ucs4buf, utf16len * sizeof(apr_int32_t), scratch_pool); + + for (lead_surrogate = 0, length = 0, offset = 0; + offset < utf16len; ++offset) + { + const apr_uint16_t code = + (swap_order ? SWAP_SHORT(utf16str[offset]) : utf16str[offset]); + + if (lead_surrogate) + { + if (IS_UTF16_TRAIL_SURROGATE(code)) + { + /* Combine the lead and trail currogates into a 32-bit code. */ + membuf_insert_ucs4(&ucs4buf, length++, + (0x010000 + + (((lead_surrogate & 0x03ff) << 10) + | (code & 0x03ff)))); + lead_surrogate = 0; + continue; + } + else + { + /* If we didn't find a surrogate pair, just dump the + lead surrogate into the stream. */ + membuf_insert_ucs4(&ucs4buf, length++, lead_surrogate); + lead_surrogate = 0; + } + } + + if ((offset + 1) < utf16len && IS_UTF16_LEAD_SURROGATE(code)) + { + /* Store a lead surrogate that is followed by at least one + code for the next iteration. */ + lead_surrogate = code; + continue; + } + else + membuf_insert_ucs4(&ucs4buf, length++, code); + } + + /* Convert the UCS-4 buffer to UTF-8, assuming an average of 2 bytes + per code point for encoding. The buffer will grow as + necessary. */ + svn_membuf__create(&resultbuf, length * 2, result_pool); + SVN_ERR(svn_utf__encode_ucs4_string( + &resultbuf, ucs4buf.data, length, &length)); + + res = apr_palloc(result_pool, sizeof(*res)); + res->data = resultbuf.data; + res->len = length; + *result = res; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_utf__utf32_to_utf8(const svn_string_t **result, + const apr_int32_t *utf32str, + apr_size_t utf32len, + svn_boolean_t big_endian, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + static const apr_int32_t endiancheck = 0xa5cbbc5a; + const svn_boolean_t arch_big_endian = + (((const char*)&endiancheck)[sizeof(endiancheck) - 1] == '\x5a'); + const svn_boolean_t swap_order = (!big_endian != !arch_big_endian); + + apr_size_t length; + svn_membuf_t resultbuf; + svn_string_t *res; + + if (utf32len == SVN_UTF__UNKNOWN_LENGTH) + { + const apr_int32_t *endp = utf32str; + while (*endp++) + ; + utf32len = (endp - utf32str); + } + + if (swap_order) + { + apr_size_t offset; + svn_membuf_t ucs4buf; + + svn_membuf__create(&ucs4buf, utf32len * sizeof(apr_int32_t), + scratch_pool); + + for (offset = 0; offset < utf32len; ++offset) + { + const apr_int32_t code = SWAP_LONG(utf32str[offset]); + membuf_insert_ucs4(&ucs4buf, offset, code); + } + utf32str = ucs4buf.data; + } + + /* Convert the UCS-4 buffer to UTF-8, assuming an average of 2 bytes + per code point for encoding. The buffer will grow as + necessary. */ + svn_membuf__create(&resultbuf, utf32len * 2, result_pool); + SVN_ERR(svn_utf__encode_ucs4_string( + &resultbuf, utf32str, utf32len, &length)); + + res = apr_palloc(result_pool, sizeof(*res)); + res->data = resultbuf.data; + res->len = length; + *result = res; + return SVN_NO_ERROR; +} + + +#ifdef WIN32 + + +svn_error_t * +svn_utf__win32_utf8_to_utf16(const WCHAR **result, + const char *src, + const WCHAR *prefix, + apr_pool_t *result_pool) +{ + const int utf8_count = strlen(src); + const int prefix_len = (prefix ? lstrlenW(prefix) : 0); + WCHAR *wide_str; + int wide_count; + + if (0 == prefix_len + utf8_count) + { + *result = L""; + return SVN_NO_ERROR; + } + + wide_count = MultiByteToWideChar(CP_UTF8, 0, src, utf8_count, NULL, 0); + if (wide_count == 0) + return svn_error_wrap_apr(apr_get_os_error(), + _("Conversion to UTF-16 failed")); + + wide_str = apr_palloc(result_pool, + (prefix_len + wide_count + 1) * sizeof(*wide_str)); + if (prefix_len) + memcpy(wide_str, prefix, prefix_len * sizeof(*wide_str)); + if (0 == MultiByteToWideChar(CP_UTF8, 0, src, utf8_count, + wide_str + prefix_len, wide_count)) + return svn_error_wrap_apr(apr_get_os_error(), + _("Conversion to UTF-16 failed")); + + wide_str[prefix_len + wide_count] = 0; + *result = wide_str; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_utf__win32_utf16_to_utf8(const char **result, + const WCHAR *src, + const char *prefix, + apr_pool_t *result_pool) +{ + const int wide_count = lstrlenW(src); + const int prefix_len = (prefix ? strlen(prefix) : 0); + char *utf8_str; + int utf8_count; + + if (0 == prefix_len + wide_count) + { + *result = ""; + return SVN_NO_ERROR; + } + + utf8_count = WideCharToMultiByte(CP_UTF8, 0, src, wide_count, + NULL, 0, NULL, FALSE); + if (utf8_count == 0) + return svn_error_wrap_apr(apr_get_os_error(), + _("Conversion from UTF-16 failed")); + + utf8_str = apr_palloc(result_pool, + (prefix_len + utf8_count + 1) * sizeof(*utf8_str)); + if (prefix_len) + memcpy(utf8_str, prefix, prefix_len * sizeof(*utf8_str)); + if (0 == WideCharToMultiByte(CP_UTF8, 0, src, wide_count, + utf8_str + prefix_len, utf8_count, + NULL, FALSE)) + return svn_error_wrap_apr(apr_get_os_error(), + _("Conversion from UTF-16 failed")); + + utf8_str[prefix_len + utf8_count] = 0; + *result = utf8_str; + + return SVN_NO_ERROR; +} + +#endif /* WIN32 */ diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc.c b/contrib/subversion/subversion/libsvn_subr/utf8proc.c new file mode 100644 index 000000000..1e705f5f2 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc.c @@ -0,0 +1,530 @@ +/* + * utf8proc.c: Wrappers for the utf8proc 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. + * ==================================================================== + */ + + + +#include + +#include "private/svn_string_private.h" +#include "private/svn_utf_private.h" +#include "svn_private_config.h" + +#define UTF8PROC_INLINE +/* Somehow utf8proc thinks it is nice to use strlen as an argument name, + while this function is already defined via apr.h */ +#define strlen svn__strlen_var +#include "utf8proc/utf8proc.c" +#undef strlen + + + +const char * +svn_utf__utf8proc_compiled_version(void) +{ + static const char utf8proc_version[] = + APR_STRINGIFY(UTF8PROC_VERSION_MAJOR) "." + APR_STRINGIFY(UTF8PROC_VERSION_MINOR) "." + APR_STRINGIFY(UTF8PROC_VERSION_PATCH); + return utf8proc_version; +} + +const char * +svn_utf__utf8proc_runtime_version(void) +{ + /* Unused static function warning removal hack. */ + SVN_UNUSED(utf8proc_NFD); + SVN_UNUSED(utf8proc_NFC); + SVN_UNUSED(utf8proc_NFKD); + SVN_UNUSED(utf8proc_NFKC); + + return utf8proc_version(); +} + + + +/* Fill the given BUFFER with decomposed UCS-4 representation of the + * UTF-8 STRING. If LENGTH is SVN_UTF__UNKNOWN_LENGTH, assume STRING + * is NUL-terminated; otherwise look only at the first LENGTH bytes in + * STRING. Upon return, BUFFER->data points at an array of UCS-4 + * characters, and return the length of the array. TRANSFORM_FLAGS + * define exactly how the decomposition is performed. + * + * A negative return value is an utf8proc error code and may indicate + * that STRING contains invalid UTF-8 or was so long that an overflow + * occurred. + */ +static ssize_t +unicode_decomposition(int transform_flags, + const char *string, apr_size_t length, + svn_membuf_t *buffer) +{ + const int nullterm = (length == SVN_UTF__UNKNOWN_LENGTH + ? UTF8PROC_NULLTERM : 0); + + for (;;) + { + apr_int32_t *const ucs4buf = buffer->data; + const ssize_t ucs4len = buffer->size / sizeof(*ucs4buf); + const ssize_t result = + utf8proc_decompose((const void*) string, length, ucs4buf, ucs4len, + UTF8PROC_DECOMPOSE | UTF8PROC_STABLE + | transform_flags | nullterm); + + if (result < 0 || result <= ucs4len) + return result; + + /* Increase the decomposition buffer size and retry */ + svn_membuf__ensure(buffer, result * sizeof(*ucs4buf)); + } +} + +/* Fill the given BUFFER with an NFD UCS-4 representation of the UTF-8 + * STRING. If LENGTH is SVN_UTF__UNKNOWN_LENGTH, assume STRING is + * NUL-terminated; otherwise look only at the first LENGTH bytes in + * STRING. Upon return, BUFFER->data points at an array of UCS-4 + * characters and *RESULT_LENGTH contains the length of the array. + * + * A returned error may indicate that STRING contains invalid UTF-8 or + * invalid Unicode codepoints. Any error message comes from utf8proc. + */ +static svn_error_t * +decompose_normalized(apr_size_t *result_length, + const char *string, apr_size_t length, + svn_membuf_t *buffer) +{ + ssize_t result = unicode_decomposition(0, string, length, buffer); + if (result < 0) + return svn_error_create(SVN_ERR_UTF8PROC_ERROR, NULL, + gettext(utf8proc_errmsg(result))); + *result_length = result; + return SVN_NO_ERROR; +} + +/* Fill the given BUFFER with an NFC UTF-8 representation of the UTF-8 + * STRING. If LENGTH is SVN_UTF__UNKNOWN_LENGTH, assume STRING is + * NUL-terminated; otherwise look only at the first LENGTH bytes in + * STRING. Upon return, BUFFER->data points at a NUL-terminated string + * of UTF-8 characters. + * + * A returned error may indicate that STRING contains invalid UTF-8 or + * invalid Unicode codepoints. Any error message comes from utf8proc. + */ +static svn_error_t * +normalize_cstring(apr_size_t *result_length, + const char *string, apr_size_t length, + svn_membuf_t *buffer) +{ + ssize_t result = unicode_decomposition(0, string, length, buffer); + if (result >= 0) + { + svn_membuf__resize(buffer, result * sizeof(apr_int32_t) + 1); + result = utf8proc_reencode(buffer->data, result, + UTF8PROC_COMPOSE | UTF8PROC_STABLE); + } + if (result < 0) + return svn_error_create(SVN_ERR_UTF8PROC_ERROR, NULL, + gettext(utf8proc_errmsg(result))); + *result_length = result; + return SVN_NO_ERROR; +} + +/* Compare two arrays of UCS-4 codes, BUFA of length LENA and BUFB of + * length LENB. Return 0 if they're equal, a negative value if BUFA is + * less than BUFB, otherwise a positive value. + * + * Yes, this is strcmp for known-length UCS-4 strings. + */ +static int +ucs4cmp(const apr_int32_t *bufa, apr_size_t lena, + const apr_int32_t *bufb, apr_size_t lenb) +{ + const apr_size_t len = (lena < lenb ? lena : lenb); + apr_size_t i; + + for (i = 0; i < len; ++i) + { + const int diff = bufa[i] - bufb[i]; + if (diff) + return diff; + } + return (lena == lenb ? 0 : (lena < lenb ? -1 : 1)); +} + +svn_error_t * +svn_utf__normcmp(int *result, + const char *str1, apr_size_t len1, + const char *str2, apr_size_t len2, + svn_membuf_t *buf1, svn_membuf_t *buf2) +{ + apr_size_t buflen1; + apr_size_t buflen2; + + /* Shortcut-circuit the decision if at least one of the strings is empty. */ + const svn_boolean_t empty1 = + (0 == len1 || (len1 == SVN_UTF__UNKNOWN_LENGTH && !*str1)); + const svn_boolean_t empty2 = + (0 == len2 || (len2 == SVN_UTF__UNKNOWN_LENGTH && !*str2)); + if (empty1 || empty2) + { + *result = (empty1 == empty2 ? 0 : (empty1 ? -1 : 1)); + return SVN_NO_ERROR; + } + + SVN_ERR(decompose_normalized(&buflen1, str1, len1, buf1)); + SVN_ERR(decompose_normalized(&buflen2, str2, len2, buf2)); + *result = ucs4cmp(buf1->data, buflen1, buf2->data, buflen2); + return SVN_NO_ERROR; +} + +svn_error_t* +svn_utf__normalize(const char **result, + const char *str, apr_size_t len, + svn_membuf_t *buf) +{ + apr_size_t result_length; + SVN_ERR(normalize_cstring(&result_length, str, len, buf)); + *result = (const char*)(buf->data); + return SVN_NO_ERROR; +} + +/* Decode a single UCS-4 code point to UTF-8, appending the result to BUFFER. + * Assume BUFFER is already filled to *LENGTH and return the new size there. + * This function does *not* nul-terminate the stringbuf! + * + * A returned error indicates that the codepoint is invalid. + */ +static svn_error_t * +encode_ucs4(svn_membuf_t *buffer, apr_int32_t ucs4chr, apr_size_t *length) +{ + apr_size_t utf8len; + + if (buffer->size - *length < 4) + svn_membuf__resize(buffer, buffer->size + 4); + + utf8len = utf8proc_encode_char(ucs4chr, ((uint8_t*)buffer->data + *length)); + if (!utf8len) + return svn_error_createf(SVN_ERR_UTF8PROC_ERROR, NULL, + _("Invalid Unicode character U+%04lX"), + (long)ucs4chr); + *length += utf8len; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_utf__encode_ucs4_string(svn_membuf_t *buffer, + const apr_int32_t *ucs4str, + apr_size_t length, + apr_size_t *result_length) +{ + *result_length = 0; + while (length-- > 0) + SVN_ERR(encode_ucs4(buffer, *ucs4str++, result_length)); + svn_membuf__resize(buffer, *result_length + 1); + ((char*)buffer->data)[*result_length] = '\0'; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_utf__glob(svn_boolean_t *match, + const char *pattern, apr_size_t pattern_len, + const char *string, apr_size_t string_len, + const char *escape, apr_size_t escape_len, + svn_boolean_t sql_like, + svn_membuf_t *pattern_buf, + svn_membuf_t *string_buf, + svn_membuf_t *temp_buf) +{ + apr_size_t patternbuf_len; + apr_size_t tempbuf_len; + + /* If we're in GLOB mode, we don't do custom escape chars. */ + if (escape && !sql_like) + return svn_error_create(SVN_ERR_UTF8_GLOB, NULL, + _("Cannot use a custom escape token" + " in glob matching mode")); + + /* Convert the patern to NFD UTF-8. We can't use the UCS-4 result + because apr_fnmatch can't handle it.*/ + SVN_ERR(decompose_normalized(&tempbuf_len, pattern, pattern_len, temp_buf)); + if (!sql_like) + SVN_ERR(svn_utf__encode_ucs4_string(pattern_buf, temp_buf->data, + tempbuf_len, &patternbuf_len)); + else + { + /* Convert a LIKE pattern to a GLOB pattern that apr_fnmatch can use. */ + const apr_int32_t *like = temp_buf->data; + apr_int32_t ucs4esc; + svn_boolean_t escaped; + apr_size_t i; + + if (!escape) + ucs4esc = -1; /* Definitely an invalid UCS-4 character. */ + else + { + const int nullterm = (escape_len == SVN_UTF__UNKNOWN_LENGTH + ? UTF8PROC_NULLTERM : 0); + ssize_t result = + utf8proc_decompose((const void*) escape, escape_len, &ucs4esc, 1, + UTF8PROC_DECOMPOSE | UTF8PROC_STABLE | nullterm); + if (result < 0) + return svn_error_create(SVN_ERR_UTF8PROC_ERROR, NULL, + gettext(utf8proc_errmsg(result))); + if (result == 0 || result > 1) + return svn_error_create(SVN_ERR_UTF8_GLOB, NULL, + _("Escape token must be one character")); + if ((ucs4esc & 0xFF) != ucs4esc) + return svn_error_createf(SVN_ERR_UTF8_GLOB, NULL, + _("Invalid escape character U+%04lX"), + (long)ucs4esc); + } + + patternbuf_len = 0; + svn_membuf__ensure(pattern_buf, tempbuf_len + 1); + for (i = 0, escaped = FALSE; i < tempbuf_len; ++i, ++like) + { + if (*like == ucs4esc && !escaped) + { + svn_membuf__resize(pattern_buf, patternbuf_len + 1); + ((char*)pattern_buf->data)[patternbuf_len++] = '\\'; + escaped = TRUE; + } + else if (escaped) + { + SVN_ERR(encode_ucs4(pattern_buf, *like, &patternbuf_len)); + escaped = FALSE; + } + else + { + if ((*like == '[' || *like == '\\') && !escaped) + { + /* Escape brackets and backslashes which are always + literals in LIKE patterns. */ + svn_membuf__resize(pattern_buf, patternbuf_len + 1); + ((char*)pattern_buf->data)[patternbuf_len++] = '\\'; + escaped = TRUE; + --i; --like; + continue; + } + + /* Replace LIKE wildcards with their GLOB equivalents. */ + if (*like == '%' || *like == '_') + { + const char wildcard = (*like == '%' ? '*' : '?'); + svn_membuf__resize(pattern_buf, patternbuf_len + 1); + ((char*)pattern_buf->data)[patternbuf_len++] = wildcard; + } + else + SVN_ERR(encode_ucs4(pattern_buf, *like, &patternbuf_len)); + } + } + svn_membuf__resize(pattern_buf, patternbuf_len + 1); + ((char*)pattern_buf->data)[patternbuf_len] = '\0'; + } + + /* Now normalize the string */ + SVN_ERR(decompose_normalized(&tempbuf_len, string, string_len, temp_buf)); + SVN_ERR(svn_utf__encode_ucs4_string(string_buf, temp_buf->data, + tempbuf_len, &tempbuf_len)); + + *match = !apr_fnmatch(pattern_buf->data, string_buf->data, 0); + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_utf__is_normalized(const char *string, apr_pool_t *scratch_pool) +{ + svn_error_t *err; + svn_membuf_t buffer; + apr_size_t result_length; + const apr_size_t length = strlen(string); + svn_membuf__create(&buffer, length * sizeof(apr_int32_t), scratch_pool); + err = normalize_cstring(&result_length, string, length, &buffer); + if (err) + { + svn_error_clear(err); + return FALSE; + } + return (length == result_length && 0 == strcmp(string, buffer.data)); +} + +const char * +svn_utf__fuzzy_escape(const char *src, apr_size_t length, apr_pool_t *pool) +{ + /* Hexadecimal digits for code conversion. */ + static const char digits[] = "0123456789ABCDEF"; + + /* Flags used for Unicode decomposition. */ + static const int decomp_flags = ( + UTF8PROC_COMPAT | UTF8PROC_STABLE | UTF8PROC_LUMP + | UTF8PROC_NLF2LF | UTF8PROC_STRIPCC | UTF8PROC_STRIPMARK); + + svn_stringbuf_t *result; + svn_membuf_t buffer; + ssize_t decomp_length; + ssize_t len; + + /* Decompose to a non-reversible compatibility format. */ + svn_membuf__create(&buffer, length * sizeof(apr_int32_t), pool); + decomp_length = unicode_decomposition(decomp_flags, src, length, &buffer); + if (decomp_length < 0) + { + svn_membuf_t part; + apr_size_t done, prev; + + /* The only other error we can receive here indicates an integer + overflow due to the length of the input string. Not very + likely, but we certainly shouldn't continue in that case. */ + SVN_ERR_ASSERT_NO_RETURN(decomp_length == UTF8PROC_ERROR_INVALIDUTF8); + + /* Break the decomposition into parts that are valid UTF-8, and + bytes that are not. Represent the invalid bytes in the target + erray by their negative value. This works because utf8proc + will not generate Unicode code points with values larger than + U+10FFFF. */ + svn_membuf__create(&part, sizeof(apr_int32_t), pool); + decomp_length = 0; + done = prev = 0; + while (done < length) + { + apr_int32_t uc; + + while (done < length) + { + len = utf8proc_iterate((uint8_t*)src + done, length - done, &uc); + if (len < 0) + break; + done += len; + } + + /* Decompose the valid part */ + if (done > prev) + { + len = unicode_decomposition( + decomp_flags, src + prev, done - prev, &part); + SVN_ERR_ASSERT_NO_RETURN(len > 0); + svn_membuf__resize( + &buffer, (decomp_length + len) * sizeof(apr_int32_t)); + memcpy((apr_int32_t*)buffer.data + decomp_length, + part.data, len * sizeof(apr_int32_t)); + decomp_length += len; + prev = done; + } + + /* What follows could be a valid UTF-8 sequence, but not + a valid Unicode character. */ + if (done < length) + { + const char *last; + + /* Determine the length of the UTF-8 sequence */ + const char *const p = src + done; + len = utf8proc_utf8class[(uint8_t)*p]; + + /* Check if the multi-byte sequence is valid UTF-8. */ + if (len > 1 && len <= (apr_ssize_t)(length - done)) + last = svn_utf__last_valid(p, len); + else + last = NULL; + + /* Might not be a valid UTF-8 sequence at all */ + if (!last || (last && last - p < len)) + { + uc = -((apr_int32_t)(*p & 0xff)); + len = 1; + } + else + { + switch (len) + { + /* Decode the UTF-8 sequence without validation. */ + case 2: + uc = ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); + break; + case 3: + uc = (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f)); + break; + case 4: + uc = (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f)); + break; + default: + SVN_ERR_ASSERT_NO_RETURN( + !"Unexpected invalid UTF-8 byte"); + } + + } + + svn_membuf__resize( + &buffer, (decomp_length + 1) * sizeof(apr_int32_t)); + ((apr_int32_t*)buffer.data)[decomp_length++] = uc; + done += len; + prev = done; + } + } + } + + /* Scan the result and deleting any combining diacriticals and + inserting placeholders where any non-ascii characters remain. */ + result = svn_stringbuf_create_ensure(decomp_length, pool); + for (len = 0; len < decomp_length; ++len) + { + const apr_int32_t cp = ((apr_int32_t*)buffer.data)[len]; + if (cp > 0 && cp < 127) + svn_stringbuf_appendbyte(result, (char)cp); + else if (cp == 0) + svn_stringbuf_appendcstr(result, "\\0"); + else if (cp < 0) + { + const apr_int32_t rcp = ((-cp) & 0xff); + svn_stringbuf_appendcstr(result, "?\\"); + svn_stringbuf_appendbyte(result, digits[(rcp & 0x00f0) >> 4]); + svn_stringbuf_appendbyte(result, digits[(rcp & 0x000f)]); + } + else + { + if (utf8proc_codepoint_valid(cp)) + { + const utf8proc_property_t *prop = utf8proc_get_property(cp); + if (prop->combining_class != 0) + continue; /* Combining mark; ignore */ + svn_stringbuf_appendcstr(result, "{U+"); + } + else + svn_stringbuf_appendcstr(result, "{U?"); + if (cp > 0xffff) + { + svn_stringbuf_appendbyte(result, digits[(cp & 0xf00000) >> 20]); + svn_stringbuf_appendbyte(result, digits[(cp & 0x0f0000) >> 16]); + } + svn_stringbuf_appendbyte(result, digits[(cp & 0xf000) >> 12]); + svn_stringbuf_appendbyte(result, digits[(cp & 0x0f00) >> 8]); + svn_stringbuf_appendbyte(result, digits[(cp & 0x00f0) >> 4]); + svn_stringbuf_appendbyte(result, digits[(cp & 0x000f)]); + svn_stringbuf_appendbyte(result, '}'); + } + } + + return result->data; +} diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc/LICENSE b/contrib/subversion/subversion/libsvn_subr/utf8proc/LICENSE new file mode 100644 index 000000000..504e4c5c0 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc/LICENSE @@ -0,0 +1,64 @@ + +Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +This software distribution contains derived data from a modified version of +the Unicode data files. The following license applies to that data: + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Unicode data files and any associated documentation (the "Data +Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, and +to permit persons to whom the Data Files or Software are furnished to do +so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File or +in the Software as well as in the documentation associated with the Data +File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR +CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + + +Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be +registered in some jurisdictions. All other trademarks and registered +trademarks mentioned herein are the property of their respective owners. + diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc/README b/contrib/subversion/subversion/libsvn_subr/utf8proc/README new file mode 100644 index 000000000..e72ffff3b --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc/README @@ -0,0 +1,116 @@ + +Please read the LICENSE file, which is shipping with this software. + + +*** QUICK START *** + +For compilation of the C library call "make c-library", for compilation of +the ruby library call "make ruby-library" and for compilation of the +PostgreSQL extension call "make pgsql-library". + +For ruby you can also create a gem-file by calling "make ruby-gem". + +"make all" can be used to build everything, but both ruby and PostgreSQL +installations are required in this case. + + +*** GENERAL INFORMATION *** + +The C library is found in this directory after successful compilation and +is named "libutf8proc.a" and "libutf8proc.so". The ruby library consists of +the files "utf8proc.rb" and "utf8proc_native.so", which are found in the +subdirectory "ruby/". If you chose to create a gem-file it is placed in the +"ruby/gem" directory. The PostgreSQL extension is named "utf8proc_pgsql.so" +and resides in the "pgsql/" directory. + +Both the ruby library and the PostgreSQL extension are built as stand-alone +libraries and are therefore not dependent the dynamic version of the +C library files, but this behaviour might change in future releases. + +The Unicode version being supported is 5.0.0. +Note: Version 4.1.0 of Unicode Standard Annex #29 was used, as + version 5.0.0 had not been available at the time of implementation. + +For Unicode normalizations, the following options have to be used: +Normalization Form C: STABLE, COMPOSE +Normalization Form D: STABLE, DECOMPOSE +Normalization Form KC: STABLE, COMPOSE, COMPAT +Normalization Form KD: STABLE, DECOMPOSE, COMPAT + + +*** C LIBRARY *** + +The documentation for the C library is found in the utf8proc.h header file. +"utf8proc_map" is most likely function you will be using for mapping UTF-8 +strings, unless you want to allocate memory yourself. + + +*** RUBY API *** + +The ruby library adds the methods "utf8map" and "utf8map!" to the String +class, and the method "utf8" to the Integer class. + +The String#utf8map method does the same as the "utf8proc_map" C function. +Options for the mapping procedure are passed as symbols, i.e: +"Hello".utf8map(:casefold) => "hello" + +The descriptions of all options are found in the C header file +"utf8proc.h". Please notice that the according symbols in ruby are all +lowercase. + +String#utf8map! is the destructive function in the meaning that the string +is replaced by the result. + +There are shortcuts for the 4 normalization forms specified by Unicode: +String#utf8nfd, String#utf8nfd!, +String#utf8nfc, String#utf8nfc!, +String#utf8nfkd, String#utf8nfkd!, +String#utf8nfkc, String#utf8nfkc! + +The method Integer#utf8 returns a UTF-8 string, which is containing the +unicode char given by the code point. +0x000A.utf8 => "\n" +0x2028.utf8 => "\342\200\250" + + +*** POSTGRESQL API *** + +For PostgreSQL there are two SQL functions supplied named "unifold" and +"unistrip". These functions function can be used to prepare index fields in +order to be folded in a way where string-comparisons make more sense, e.g. +where "bathtub" == "bathtub" +or "Hello World" == "hello world". + +CREATE TABLE people ( + id serial8 primary key, + name text, + CHECK (unifold(name) NOTNULL) +); +CREATE INDEX name_idx ON people (unifold(name)); +SELECT * FROM people WHERE unifold(name) = unifold('John Doe'); + +The function "unistrip" removes character marks like accents or diaeresis, +while "unifold" keeps then. + +NOTICE: The outputs of the function can change between releases, as + utf8proc does not follow a versioning stability policy. You have to + rebuild your database indicies, if you upgrade to a newer version + of utf8proc. + + +*** TODO *** + +- detect stable code points and process segments independently in order to + save memory +- do a quick check before normalizing strings to optimize speed +- support stream processing + + +*** CONTACT *** + +If you find any bugs or experience difficulties in compiling this software, +please contact us: + +Project page: http://www.public-software-group.org/utf8proc + + diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.c b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.c new file mode 100644 index 000000000..2993c8fe2 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * This library contains derived data from a modified version of the + * Unicode data files. + * + * The original data files are available at + * http://www.unicode.org/Public/UNIDATA/ + * + * Please notice the copyright statement in the file "utf8proc_data.c". + */ + + +/* + * File name: utf8proc.c + * + * Description: + * Implementation of libutf8proc. + */ + + +#include "utf8proc.h" +#include "utf8proc_data.c" + + +UTF8PROC_DATA +const int8_t utf8proc_utf8class[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#define UTF8PROC_HANGUL_SBASE 0xAC00 +#define UTF8PROC_HANGUL_LBASE 0x1100 +#define UTF8PROC_HANGUL_VBASE 0x1161 +#define UTF8PROC_HANGUL_TBASE 0x11A7 +#define UTF8PROC_HANGUL_LCOUNT 19 +#define UTF8PROC_HANGUL_VCOUNT 21 +#define UTF8PROC_HANGUL_TCOUNT 28 +#define UTF8PROC_HANGUL_NCOUNT 588 +#define UTF8PROC_HANGUL_SCOUNT 11172 +/* END is exclusive */ +#define UTF8PROC_HANGUL_L_START 0x1100 +#define UTF8PROC_HANGUL_L_END 0x115A +#define UTF8PROC_HANGUL_L_FILLER 0x115F +#define UTF8PROC_HANGUL_V_START 0x1160 +#define UTF8PROC_HANGUL_V_END 0x11A3 +#define UTF8PROC_HANGUL_T_START 0x11A8 +#define UTF8PROC_HANGUL_T_END 0x11FA +#define UTF8PROC_HANGUL_S_START 0xAC00 +#define UTF8PROC_HANGUL_S_END 0xD7A4 + + +#define UTF8PROC_BOUNDCLASS_START 0 +#define UTF8PROC_BOUNDCLASS_OTHER 1 +#define UTF8PROC_BOUNDCLASS_CR 2 +#define UTF8PROC_BOUNDCLASS_LF 3 +#define UTF8PROC_BOUNDCLASS_CONTROL 4 +#define UTF8PROC_BOUNDCLASS_EXTEND 5 +#define UTF8PROC_BOUNDCLASS_L 6 +#define UTF8PROC_BOUNDCLASS_V 7 +#define UTF8PROC_BOUNDCLASS_T 8 +#define UTF8PROC_BOUNDCLASS_LV 9 +#define UTF8PROC_BOUNDCLASS_LVT 10 + + +UTF8PROC_API +const char *utf8proc_version(void) { + return "1.1.5"; +} + +/* + * This macro tells translators that string X should be translated, + * but does not look up the translation at run time. This is standard + * GNU gettext notation for annotating compile-time constant strings. + */ +#ifndef N_ +#define N_(x) x +#endif + +UTF8PROC_API +const char *utf8proc_errmsg(ssize_t errcode) { + switch (errcode) { + case UTF8PROC_ERROR_NOMEM: + return N_("Memory for processing UTF-8 data could not be allocated."); + case UTF8PROC_ERROR_OVERFLOW: + return N_("UTF-8 string is too long to be processed."); + case UTF8PROC_ERROR_INVALIDUTF8: + return N_("Invalid UTF-8 string"); + case UTF8PROC_ERROR_NOTASSIGNED: + return N_("Unassigned Unicode code point found in UTF-8 string."); + case UTF8PROC_ERROR_INVALIDOPTS: + return N_("Invalid options for UTF-8 processing chosen."); + default: + return N_("An unknown error occured while processing UTF-8 data."); + } +} + +UTF8PROC_API +ssize_t utf8proc_iterate( + const uint8_t *str, ssize_t strlen, int32_t *dst +) { + int length; + int i; + int32_t uc = -1; + *dst = -1; + if (!strlen) return 0; + length = utf8proc_utf8class[str[0]]; + if (!length) return UTF8PROC_ERROR_INVALIDUTF8; + if (strlen >= 0 && length > strlen) return UTF8PROC_ERROR_INVALIDUTF8; + for (i=1; i= 0xD800 && uc < 0xE000) || + (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1; + break; + case 4: + uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12) + + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F); + if (uc < 0x10000 || uc >= 0x110000) uc = -1; + break; + } + if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE)) + return UTF8PROC_ERROR_INVALIDUTF8; + *dst = uc; + return length; +} + +UTF8PROC_API +bool utf8proc_codepoint_valid(int32_t uc) { + if (uc < 0 || uc >= 0x110000 || + ((uc & 0xFFFF) >= 0xFFFE) || (uc >= 0xD800 && uc < 0xE000) || + (uc >= 0xFDD0 && uc < 0xFDF0)) return false; + else return true; +} + +UTF8PROC_API +ssize_t utf8proc_encode_char(int32_t uc, uint8_t *dst) { + if (uc < 0x00) { + return 0; + } else if (uc < 0x80) { + dst[0] = (uint8_t)uc; + return 1; + } else if (uc < 0x800) { + dst[0] = 0xC0 + (uint8_t)(uc >> 6); + dst[1] = 0x80 + (uc & 0x3F); + return 2; + } else if (uc == 0xFFFF) { + dst[0] = 0xFF; + return 1; + } else if (uc == 0xFFFE) { + dst[0] = 0xFE; + return 1; + } else if (uc < 0x10000) { + dst[0] = 0xE0 + (uint8_t)(uc >> 12); + dst[1] = 0x80 + ((uc >> 6) & 0x3F); + dst[2] = 0x80 + (uc & 0x3F); + return 3; + } else if (uc < 0x110000) { + dst[0] = 0xF0 + (uint8_t)(uc >> 18); + dst[1] = 0x80 + ((uc >> 12) & 0x3F); + dst[2] = 0x80 + ((uc >> 6) & 0x3F); + dst[3] = 0x80 + (uc & 0x3F); + return 4; + } else return 0; +} + +UTF8PROC_API +const utf8proc_property_t *utf8proc_get_property(int32_t uc) { + /* ASSERT: uc >= 0 && uc < 0x110000 */ + return utf8proc_properties + ( + utf8proc_stage2table[ + utf8proc_stage1table[uc >> 8] + (uc & 0xFF) + ] + ); +} + +#define utf8proc_decompose_lump(replacement_uc) \ + return utf8proc_decompose_char((replacement_uc), dst, bufsize, \ + options & ~UTF8PROC_LUMP, last_boundclass) + +UTF8PROC_API +ssize_t utf8proc_decompose_char(int32_t uc, int32_t *dst, ssize_t bufsize, + int options, int *last_boundclass) { + /* ASSERT: uc >= 0 && uc < 0x110000 */ + const utf8proc_property_t *property; + utf8proc_propval_t category; + int32_t hangul_sindex; + property = utf8proc_get_property(uc); + category = property->category; + hangul_sindex = uc - UTF8PROC_HANGUL_SBASE; + if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { + if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT) { + int32_t hangul_tindex; + if (bufsize >= 1) { + dst[0] = UTF8PROC_HANGUL_LBASE + + hangul_sindex / UTF8PROC_HANGUL_NCOUNT; + if (bufsize >= 2) dst[1] = UTF8PROC_HANGUL_VBASE + + (hangul_sindex % UTF8PROC_HANGUL_NCOUNT) / UTF8PROC_HANGUL_TCOUNT; + } + hangul_tindex = hangul_sindex % UTF8PROC_HANGUL_TCOUNT; + if (!hangul_tindex) return 2; + if (bufsize >= 3) dst[2] = UTF8PROC_HANGUL_TBASE + hangul_tindex; + return 3; + } + } + if (options & UTF8PROC_REJECTNA) { + if (!category) return UTF8PROC_ERROR_NOTASSIGNED; + } + if (options & UTF8PROC_IGNORE) { + if (property->ignorable) return 0; + } + if (options & UTF8PROC_LUMP) { + if (category == UTF8PROC_CATEGORY_ZS) utf8proc_decompose_lump(0x0020); + if (uc == 0x2018 || uc == 0x2019 || uc == 0x02BC || uc == 0x02C8) + utf8proc_decompose_lump(0x0027); + if (category == UTF8PROC_CATEGORY_PD || uc == 0x2212) + utf8proc_decompose_lump(0x002D); + if (uc == 0x2044 || uc == 0x2215) utf8proc_decompose_lump(0x002F); + if (uc == 0x2236) utf8proc_decompose_lump(0x003A); + if (uc == 0x2039 || uc == 0x2329 || uc == 0x3008) + utf8proc_decompose_lump(0x003C); + if (uc == 0x203A || uc == 0x232A || uc == 0x3009) + utf8proc_decompose_lump(0x003E); + if (uc == 0x2216) utf8proc_decompose_lump(0x005C); + if (uc == 0x02C4 || uc == 0x02C6 || uc == 0x2038 || uc == 0x2303) + utf8proc_decompose_lump(0x005E); + if (category == UTF8PROC_CATEGORY_PC || uc == 0x02CD) + utf8proc_decompose_lump(0x005F); + if (uc == 0x02CB) utf8proc_decompose_lump(0x0060); + if (uc == 0x2223) utf8proc_decompose_lump(0x007C); + if (uc == 0x223C) utf8proc_decompose_lump(0x007E); + if ((options & UTF8PROC_NLF2LS) && (options & UTF8PROC_NLF2PS)) { + if (category == UTF8PROC_CATEGORY_ZL || + category == UTF8PROC_CATEGORY_ZP) + utf8proc_decompose_lump(0x000A); + } + } + if (options & UTF8PROC_STRIPMARK) { + if (category == UTF8PROC_CATEGORY_MN || + category == UTF8PROC_CATEGORY_MC || + category == UTF8PROC_CATEGORY_ME) return 0; + } + if (options & UTF8PROC_CASEFOLD) { + if (property->casefold_mapping) { + const int32_t *casefold_entry; + ssize_t written = 0; + for (casefold_entry = property->casefold_mapping; + *casefold_entry >= 0; casefold_entry++) { + written += utf8proc_decompose_char(*casefold_entry, dst+written, + (bufsize > written) ? (bufsize - written) : 0, options, + last_boundclass); + if (written < 0) return UTF8PROC_ERROR_OVERFLOW; + } + return written; + } + } + if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { + if (property->decomp_mapping && + (!property->decomp_type || (options & UTF8PROC_COMPAT))) { + const int32_t *decomp_entry; + ssize_t written = 0; + for (decomp_entry = property->decomp_mapping; + *decomp_entry >= 0; decomp_entry++) { + written += utf8proc_decompose_char(*decomp_entry, dst+written, + (bufsize > written) ? (bufsize - written) : 0, options, + last_boundclass); + if (written < 0) return UTF8PROC_ERROR_OVERFLOW; + } + return written; + } + } + if (options & UTF8PROC_CHARBOUND) { + bool boundary; + int tbc, lbc; + tbc = + (uc == 0x000D) ? UTF8PROC_BOUNDCLASS_CR : + (uc == 0x000A) ? UTF8PROC_BOUNDCLASS_LF : + ((category == UTF8PROC_CATEGORY_ZL || + category == UTF8PROC_CATEGORY_ZP || + category == UTF8PROC_CATEGORY_CC || + category == UTF8PROC_CATEGORY_CF) && + !(uc == 0x200C || uc == 0x200D)) ? UTF8PROC_BOUNDCLASS_CONTROL : + property->extend ? UTF8PROC_BOUNDCLASS_EXTEND : + ((uc >= UTF8PROC_HANGUL_L_START && uc < UTF8PROC_HANGUL_L_END) || + uc == UTF8PROC_HANGUL_L_FILLER) ? UTF8PROC_BOUNDCLASS_L : + (uc >= UTF8PROC_HANGUL_V_START && uc < UTF8PROC_HANGUL_V_END) ? + UTF8PROC_BOUNDCLASS_V : + (uc >= UTF8PROC_HANGUL_T_START && uc < UTF8PROC_HANGUL_T_END) ? + UTF8PROC_BOUNDCLASS_T : + (uc >= UTF8PROC_HANGUL_S_START && uc < UTF8PROC_HANGUL_S_END) ? ( + ((uc-UTF8PROC_HANGUL_SBASE) % UTF8PROC_HANGUL_TCOUNT == 0) ? + UTF8PROC_BOUNDCLASS_LV : UTF8PROC_BOUNDCLASS_LVT + ) : + UTF8PROC_BOUNDCLASS_OTHER; + lbc = *last_boundclass; + boundary = + (tbc == UTF8PROC_BOUNDCLASS_EXTEND) ? false : + (lbc == UTF8PROC_BOUNDCLASS_START) ? true : + (lbc == UTF8PROC_BOUNDCLASS_CR && + tbc == UTF8PROC_BOUNDCLASS_LF) ? false : + (lbc == UTF8PROC_BOUNDCLASS_CONTROL) ? true : + (tbc == UTF8PROC_BOUNDCLASS_CONTROL) ? true : + (lbc == UTF8PROC_BOUNDCLASS_L && + (tbc == UTF8PROC_BOUNDCLASS_L || + tbc == UTF8PROC_BOUNDCLASS_V || + tbc == UTF8PROC_BOUNDCLASS_LV || + tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false : + ((lbc == UTF8PROC_BOUNDCLASS_LV || + lbc == UTF8PROC_BOUNDCLASS_V) && + (tbc == UTF8PROC_BOUNDCLASS_V || + tbc == UTF8PROC_BOUNDCLASS_T)) ? false : + ((lbc == UTF8PROC_BOUNDCLASS_LVT || + lbc == UTF8PROC_BOUNDCLASS_T) && + tbc == UTF8PROC_BOUNDCLASS_T) ? false : + true; + *last_boundclass = tbc; + if (boundary) { + if (bufsize >= 1) dst[0] = 0xFFFF; + if (bufsize >= 2) dst[1] = uc; + return 2; + } + } + if (bufsize >= 1) *dst = uc; + return 1; +} + +UTF8PROC_API +ssize_t utf8proc_decompose( + const uint8_t *str, ssize_t strlen, + int32_t *buffer, ssize_t bufsize, int options +) { + /* strlen will be ignored, if UTF8PROC_NULLTERM is set in options */ + ssize_t wpos = 0; + if ((options & UTF8PROC_COMPOSE) && (options & UTF8PROC_DECOMPOSE)) + return UTF8PROC_ERROR_INVALIDOPTS; + if ((options & UTF8PROC_STRIPMARK) && + !(options & UTF8PROC_COMPOSE) && !(options & UTF8PROC_DECOMPOSE)) + return UTF8PROC_ERROR_INVALIDOPTS; + { + int32_t uc; + ssize_t rpos = 0; + ssize_t decomp_result; + int boundclass = UTF8PROC_BOUNDCLASS_START; + while (1) { + if (options & UTF8PROC_NULLTERM) { + rpos += utf8proc_iterate(str + rpos, -1, &uc); + /* checking of return value is not neccessary, + as 'uc' is < 0 in case of error */ + if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; + if (rpos < 0) return UTF8PROC_ERROR_OVERFLOW; + if (uc == 0) break; + } else { + if (rpos >= strlen) break; + rpos += utf8proc_iterate(str + rpos, strlen - rpos, &uc); + if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; + } + decomp_result = utf8proc_decompose_char( + uc, buffer + wpos, (bufsize > wpos) ? (bufsize - wpos) : 0, options, + &boundclass + ); + if (decomp_result < 0) return decomp_result; + wpos += decomp_result; + /* prohibiting integer overflows due to too long strings: */ + if (wpos < 0 || wpos > SSIZE_MAX/sizeof(int32_t)/2) + return UTF8PROC_ERROR_OVERFLOW; + } + } + if ((options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) && bufsize >= wpos) { + ssize_t pos = 0; + while (pos < wpos-1) { + int32_t uc1, uc2; + const utf8proc_property_t *property1, *property2; + uc1 = buffer[pos]; + uc2 = buffer[pos+1]; + property1 = utf8proc_get_property(uc1); + property2 = utf8proc_get_property(uc2); + if (property1->combining_class > property2->combining_class && + property2->combining_class > 0) { + buffer[pos] = uc2; + buffer[pos+1] = uc1; + if (pos > 0) pos--; else pos++; + } else { + pos++; + } + } + } + return wpos; +} + +UTF8PROC_API +ssize_t utf8proc_reencode(int32_t *buffer, ssize_t length, int options) { + /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored + ASSERT: 'buffer' has one spare byte of free space at the end! */ + if (options & (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS | UTF8PROC_STRIPCC)) { + ssize_t rpos; + ssize_t wpos = 0; + int32_t uc; + for (rpos = 0; rpos < length; rpos++) { + uc = buffer[rpos]; + if (uc == 0x000D && rpos < length-1 && buffer[rpos+1] == 0x000A) rpos++; + if (uc == 0x000A || uc == 0x000D || uc == 0x0085 || + ((options & UTF8PROC_STRIPCC) && (uc == 0x000B || uc == 0x000C))) { + if (options & UTF8PROC_NLF2LS) { + if (options & UTF8PROC_NLF2PS) { + buffer[wpos++] = 0x000A; + } else { + buffer[wpos++] = 0x2028; + } + } else { + if (options & UTF8PROC_NLF2PS) { + buffer[wpos++] = 0x2029; + } else { + buffer[wpos++] = 0x0020; + } + } + } else if ((options & UTF8PROC_STRIPCC) && + (uc < 0x0020 || (uc >= 0x007F && uc < 0x00A0))) { + if (uc == 0x0009) buffer[wpos++] = 0x0020; + } else { + buffer[wpos++] = uc; + } + } + length = wpos; + } + if (options & UTF8PROC_COMPOSE) { + int32_t *starter = NULL; + int32_t current_char; + const utf8proc_property_t *starter_property = NULL, *current_property; + utf8proc_propval_t max_combining_class = -1; + ssize_t rpos; + ssize_t wpos = 0; + int32_t composition; + for (rpos = 0; rpos < length; rpos++) { + current_char = buffer[rpos]; + current_property = utf8proc_get_property(current_char); + if (starter && current_property->combining_class > max_combining_class) { + /* combination perhaps possible */ + int32_t hangul_lindex; + int32_t hangul_sindex; + hangul_lindex = *starter - UTF8PROC_HANGUL_LBASE; + if (hangul_lindex >= 0 && hangul_lindex < UTF8PROC_HANGUL_LCOUNT) { + int32_t hangul_vindex; + hangul_vindex = current_char - UTF8PROC_HANGUL_VBASE; + if (hangul_vindex >= 0 && hangul_vindex < UTF8PROC_HANGUL_VCOUNT) { + *starter = UTF8PROC_HANGUL_SBASE + + (hangul_lindex * UTF8PROC_HANGUL_VCOUNT + hangul_vindex) * + UTF8PROC_HANGUL_TCOUNT; + starter_property = NULL; + continue; + } + } + hangul_sindex = *starter - UTF8PROC_HANGUL_SBASE; + if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT && + (hangul_sindex % UTF8PROC_HANGUL_TCOUNT) == 0) { + int32_t hangul_tindex; + hangul_tindex = current_char - UTF8PROC_HANGUL_TBASE; + if (hangul_tindex >= 0 && hangul_tindex < UTF8PROC_HANGUL_TCOUNT) { + *starter += hangul_tindex; + starter_property = NULL; + continue; + } + } + if (!starter_property) { + starter_property = utf8proc_get_property(*starter); + } + if (starter_property->comb1st_index >= 0 && + current_property->comb2nd_index >= 0) { + composition = utf8proc_combinations[ + starter_property->comb1st_index + + current_property->comb2nd_index + ]; + if (composition >= 0 && (!(options & UTF8PROC_STABLE) || + !(utf8proc_get_property(composition)->comp_exclusion))) { + *starter = composition; + starter_property = NULL; + continue; + } + } + } + buffer[wpos] = current_char; + if (current_property->combining_class) { + if (current_property->combining_class > max_combining_class) { + max_combining_class = current_property->combining_class; + } + } else { + starter = buffer + wpos; + starter_property = NULL; + max_combining_class = -1; + } + wpos++; + } + length = wpos; + } + { + ssize_t rpos, wpos = 0; + int32_t uc; + for (rpos = 0; rpos < length; rpos++) { + uc = buffer[rpos]; + wpos += utf8proc_encode_char(uc, ((uint8_t *)buffer) + wpos); + } + ((uint8_t *)buffer)[wpos] = 0; + return wpos; + } +} + +UTF8PROC_API +ssize_t utf8proc_map( + const uint8_t *str, ssize_t strlen, uint8_t **dstptr, int options +) { + int32_t *buffer; + ssize_t result; + *dstptr = NULL; + result = utf8proc_decompose(str, strlen, NULL, 0, options); + if (result < 0) return result; + buffer = malloc(result * sizeof(int32_t) + 1); + if (!buffer) return UTF8PROC_ERROR_NOMEM; + result = utf8proc_decompose(str, strlen, buffer, result, options); + if (result < 0) { + free(buffer); + return result; + } + result = utf8proc_reencode(buffer, result, options); + if (result < 0) { + free(buffer); + return result; + } + { + int32_t *newptr; + newptr = realloc(buffer, (size_t)result+1); + if (newptr) buffer = newptr; + } + *dstptr = (uint8_t *)buffer; + return result; +} + +UTF8PROC_API +uint8_t *utf8proc_NFD(const uint8_t *str) { + uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_DECOMPOSE); + return retval; +} + +UTF8PROC_API +uint8_t *utf8proc_NFC(const uint8_t *str) { + uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_COMPOSE); + return retval; +} + +UTF8PROC_API +uint8_t *utf8proc_NFKD(const uint8_t *str) { + uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT); + return retval; +} + +UTF8PROC_API +uint8_t *utf8proc_NFKC(const uint8_t *str) { + uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_COMPOSE | UTF8PROC_COMPAT); + return retval; +} + diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.h b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.h new file mode 100644 index 000000000..25ca139b6 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc.h @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +/* + * File name: utf8proc.h + * + * Description: + * Header files for libutf8proc, which is a mapping tool for UTF-8 strings + * with following features: + * - decomposing and composing of strings + * - replacing compatibility characters with their equivalents + * - stripping of "default ignorable characters" + * like SOFT-HYPHEN or ZERO-WIDTH-SPACE + * - folding of certain characters for string comparison + * (e.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-") + * (see "LUMP" option) + * - optional rejection of strings containing non-assigned code points + * - stripping of control characters + * - stripping of character marks (accents, etc.) + * - transformation of LF, CRLF, CR and NEL to line-feed (LF) + * or to the unicode chararacters for paragraph separation (PS) + * or line separation (LS). + * - unicode case folding (for case insensitive string comparisons) + * - rejection of illegal UTF-8 data + * (i.e. UTF-8 encoded UTF-16 surrogates) + * - support for korean hangul characters + * Unicode Version 5.0.0 is supported. + */ + + +#ifndef UTF8PROC_H +#define UTF8PROC_H + +/** @name API version + * + * The utf8proc API version MAJOR.MINOR.PATCH, following + * semantic-versioning rules (http://semver.org) based on API + * compatibility. + * + * This is also returned at runtime by @ref utf8proc_version; however, the + * runtime version may append a string like "-dev" to the version number + * for prerelease versions. + * + * @note The shared-library version number in the Makefile may be different, + * being based on ABI compatibility rather than API compatibility. + */ +/** @{ */ +/** The MAJOR version number (increased when backwards API compatibility is broken). */ +#define UTF8PROC_VERSION_MAJOR 1 +/** The MINOR version number (increased when new functionality is added in a backwards-compatible manner). */ +#define UTF8PROC_VERSION_MINOR 1 +/** The PATCH version (increased for fixes that do not change the API). */ +#define UTF8PROC_VERSION_PATCH 5 +/** @} */ + +/* + * Define UTF8PROC_INLINE and include utf8proc.c to embed a static + * version of utf8proc in your program or library without exporting + * any of its symbols. + */ +#ifdef UTF8PROC_INLINE +#define UTF8PROC_API static +#undef UTF8PROC_DATA_EXPORT +#define UTF8PROC_DATA static +#else +#define UTF8PROC_API +#define UTF8PROC_DATA_EXPORT +#define UTF8PROC_DATA +#endif + + +#include +#include +#ifdef _MSC_VER +# if _MSC_VER >= 1900 +# include +# include +# else + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned char bool; + enum {false, true}; +# endif +# ifdef _WIN64 +# define ssize_t __int64 +# else +# define ssize_t int +# endif +#elif defined(HAVE_STDBOOL_H) && defined(HAVE_INTTYPES_H) +#include +#include +#else +#include +typedef uint8_t bool; +enum {false, true}; +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SSIZE_MAX +#define SSIZE_MAX ((size_t)SIZE_MAX/2) +#endif + +#define UTF8PROC_NULLTERM (1<<0) +#define UTF8PROC_STABLE (1<<1) +#define UTF8PROC_COMPAT (1<<2) +#define UTF8PROC_COMPOSE (1<<3) +#define UTF8PROC_DECOMPOSE (1<<4) +#define UTF8PROC_IGNORE (1<<5) +#define UTF8PROC_REJECTNA (1<<6) +#define UTF8PROC_NLF2LS (1<<7) +#define UTF8PROC_NLF2PS (1<<8) +#define UTF8PROC_NLF2LF (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS) +#define UTF8PROC_STRIPCC (1<<9) +#define UTF8PROC_CASEFOLD (1<<10) +#define UTF8PROC_CHARBOUND (1<<11) +#define UTF8PROC_LUMP (1<<12) +#define UTF8PROC_STRIPMARK (1<<13) +/* + * Flags being regarded by several functions in the library: + * NULLTERM: The given UTF-8 input is NULL terminated. + * STABLE: Unicode Versioning Stability has to be respected. + * COMPAT: Compatiblity decomposition + * (i.e. formatting information is lost) + * COMPOSE: Return a result with composed characters. + * DECOMPOSE: Return a result with decomposed characters. + * IGNORE: Strip "default ignorable characters" + * REJECTNA: Return an error, if the input contains unassigned + * code points. + * NLF2LS: Indicating that NLF-sequences (LF, CRLF, CR, NEL) are + * representing a line break, and should be converted to the + * unicode character for line separation (LS). + * NLF2PS: Indicating that NLF-sequences are representing a paragraph + * break, and should be converted to the unicode character for + * paragraph separation (PS). + * NLF2LF: Indicating that the meaning of NLF-sequences is unknown. + * STRIPCC: Strips and/or convers control characters. + * NLF-sequences are transformed into space, except if one of + * the NLF2LS/PS/LF options is given. + * HorizontalTab (HT) and FormFeed (FF) are treated as a + * NLF-sequence in this case. + * All other control characters are simply removed. + * CASEFOLD: Performs unicode case folding, to be able to do a + * case-insensitive string comparison. + * CHARBOUND: Inserts 0xFF bytes at the beginning of each sequence which + * is representing a single grapheme cluster (see UAX#29). + * LUMP: Lumps certain characters together + * (e.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-"). + * (See lump.txt for details.) + * If NLF2LF is set, this includes a transformation of + * paragraph and line separators to ASCII line-feed (LF). + * STRIPMARK: Strips all character markings + * (non-spacing, spacing and enclosing) (i.e. accents) + * NOTE: this option works only with COMPOSE or DECOMPOSE + */ + +#define UTF8PROC_ERROR_NOMEM -1 +#define UTF8PROC_ERROR_OVERFLOW -2 +#define UTF8PROC_ERROR_INVALIDUTF8 -3 +#define UTF8PROC_ERROR_NOTASSIGNED -4 +#define UTF8PROC_ERROR_INVALIDOPTS -5 +/* + * Error codes being returned by almost all functions: + * ERROR_NOMEM: Memory could not be allocated. + * ERROR_OVERFLOW: The given string is too long to be processed. + * ERROR_INVALIDUTF8: The given string is not a legal UTF-8 string. + * ERROR_NOTASSIGNED: The REJECTNA flag was set, + * and an unassigned code point was found. + * ERROR_INVALIDOPTS: Invalid options have been used. + */ + +typedef int16_t utf8proc_propval_t; +typedef struct utf8proc_property_struct { + utf8proc_propval_t category; + utf8proc_propval_t combining_class; + utf8proc_propval_t bidi_class; + utf8proc_propval_t decomp_type; + const int32_t *decomp_mapping; + unsigned bidi_mirrored:1; + int32_t uppercase_mapping; + int32_t lowercase_mapping; + int32_t titlecase_mapping; + int32_t comb1st_index; + int32_t comb2nd_index; + unsigned comp_exclusion:1; + unsigned ignorable:1; + unsigned control_boundary:1; + unsigned extend:1; + const int32_t *casefold_mapping; +} utf8proc_property_t; + +#define UTF8PROC_CATEGORY_LU 1 +#define UTF8PROC_CATEGORY_LL 2 +#define UTF8PROC_CATEGORY_LT 3 +#define UTF8PROC_CATEGORY_LM 4 +#define UTF8PROC_CATEGORY_LO 5 +#define UTF8PROC_CATEGORY_MN 6 +#define UTF8PROC_CATEGORY_MC 7 +#define UTF8PROC_CATEGORY_ME 8 +#define UTF8PROC_CATEGORY_ND 9 +#define UTF8PROC_CATEGORY_NL 10 +#define UTF8PROC_CATEGORY_NO 11 +#define UTF8PROC_CATEGORY_PC 12 +#define UTF8PROC_CATEGORY_PD 13 +#define UTF8PROC_CATEGORY_PS 14 +#define UTF8PROC_CATEGORY_PE 15 +#define UTF8PROC_CATEGORY_PI 16 +#define UTF8PROC_CATEGORY_PF 17 +#define UTF8PROC_CATEGORY_PO 18 +#define UTF8PROC_CATEGORY_SM 19 +#define UTF8PROC_CATEGORY_SC 20 +#define UTF8PROC_CATEGORY_SK 21 +#define UTF8PROC_CATEGORY_SO 22 +#define UTF8PROC_CATEGORY_ZS 23 +#define UTF8PROC_CATEGORY_ZL 24 +#define UTF8PROC_CATEGORY_ZP 25 +#define UTF8PROC_CATEGORY_CC 26 +#define UTF8PROC_CATEGORY_CF 27 +#define UTF8PROC_CATEGORY_CS 28 +#define UTF8PROC_CATEGORY_CO 29 +#define UTF8PROC_CATEGORY_CN 30 +#define UTF8PROC_BIDI_CLASS_L 1 +#define UTF8PROC_BIDI_CLASS_LRE 2 +#define UTF8PROC_BIDI_CLASS_LRO 3 +#define UTF8PROC_BIDI_CLASS_R 4 +#define UTF8PROC_BIDI_CLASS_AL 5 +#define UTF8PROC_BIDI_CLASS_RLE 6 +#define UTF8PROC_BIDI_CLASS_RLO 7 +#define UTF8PROC_BIDI_CLASS_PDF 8 +#define UTF8PROC_BIDI_CLASS_EN 9 +#define UTF8PROC_BIDI_CLASS_ES 10 +#define UTF8PROC_BIDI_CLASS_ET 11 +#define UTF8PROC_BIDI_CLASS_AN 12 +#define UTF8PROC_BIDI_CLASS_CS 13 +#define UTF8PROC_BIDI_CLASS_NSM 14 +#define UTF8PROC_BIDI_CLASS_BN 15 +#define UTF8PROC_BIDI_CLASS_B 16 +#define UTF8PROC_BIDI_CLASS_S 17 +#define UTF8PROC_BIDI_CLASS_WS 18 +#define UTF8PROC_BIDI_CLASS_ON 19 +#define UTF8PROC_DECOMP_TYPE_FONT 1 +#define UTF8PROC_DECOMP_TYPE_NOBREAK 2 +#define UTF8PROC_DECOMP_TYPE_INITIAL 3 +#define UTF8PROC_DECOMP_TYPE_MEDIAL 4 +#define UTF8PROC_DECOMP_TYPE_FINAL 5 +#define UTF8PROC_DECOMP_TYPE_ISOLATED 6 +#define UTF8PROC_DECOMP_TYPE_CIRCLE 7 +#define UTF8PROC_DECOMP_TYPE_SUPER 8 +#define UTF8PROC_DECOMP_TYPE_SUB 9 +#define UTF8PROC_DECOMP_TYPE_VERTICAL 10 +#define UTF8PROC_DECOMP_TYPE_WIDE 11 +#define UTF8PROC_DECOMP_TYPE_NARROW 12 +#define UTF8PROC_DECOMP_TYPE_SMALL 13 +#define UTF8PROC_DECOMP_TYPE_SQUARE 14 +#define UTF8PROC_DECOMP_TYPE_FRACTION 15 +#define UTF8PROC_DECOMP_TYPE_COMPAT 16 + +#ifdef UTF8PROC_DATA_EXPORT +extern const int8_t utf8proc_utf8class[256]; +#endif + +UTF8PROC_API +const char *utf8proc_version(void); + +UTF8PROC_API +const char *utf8proc_errmsg(ssize_t errcode); +/* + * Returns a static error string for the given error code. + */ + +UTF8PROC_API +ssize_t utf8proc_iterate(const uint8_t *str, ssize_t strlen, int32_t *dst); +/* + * Reads a single char from the UTF-8 sequence being pointed to by 'str'. + * The maximum number of bytes read is 'strlen', unless 'strlen' is + * negative. + * If a valid unicode char could be read, it is stored in the variable + * being pointed to by 'dst', otherwise that variable will be set to -1. + * In case of success the number of bytes read is returned, otherwise a + * negative error code is returned. + */ + +UTF8PROC_API +bool utf8proc_codepoint_valid(int32_t uc); +/* + * Returns 1, if the given unicode code-point is valid, otherwise 0. + */ + +UTF8PROC_API +ssize_t utf8proc_encode_char(int32_t uc, uint8_t *dst); +/* + * Encodes the unicode char with the code point 'uc' as an UTF-8 string in + * the byte array being pointed to by 'dst'. This array has to be at least + * 4 bytes long. + * In case of success the number of bytes written is returned, + * otherwise 0. + * This function does not check if 'uc' is a valid unicode code point. + */ + +UTF8PROC_API +const utf8proc_property_t *utf8proc_get_property(int32_t uc); +/* + * Returns a pointer to a (constant) struct containing information about + * the unicode char with the given code point 'uc'. + * If the character is not existent a pointer to a special struct is + * returned, where 'category' is a NULL pointer. + * WARNING: The parameter 'uc' has to be in the range of 0x0000 to + * 0x10FFFF, otherwise the program might crash! + */ + +UTF8PROC_API +ssize_t utf8proc_decompose_char( + int32_t uc, int32_t *dst, ssize_t bufsize, + int options, int *last_boundclass +); +/* + * Writes a decomposition of the unicode char 'uc' into the array being + * pointed to by 'dst'. + * Following flags in the 'options' field are regarded: + * REJECTNA: an unassigned unicode code point leads to an error + * IGNORE: "default ignorable" chars are stripped + * CASEFOLD: unicode casefolding is applied + * COMPAT: replace certain characters with their + * compatibility decomposition + * CHARBOUND: Inserts 0xFF bytes before each grapheme cluster + * LUMP: lumps certain different characters together + * STRIPMARK: removes all character marks + * The pointer 'last_boundclass' has to point to an integer variable which + * is storing the last character boundary class, if the CHARBOUND option + * is used. + * In case of success the number of chars written is returned, + * in case of an error, a negative error code is returned. + * If the number of written chars would be bigger than 'bufsize', + * the buffer (up to 'bufsize') has inpredictable data, and the needed + * buffer size is returned. + * WARNING: The parameter 'uc' has to be in the range of 0x0000 to + * 0x10FFFF, otherwise the program might crash! + */ + +UTF8PROC_API +ssize_t utf8proc_decompose( + const uint8_t *str, ssize_t strlen, + int32_t *buffer, ssize_t bufsize, int options +); +/* + * Does the same as 'utf8proc_decompose_char', but acts on a whole UTF-8 + * string, and orders the decomposed sequences correctly. + * If the NULLTERM flag in 'options' is set, processing will be stopped, + * when a NULL byte is encounted, otherwise 'strlen' bytes are processed. + * The result in form of unicode code points is written into the buffer + * being pointed to by 'buffer', having the length of 'bufsize' entries. + * In case of success the number of chars written is returned, + * in case of an error, a negative error code is returned. + * If the number of written chars would be bigger than 'bufsize', + * the buffer (up to 'bufsize') has inpredictable data, and the needed + * buffer size is returned. + */ + +UTF8PROC_API +ssize_t utf8proc_reencode(int32_t *buffer, ssize_t length, int options); +/* + * Reencodes the sequence of unicode characters given by the pointer + * 'buffer' and 'length' as UTF-8. + * The result is stored in the same memory area where the data is read. + * Following flags in the 'options' field are regarded: + * NLF2LS: converts LF, CRLF, CR and NEL into LS + * NLF2PS: converts LF, CRLF, CR and NEL into PS + * NLF2LF: converts LF, CRLF, CR and NEL into LF + * STRIPCC: strips or converts all non-affected control characters + * COMPOSE: tries to combine decomposed characters into composite + * characters + * STABLE: prohibits combining characters which would violate + * the unicode versioning stability + * In case of success the length of the resulting UTF-8 string is + * returned, otherwise a negative error code is returned. + * WARNING: The amount of free space being pointed to by 'buffer', has to + * exceed the amount of the input data by one byte, and the + * entries of the array pointed to by 'str' have to be in the + * range of 0x0000 to 0x10FFFF, otherwise the program might + * crash! + */ + +UTF8PROC_API +ssize_t utf8proc_map( + const uint8_t *str, ssize_t strlen, uint8_t **dstptr, int options +); +/* + * Maps the given UTF-8 string being pointed to by 'str' to a new UTF-8 + * string, which is allocated dynamically, and afterwards pointed to by + * the pointer being pointed to by 'dstptr'. + * If the NULLTERM flag in the 'options' field is set, the length is + * determined by a NULL terminator, otherwise the parameter 'strlen' is + * evaluated to determine the string length, but in any case the result + * will be NULL terminated (though it might contain NULL characters + * before). Other flags in the 'options' field are passed to the functions + * defined above, and regarded as described. + * In case of success the length of the new string is returned, + * otherwise a negative error code is returned. + * NOTICE: The memory of the new UTF-8 string will have been allocated with + * 'malloc', and has theirfore to be freed with 'free'. + */ + +UTF8PROC_API +uint8_t *utf8proc_NFD(const uint8_t *str); +UTF8PROC_API +uint8_t *utf8proc_NFC(const uint8_t *str); +UTF8PROC_API +uint8_t *utf8proc_NFKD(const uint8_t *str); +UTF8PROC_API +uint8_t *utf8proc_NFKC(const uint8_t *str); +/* + * Returns a pointer to newly allocated memory of a NFD, NFC, NFKD or NFKC + * normalized version of the null-terminated string 'str'. + */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc_data.c b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc_data.c new file mode 100644 index 000000000..55ebb5327 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/utf8proc/utf8proc_data.c @@ -0,0 +1,13388 @@ +/* + * This file contains derived data from a modified version of the + * Unicode data files. + * + * The original data files are available at + * http://www.unicode.org/Public/UNIDATA/ + * + * + * COPYRIGHT AND PERMISSION NOTICE + * + * Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed + * under the Terms of Use in http://www.unicode.org/copyright.html. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of the Unicode data files and any associated documentation (the "Data + * Files") or Unicode software and any associated documentation (the + * "Software") to deal in the Data Files or Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, and/or sell copies of the Data Files or Software, and + * to permit persons to whom the Data Files or Software are furnished to do + * so, provided that (a) the above copyright notice(s) and this permission + * notice appear with all copies of the Data Files or Software, (b) both the + * above copyright notice(s) and this permission notice appear in associated + * documentation, and (c) there is clear notice in each modified Data File or + * in the Software as well as in the documentation associated with the Data + * File(s) or Software that the data or software has been modified. + * + * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR + * CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THE DATA FILES OR SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in these Data Files or Software without prior written + * authorization of the copyright holder. + */ + + +UTF8PROC_DATA +const int32_t utf8proc_sequences[] = { + 97, -1, 98, -1, 99, -1, 100, + -1, 101, -1, 102, -1, 103, -1, 104, + -1, 105, -1, 106, -1, 107, -1, 108, + -1, 109, -1, 110, -1, 111, -1, 112, + -1, 113, -1, 114, -1, 115, -1, 116, + -1, 117, -1, 118, -1, 119, -1, 120, + -1, 121, -1, 122, -1, 32, -1, 32, + 776, -1, 32, 772, -1, 50, -1, 51, + -1, 32, 769, -1, 956, -1, 32, 807, + -1, 49, -1, 49, 8260, 52, -1, 49, + 8260, 50, -1, 51, 8260, 52, -1, 65, + 768, -1, 224, -1, 65, 769, -1, 225, + -1, 65, 770, -1, 226, -1, 65, 771, + -1, 227, -1, 65, 776, -1, 228, -1, + 65, 778, -1, 229, -1, 230, -1, 67, + 807, -1, 231, -1, 69, 768, -1, 232, + -1, 69, 769, -1, 233, -1, 69, 770, + -1, 234, -1, 69, 776, -1, 235, -1, + 73, 768, -1, 236, -1, 73, 769, -1, + 237, -1, 73, 770, -1, 238, -1, 73, + 776, -1, 239, -1, 240, -1, 78, 771, + -1, 241, -1, 79, 768, -1, 242, -1, + 79, 769, -1, 243, -1, 79, 770, -1, + 244, -1, 79, 771, -1, 245, -1, 79, + 776, -1, 246, -1, 248, -1, 85, 768, + -1, 249, -1, 85, 769, -1, 250, -1, + 85, 770, -1, 251, -1, 85, 776, -1, + 252, -1, 89, 769, -1, 253, -1, 254, + -1, 115, 115, -1, 97, 768, -1, 97, + 769, -1, 97, 770, -1, 97, 771, -1, + 97, 776, -1, 97, 778, -1, 99, 807, + -1, 101, 768, -1, 101, 769, -1, 101, + 770, -1, 101, 776, -1, 105, 768, -1, + 105, 769, -1, 105, 770, -1, 105, 776, + -1, 110, 771, -1, 111, 768, -1, 111, + 769, -1, 111, 770, -1, 111, 771, -1, + 111, 776, -1, 117, 768, -1, 117, 769, + -1, 117, 770, -1, 117, 776, -1, 121, + 769, -1, 121, 776, -1, 65, 772, -1, + 257, -1, 97, 772, -1, 65, 774, -1, + 259, -1, 97, 774, -1, 65, 808, -1, + 261, -1, 97, 808, -1, 67, 769, -1, + 263, -1, 99, 769, -1, 67, 770, -1, + 265, -1, 99, 770, -1, 67, 775, -1, + 267, -1, 99, 775, -1, 67, 780, -1, + 269, -1, 99, 780, -1, 68, 780, -1, + 271, -1, 100, 780, -1, 273, -1, 69, + 772, -1, 275, -1, 101, 772, -1, 69, + 774, -1, 277, -1, 101, 774, -1, 69, + 775, -1, 279, -1, 101, 775, -1, 69, + 808, -1, 281, -1, 101, 808, -1, 69, + 780, -1, 283, -1, 101, 780, -1, 71, + 770, -1, 285, -1, 103, 770, -1, 71, + 774, -1, 287, -1, 103, 774, -1, 71, + 775, -1, 289, -1, 103, 775, -1, 71, + 807, -1, 291, -1, 103, 807, -1, 72, + 770, -1, 293, -1, 104, 770, -1, 295, + -1, 73, 771, -1, 297, -1, 105, 771, + -1, 73, 772, -1, 299, -1, 105, 772, + -1, 73, 774, -1, 301, -1, 105, 774, + -1, 73, 808, -1, 303, -1, 105, 808, + -1, 73, 775, -1, 105, 775, -1, 73, + 74, -1, 307, -1, 105, 106, -1, 74, + 770, -1, 309, -1, 106, 770, -1, 75, + 807, -1, 311, -1, 107, 807, -1, 76, + 769, -1, 314, -1, 108, 769, -1, 76, + 807, -1, 316, -1, 108, 807, -1, 76, + 780, -1, 318, -1, 108, 780, -1, 76, + 183, -1, 320, -1, 108, 183, -1, 322, + -1, 78, 769, -1, 324, -1, 110, 769, + -1, 78, 807, -1, 326, -1, 110, 807, + -1, 78, 780, -1, 328, -1, 110, 780, + -1, 700, 110, -1, 331, -1, 79, 772, + -1, 333, -1, 111, 772, -1, 79, 774, + -1, 335, -1, 111, 774, -1, 79, 779, + -1, 337, -1, 111, 779, -1, 339, -1, + 82, 769, -1, 341, -1, 114, 769, -1, + 82, 807, -1, 343, -1, 114, 807, -1, + 82, 780, -1, 345, -1, 114, 780, -1, + 83, 769, -1, 347, -1, 115, 769, -1, + 83, 770, -1, 349, -1, 115, 770, -1, + 83, 807, -1, 351, -1, 115, 807, -1, + 83, 780, -1, 353, -1, 115, 780, -1, + 84, 807, -1, 355, -1, 116, 807, -1, + 84, 780, -1, 357, -1, 116, 780, -1, + 359, -1, 85, 771, -1, 361, -1, 117, + 771, -1, 85, 772, -1, 363, -1, 117, + 772, -1, 85, 774, -1, 365, -1, 117, + 774, -1, 85, 778, -1, 367, -1, 117, + 778, -1, 85, 779, -1, 369, -1, 117, + 779, -1, 85, 808, -1, 371, -1, 117, + 808, -1, 87, 770, -1, 373, -1, 119, + 770, -1, 89, 770, -1, 375, -1, 121, + 770, -1, 89, 776, -1, 255, -1, 90, + 769, -1, 378, -1, 122, 769, -1, 90, + 775, -1, 380, -1, 122, 775, -1, 90, + 780, -1, 382, -1, 122, 780, -1, 595, + -1, 387, -1, 389, -1, 596, -1, 392, + -1, 598, -1, 599, -1, 396, -1, 477, + -1, 601, -1, 603, -1, 402, -1, 608, + -1, 611, -1, 617, -1, 616, -1, 409, + -1, 623, -1, 626, -1, 629, -1, 79, + 795, -1, 417, -1, 111, 795, -1, 419, + -1, 421, -1, 640, -1, 424, -1, 643, + -1, 429, -1, 648, -1, 85, 795, -1, + 432, -1, 117, 795, -1, 650, -1, 651, + -1, 436, -1, 438, -1, 658, -1, 441, + -1, 445, -1, 68, 381, -1, 454, -1, + 68, 382, -1, 100, 382, -1, 76, 74, + -1, 457, -1, 76, 106, -1, 108, 106, + -1, 78, 74, -1, 460, -1, 78, 106, + -1, 110, 106, -1, 65, 780, -1, 462, + -1, 97, 780, -1, 73, 780, -1, 464, + -1, 105, 780, -1, 79, 780, -1, 466, + -1, 111, 780, -1, 85, 780, -1, 468, + -1, 117, 780, -1, 220, 772, -1, 470, + -1, 252, 772, -1, 220, 769, -1, 472, + -1, 252, 769, -1, 220, 780, -1, 474, + -1, 252, 780, -1, 220, 768, -1, 476, + -1, 252, 768, -1, 196, 772, -1, 479, + -1, 228, 772, -1, 550, 772, -1, 481, + -1, 551, 772, -1, 198, 772, -1, 483, + -1, 230, 772, -1, 485, -1, 71, 780, + -1, 487, -1, 103, 780, -1, 75, 780, + -1, 489, -1, 107, 780, -1, 79, 808, + -1, 491, -1, 111, 808, -1, 490, 772, + -1, 493, -1, 491, 772, -1, 439, 780, + -1, 495, -1, 658, 780, -1, 106, 780, + -1, 68, 90, -1, 499, -1, 68, 122, + -1, 100, 122, -1, 71, 769, -1, 501, + -1, 103, 769, -1, 405, -1, 447, -1, + 78, 768, -1, 505, -1, 110, 768, -1, + 197, 769, -1, 507, -1, 229, 769, -1, + 198, 769, -1, 509, -1, 230, 769, -1, + 216, 769, -1, 511, -1, 248, 769, -1, + 65, 783, -1, 513, -1, 97, 783, -1, + 65, 785, -1, 515, -1, 97, 785, -1, + 69, 783, -1, 517, -1, 101, 783, -1, + 69, 785, -1, 519, -1, 101, 785, -1, + 73, 783, -1, 521, -1, 105, 783, -1, + 73, 785, -1, 523, -1, 105, 785, -1, + 79, 783, -1, 525, -1, 111, 783, -1, + 79, 785, -1, 527, -1, 111, 785, -1, + 82, 783, -1, 529, -1, 114, 783, -1, + 82, 785, -1, 531, -1, 114, 785, -1, + 85, 783, -1, 533, -1, 117, 783, -1, + 85, 785, -1, 535, -1, 117, 785, -1, + 83, 806, -1, 537, -1, 115, 806, -1, + 84, 806, -1, 539, -1, 116, 806, -1, + 541, -1, 72, 780, -1, 543, -1, 104, + 780, -1, 414, -1, 547, -1, 549, -1, + 65, 775, -1, 551, -1, 97, 775, -1, + 69, 807, -1, 553, -1, 101, 807, -1, + 214, 772, -1, 555, -1, 246, 772, -1, + 213, 772, -1, 557, -1, 245, 772, -1, + 79, 775, -1, 559, -1, 111, 775, -1, + 558, 772, -1, 561, -1, 559, 772, -1, + 89, 772, -1, 563, -1, 121, 772, -1, + 11365, -1, 572, -1, 410, -1, 11366, -1, + 578, -1, 384, -1, 649, -1, 652, -1, + 583, -1, 585, -1, 587, -1, 589, -1, + 591, -1, 614, -1, 633, -1, 635, -1, + 641, -1, 32, 774, -1, 32, 775, -1, + 32, 778, -1, 32, 808, -1, 32, 771, + -1, 32, 779, -1, 661, -1, 768, -1, + 769, -1, 787, -1, 776, 769, -1, 953, + -1, 697, -1, 32, 837, -1, 59, -1, + 168, 769, -1, 913, 769, -1, 940, -1, + 183, -1, 917, 769, -1, 941, -1, 919, + 769, -1, 942, -1, 921, 769, -1, 943, + -1, 927, 769, -1, 972, -1, 933, 769, + -1, 973, -1, 937, 769, -1, 974, -1, + 970, 769, -1, 953, 776, 769, -1, 945, + -1, 946, -1, 947, -1, 948, -1, 949, + -1, 950, -1, 951, -1, 952, -1, 954, + -1, 955, -1, 957, -1, 958, -1, 959, + -1, 960, -1, 961, -1, 963, -1, 964, + -1, 965, -1, 966, -1, 967, -1, 968, + -1, 969, -1, 921, 776, -1, 970, -1, + 933, 776, -1, 971, -1, 945, 769, -1, + 949, 769, -1, 951, 769, -1, 953, 769, + -1, 971, 769, -1, 965, 776, 769, -1, + 953, 776, -1, 965, 776, -1, 959, 769, + -1, 965, 769, -1, 969, 769, -1, 933, + -1, 978, 769, -1, 978, 776, -1, 985, + -1, 987, -1, 989, -1, 991, -1, 993, + -1, 995, -1, 997, -1, 999, -1, 1001, + -1, 1003, -1, 1005, -1, 1007, -1, 962, + -1, 920, -1, 1016, -1, 931, -1, 1010, + -1, 1019, -1, 891, -1, 892, -1, 893, + -1, 1045, 768, -1, 1104, -1, 1045, 776, + -1, 1105, -1, 1106, -1, 1043, 769, -1, + 1107, -1, 1108, -1, 1109, -1, 1110, -1, + 1030, 776, -1, 1111, -1, 1112, -1, 1113, + -1, 1114, -1, 1115, -1, 1050, 769, -1, + 1116, -1, 1048, 768, -1, 1117, -1, 1059, + 774, -1, 1118, -1, 1119, -1, 1072, -1, + 1073, -1, 1074, -1, 1075, -1, 1076, -1, + 1077, -1, 1078, -1, 1079, -1, 1080, -1, + 1048, 774, -1, 1081, -1, 1082, -1, 1083, + -1, 1084, -1, 1085, -1, 1086, -1, 1087, + -1, 1088, -1, 1089, -1, 1090, -1, 1091, + -1, 1092, -1, 1093, -1, 1094, -1, 1095, + -1, 1096, -1, 1097, -1, 1098, -1, 1099, + -1, 1100, -1, 1101, -1, 1102, -1, 1103, + -1, 1080, 774, -1, 1077, 768, -1, 1077, + 776, -1, 1075, 769, -1, 1110, 776, -1, + 1082, 769, -1, 1080, 768, -1, 1091, 774, + -1, 1121, -1, 1123, -1, 1125, -1, 1127, + -1, 1129, -1, 1131, -1, 1133, -1, 1135, + -1, 1137, -1, 1139, -1, 1141, -1, 1140, + 783, -1, 1143, -1, 1141, 783, -1, 1145, + -1, 1147, -1, 1149, -1, 1151, -1, 1153, + -1, 1163, -1, 1165, -1, 1167, -1, 1169, + -1, 1171, -1, 1173, -1, 1175, -1, 1177, + -1, 1179, -1, 1181, -1, 1183, -1, 1185, + -1, 1187, -1, 1189, -1, 1191, -1, 1193, + -1, 1195, -1, 1197, -1, 1199, -1, 1201, + -1, 1203, -1, 1205, -1, 1207, -1, 1209, + -1, 1211, -1, 1213, -1, 1215, -1, 1231, + -1, 1046, 774, -1, 1218, -1, 1078, 774, + -1, 1220, -1, 1222, -1, 1224, -1, 1226, + -1, 1228, -1, 1230, -1, 1040, 774, -1, + 1233, -1, 1072, 774, -1, 1040, 776, -1, + 1235, -1, 1072, 776, -1, 1237, -1, 1045, + 774, -1, 1239, -1, 1077, 774, -1, 1241, + -1, 1240, 776, -1, 1243, -1, 1241, 776, + -1, 1046, 776, -1, 1245, -1, 1078, 776, + -1, 1047, 776, -1, 1247, -1, 1079, 776, + -1, 1249, -1, 1048, 772, -1, 1251, -1, + 1080, 772, -1, 1048, 776, -1, 1253, -1, + 1080, 776, -1, 1054, 776, -1, 1255, -1, + 1086, 776, -1, 1257, -1, 1256, 776, -1, + 1259, -1, 1257, 776, -1, 1069, 776, -1, + 1261, -1, 1101, 776, -1, 1059, 772, -1, + 1263, -1, 1091, 772, -1, 1059, 776, -1, + 1265, -1, 1091, 776, -1, 1059, 779, -1, + 1267, -1, 1091, 779, -1, 1063, 776, -1, + 1269, -1, 1095, 776, -1, 1271, -1, 1067, + 776, -1, 1273, -1, 1099, 776, -1, 1275, + -1, 1277, -1, 1279, -1, 1281, -1, 1283, + -1, 1285, -1, 1287, -1, 1289, -1, 1291, + -1, 1293, -1, 1295, -1, 1297, -1, 1299, + -1, 1377, -1, 1378, -1, 1379, -1, 1380, + -1, 1381, -1, 1382, -1, 1383, -1, 1384, + -1, 1385, -1, 1386, -1, 1387, -1, 1388, + -1, 1389, -1, 1390, -1, 1391, -1, 1392, + -1, 1393, -1, 1394, -1, 1395, -1, 1396, + -1, 1397, -1, 1398, -1, 1399, -1, 1400, + -1, 1401, -1, 1402, -1, 1403, -1, 1404, + -1, 1405, -1, 1406, -1, 1407, -1, 1408, + -1, 1409, -1, 1410, -1, 1411, -1, 1412, + -1, 1413, -1, 1414, -1, 1381, 1410, -1, + 1575, 1619, -1, 1575, 1620, -1, 1608, 1620, + -1, 1575, 1621, -1, 1610, 1620, -1, 1575, + 1652, -1, 1608, 1652, -1, 1735, 1652, -1, + 1610, 1652, -1, 1749, 1620, -1, 1729, 1620, + -1, 1746, 1620, -1, 2344, 2364, -1, 2352, + 2364, -1, 2355, 2364, -1, 2325, 2364, -1, + 2326, 2364, -1, 2327, 2364, -1, 2332, 2364, + -1, 2337, 2364, -1, 2338, 2364, -1, 2347, + 2364, -1, 2351, 2364, -1, 2503, 2494, -1, + 2503, 2519, -1, 2465, 2492, -1, 2466, 2492, + -1, 2479, 2492, -1, 2610, 2620, -1, 2616, + 2620, -1, 2582, 2620, -1, 2583, 2620, -1, + 2588, 2620, -1, 2603, 2620, -1, 2887, 2902, + -1, 2887, 2878, -1, 2887, 2903, -1, 2849, + 2876, -1, 2850, 2876, -1, 2962, 3031, -1, + 3014, 3006, -1, 3015, 3006, -1, 3014, 3031, + -1, 3142, 3158, -1, 3263, 3285, -1, 3270, + 3285, -1, 3270, 3286, -1, 3270, 3266, -1, + 3274, 3285, -1, 3398, 3390, -1, 3399, 3390, + -1, 3398, 3415, -1, 3545, 3530, -1, 3545, + 3535, -1, 3548, 3530, -1, 3545, 3551, -1, + 3661, 3634, -1, 3789, 3762, -1, 3755, 3737, + -1, 3755, 3745, -1, 3851, -1, 3906, 4023, + -1, 3916, 4023, -1, 3921, 4023, -1, 3926, + 4023, -1, 3931, 4023, -1, 3904, 4021, -1, + 3953, 3954, -1, 3953, 3956, -1, 4018, 3968, + -1, 4018, 3969, -1, 4019, 3968, -1, 4019, + 3969, -1, 3953, 3968, -1, 3986, 4023, -1, + 3996, 4023, -1, 4001, 4023, -1, 4006, 4023, + -1, 4011, 4023, -1, 3984, 4021, -1, 4133, + 4142, -1, 11520, -1, 11521, -1, 11522, -1, + 11523, -1, 11524, -1, 11525, -1, 11526, -1, + 11527, -1, 11528, -1, 11529, -1, 11530, -1, + 11531, -1, 11532, -1, 11533, -1, 11534, -1, + 11535, -1, 11536, -1, 11537, -1, 11538, -1, + 11539, -1, 11540, -1, 11541, -1, 11542, -1, + 11543, -1, 11544, -1, 11545, -1, 11546, -1, + 11547, -1, 11548, -1, 11549, -1, 11550, -1, + 11551, -1, 11552, -1, 11553, -1, 11554, -1, + 11555, -1, 11556, -1, 11557, -1, 4316, -1, + 6917, 6965, -1, 6919, 6965, -1, 6921, 6965, + -1, 6923, 6965, -1, 6925, 6965, -1, 6929, + 6965, -1, 6970, 6965, -1, 6972, 6965, -1, + 6974, 6965, -1, 6975, 6965, -1, 6978, 6965, + -1, 65, -1, 198, -1, 66, -1, 68, + -1, 69, -1, 398, -1, 71, -1, 72, + -1, 73, -1, 74, -1, 75, -1, 76, + -1, 77, -1, 78, -1, 79, -1, 546, + -1, 80, -1, 82, -1, 84, -1, 85, + -1, 87, -1, 592, -1, 593, -1, 7426, + -1, 604, -1, 7446, -1, 7447, -1, 7453, + -1, 7461, -1, 594, -1, 597, -1, 607, + -1, 609, -1, 613, -1, 618, -1, 7547, + -1, 669, -1, 621, -1, 7557, -1, 671, + -1, 625, -1, 624, -1, 627, -1, 628, + -1, 632, -1, 642, -1, 427, -1, 7452, + -1, 656, -1, 657, -1, 65, 805, -1, + 7681, -1, 97, 805, -1, 66, 775, -1, + 7683, -1, 98, 775, -1, 66, 803, -1, + 7685, -1, 98, 803, -1, 66, 817, -1, + 7687, -1, 98, 817, -1, 199, 769, -1, + 7689, -1, 231, 769, -1, 68, 775, -1, + 7691, -1, 100, 775, -1, 68, 803, -1, + 7693, -1, 100, 803, -1, 68, 817, -1, + 7695, -1, 100, 817, -1, 68, 807, -1, + 7697, -1, 100, 807, -1, 68, 813, -1, + 7699, -1, 100, 813, -1, 274, 768, -1, + 7701, -1, 275, 768, -1, 274, 769, -1, + 7703, -1, 275, 769, -1, 69, 813, -1, + 7705, -1, 101, 813, -1, 69, 816, -1, + 7707, -1, 101, 816, -1, 552, 774, -1, + 7709, -1, 553, 774, -1, 70, 775, -1, + 7711, -1, 102, 775, -1, 71, 772, -1, + 7713, -1, 103, 772, -1, 72, 775, -1, + 7715, -1, 104, 775, -1, 72, 803, -1, + 7717, -1, 104, 803, -1, 72, 776, -1, + 7719, -1, 104, 776, -1, 72, 807, -1, + 7721, -1, 104, 807, -1, 72, 814, -1, + 7723, -1, 104, 814, -1, 73, 816, -1, + 7725, -1, 105, 816, -1, 207, 769, -1, + 7727, -1, 239, 769, -1, 75, 769, -1, + 7729, -1, 107, 769, -1, 75, 803, -1, + 7731, -1, 107, 803, -1, 75, 817, -1, + 7733, -1, 107, 817, -1, 76, 803, -1, + 7735, -1, 108, 803, -1, 7734, 772, -1, + 7737, -1, 7735, 772, -1, 76, 817, -1, + 7739, -1, 108, 817, -1, 76, 813, -1, + 7741, -1, 108, 813, -1, 77, 769, -1, + 7743, -1, 109, 769, -1, 77, 775, -1, + 7745, -1, 109, 775, -1, 77, 803, -1, + 7747, -1, 109, 803, -1, 78, 775, -1, + 7749, -1, 110, 775, -1, 78, 803, -1, + 7751, -1, 110, 803, -1, 78, 817, -1, + 7753, -1, 110, 817, -1, 78, 813, -1, + 7755, -1, 110, 813, -1, 213, 769, -1, + 7757, -1, 245, 769, -1, 213, 776, -1, + 7759, -1, 245, 776, -1, 332, 768, -1, + 7761, -1, 333, 768, -1, 332, 769, -1, + 7763, -1, 333, 769, -1, 80, 769, -1, + 7765, -1, 112, 769, -1, 80, 775, -1, + 7767, -1, 112, 775, -1, 82, 775, -1, + 7769, -1, 114, 775, -1, 82, 803, -1, + 7771, -1, 114, 803, -1, 7770, 772, -1, + 7773, -1, 7771, 772, -1, 82, 817, -1, + 7775, -1, 114, 817, -1, 83, 775, -1, + 7777, -1, 115, 775, -1, 83, 803, -1, + 7779, -1, 115, 803, -1, 346, 775, -1, + 7781, -1, 347, 775, -1, 352, 775, -1, + 7783, -1, 353, 775, -1, 7778, 775, -1, + 7785, -1, 7779, 775, -1, 84, 775, -1, + 7787, -1, 116, 775, -1, 84, 803, -1, + 7789, -1, 116, 803, -1, 84, 817, -1, + 7791, -1, 116, 817, -1, 84, 813, -1, + 7793, -1, 116, 813, -1, 85, 804, -1, + 7795, -1, 117, 804, -1, 85, 816, -1, + 7797, -1, 117, 816, -1, 85, 813, -1, + 7799, -1, 117, 813, -1, 360, 769, -1, + 7801, -1, 361, 769, -1, 362, 776, -1, + 7803, -1, 363, 776, -1, 86, 771, -1, + 7805, -1, 118, 771, -1, 86, 803, -1, + 7807, -1, 118, 803, -1, 87, 768, -1, + 7809, -1, 119, 768, -1, 87, 769, -1, + 7811, -1, 119, 769, -1, 87, 776, -1, + 7813, -1, 119, 776, -1, 87, 775, -1, + 7815, -1, 119, 775, -1, 87, 803, -1, + 7817, -1, 119, 803, -1, 88, 775, -1, + 7819, -1, 120, 775, -1, 88, 776, -1, + 7821, -1, 120, 776, -1, 89, 775, -1, + 7823, -1, 121, 775, -1, 90, 770, -1, + 7825, -1, 122, 770, -1, 90, 803, -1, + 7827, -1, 122, 803, -1, 90, 817, -1, + 7829, -1, 122, 817, -1, 104, 817, -1, + 116, 776, -1, 119, 778, -1, 121, 778, + -1, 97, 702, -1, 383, 775, -1, 65, + 803, -1, 7841, -1, 97, 803, -1, 65, + 777, -1, 7843, -1, 97, 777, -1, 194, + 769, -1, 7845, -1, 226, 769, -1, 194, + 768, -1, 7847, -1, 226, 768, -1, 194, + 777, -1, 7849, -1, 226, 777, -1, 194, + 771, -1, 7851, -1, 226, 771, -1, 7840, + 770, -1, 7853, -1, 7841, 770, -1, 258, + 769, -1, 7855, -1, 259, 769, -1, 258, + 768, -1, 7857, -1, 259, 768, -1, 258, + 777, -1, 7859, -1, 259, 777, -1, 258, + 771, -1, 7861, -1, 259, 771, -1, 7840, + 774, -1, 7863, -1, 7841, 774, -1, 69, + 803, -1, 7865, -1, 101, 803, -1, 69, + 777, -1, 7867, -1, 101, 777, -1, 69, + 771, -1, 7869, -1, 101, 771, -1, 202, + 769, -1, 7871, -1, 234, 769, -1, 202, + 768, -1, 7873, -1, 234, 768, -1, 202, + 777, -1, 7875, -1, 234, 777, -1, 202, + 771, -1, 7877, -1, 234, 771, -1, 7864, + 770, -1, 7879, -1, 7865, 770, -1, 73, + 777, -1, 7881, -1, 105, 777, -1, 73, + 803, -1, 7883, -1, 105, 803, -1, 79, + 803, -1, 7885, -1, 111, 803, -1, 79, + 777, -1, 7887, -1, 111, 777, -1, 212, + 769, -1, 7889, -1, 244, 769, -1, 212, + 768, -1, 7891, -1, 244, 768, -1, 212, + 777, -1, 7893, -1, 244, 777, -1, 212, + 771, -1, 7895, -1, 244, 771, -1, 7884, + 770, -1, 7897, -1, 7885, 770, -1, 416, + 769, -1, 7899, -1, 417, 769, -1, 416, + 768, -1, 7901, -1, 417, 768, -1, 416, + 777, -1, 7903, -1, 417, 777, -1, 416, + 771, -1, 7905, -1, 417, 771, -1, 416, + 803, -1, 7907, -1, 417, 803, -1, 85, + 803, -1, 7909, -1, 117, 803, -1, 85, + 777, -1, 7911, -1, 117, 777, -1, 431, + 769, -1, 7913, -1, 432, 769, -1, 431, + 768, -1, 7915, -1, 432, 768, -1, 431, + 777, -1, 7917, -1, 432, 777, -1, 431, + 771, -1, 7919, -1, 432, 771, -1, 431, + 803, -1, 7921, -1, 432, 803, -1, 89, + 768, -1, 7923, -1, 121, 768, -1, 89, + 803, -1, 7925, -1, 121, 803, -1, 89, + 777, -1, 7927, -1, 121, 777, -1, 89, + 771, -1, 7929, -1, 121, 771, -1, 945, + 787, -1, 945, 788, -1, 7936, 768, -1, + 7937, 768, -1, 7936, 769, -1, 7937, 769, + -1, 7936, 834, -1, 7937, 834, -1, 913, + 787, -1, 7936, -1, 913, 788, -1, 7937, + -1, 7944, 768, -1, 7938, -1, 7945, 768, + -1, 7939, -1, 7944, 769, -1, 7940, -1, + 7945, 769, -1, 7941, -1, 7944, 834, -1, + 7942, -1, 7945, 834, -1, 7943, -1, 949, + 787, -1, 949, 788, -1, 7952, 768, -1, + 7953, 768, -1, 7952, 769, -1, 7953, 769, + -1, 917, 787, -1, 7952, -1, 917, 788, + -1, 7953, -1, 7960, 768, -1, 7954, -1, + 7961, 768, -1, 7955, -1, 7960, 769, -1, + 7956, -1, 7961, 769, -1, 7957, -1, 951, + 787, -1, 951, 788, -1, 7968, 768, -1, + 7969, 768, -1, 7968, 769, -1, 7969, 769, + -1, 7968, 834, -1, 7969, 834, -1, 919, + 787, -1, 7968, -1, 919, 788, -1, 7969, + -1, 7976, 768, -1, 7970, -1, 7977, 768, + -1, 7971, -1, 7976, 769, -1, 7972, -1, + 7977, 769, -1, 7973, -1, 7976, 834, -1, + 7974, -1, 7977, 834, -1, 7975, -1, 953, + 787, -1, 953, 788, -1, 7984, 768, -1, + 7985, 768, -1, 7984, 769, -1, 7985, 769, + -1, 7984, 834, -1, 7985, 834, -1, 921, + 787, -1, 7984, -1, 921, 788, -1, 7985, + -1, 7992, 768, -1, 7986, -1, 7993, 768, + -1, 7987, -1, 7992, 769, -1, 7988, -1, + 7993, 769, -1, 7989, -1, 7992, 834, -1, + 7990, -1, 7993, 834, -1, 7991, -1, 959, + 787, -1, 959, 788, -1, 8000, 768, -1, + 8001, 768, -1, 8000, 769, -1, 8001, 769, + -1, 927, 787, -1, 8000, -1, 927, 788, + -1, 8001, -1, 8008, 768, -1, 8002, -1, + 8009, 768, -1, 8003, -1, 8008, 769, -1, + 8004, -1, 8009, 769, -1, 8005, -1, 965, + 787, -1, 965, 788, -1, 8016, 768, -1, + 965, 787, 768, -1, 8017, 768, -1, 8016, + 769, -1, 965, 787, 769, -1, 8017, 769, + -1, 8016, 834, -1, 965, 787, 834, -1, + 8017, 834, -1, 933, 788, -1, 8017, -1, + 8025, 768, -1, 8019, -1, 8025, 769, -1, + 8021, -1, 8025, 834, -1, 8023, -1, 969, + 787, -1, 969, 788, -1, 8032, 768, -1, + 8033, 768, -1, 8032, 769, -1, 8033, 769, + -1, 8032, 834, -1, 8033, 834, -1, 937, + 787, -1, 8032, -1, 937, 788, -1, 8033, + -1, 8040, 768, -1, 8034, -1, 8041, 768, + -1, 8035, -1, 8040, 769, -1, 8036, -1, + 8041, 769, -1, 8037, -1, 8040, 834, -1, + 8038, -1, 8041, 834, -1, 8039, -1, 945, + 768, -1, 949, 768, -1, 951, 768, -1, + 953, 768, -1, 959, 768, -1, 965, 768, + -1, 969, 768, -1, 7936, 837, -1, 7936, + 953, -1, 7937, 837, -1, 7937, 953, -1, + 7938, 837, -1, 7938, 953, -1, 7939, 837, + -1, 7939, 953, -1, 7940, 837, -1, 7940, + 953, -1, 7941, 837, -1, 7941, 953, -1, + 7942, 837, -1, 7942, 953, -1, 7943, 837, + -1, 7943, 953, -1, 7944, 837, -1, 8064, + -1, 7945, 837, -1, 8065, -1, 7946, 837, + -1, 8066, -1, 7947, 837, -1, 8067, -1, + 7948, 837, -1, 8068, -1, 7949, 837, -1, + 8069, -1, 7950, 837, -1, 8070, -1, 7951, + 837, -1, 8071, -1, 7968, 837, -1, 7968, + 953, -1, 7969, 837, -1, 7969, 953, -1, + 7970, 837, -1, 7970, 953, -1, 7971, 837, + -1, 7971, 953, -1, 7972, 837, -1, 7972, + 953, -1, 7973, 837, -1, 7973, 953, -1, + 7974, 837, -1, 7974, 953, -1, 7975, 837, + -1, 7975, 953, -1, 7976, 837, -1, 8080, + -1, 7977, 837, -1, 8081, -1, 7978, 837, + -1, 8082, -1, 7979, 837, -1, 8083, -1, + 7980, 837, -1, 8084, -1, 7981, 837, -1, + 8085, -1, 7982, 837, -1, 8086, -1, 7983, + 837, -1, 8087, -1, 8032, 837, -1, 8032, + 953, -1, 8033, 837, -1, 8033, 953, -1, + 8034, 837, -1, 8034, 953, -1, 8035, 837, + -1, 8035, 953, -1, 8036, 837, -1, 8036, + 953, -1, 8037, 837, -1, 8037, 953, -1, + 8038, 837, -1, 8038, 953, -1, 8039, 837, + -1, 8039, 953, -1, 8040, 837, -1, 8096, + -1, 8041, 837, -1, 8097, -1, 8042, 837, + -1, 8098, -1, 8043, 837, -1, 8099, -1, + 8044, 837, -1, 8100, -1, 8045, 837, -1, + 8101, -1, 8046, 837, -1, 8102, -1, 8047, + 837, -1, 8103, -1, 945, 774, -1, 945, + 772, -1, 8048, 837, -1, 8048, 953, -1, + 945, 837, -1, 945, 953, -1, 940, 837, + -1, 940, 953, -1, 945, 834, -1, 8118, + 837, -1, 945, 834, 953, -1, 913, 774, + -1, 8112, -1, 913, 772, -1, 8113, -1, + 913, 768, -1, 8048, -1, 902, -1, 8049, + -1, 913, 837, -1, 8115, -1, 32, 787, + -1, 32, 834, -1, 168, 834, -1, 8052, + 837, -1, 8052, 953, -1, 951, 837, -1, + 951, 953, -1, 942, 837, -1, 942, 953, + -1, 951, 834, -1, 8134, 837, -1, 951, + 834, 953, -1, 917, 768, -1, 8050, -1, + 904, -1, 8051, -1, 919, 768, -1, 8052, + -1, 905, -1, 8053, -1, 919, 837, -1, + 8131, -1, 8127, 768, -1, 8127, 769, -1, + 8127, 834, -1, 953, 774, -1, 953, 772, + -1, 970, 768, -1, 953, 776, 768, -1, + 912, -1, 953, 834, -1, 970, 834, -1, + 953, 776, 834, -1, 921, 774, -1, 8144, + -1, 921, 772, -1, 8145, -1, 921, 768, + -1, 8054, -1, 906, -1, 8055, -1, 8190, + 768, -1, 8190, 769, -1, 8190, 834, -1, + 965, 774, -1, 965, 772, -1, 971, 768, + -1, 965, 776, 768, -1, 944, -1, 961, + 787, -1, 961, 788, -1, 965, 834, -1, + 971, 834, -1, 965, 776, 834, -1, 933, + 774, -1, 8160, -1, 933, 772, -1, 8161, + -1, 933, 768, -1, 8058, -1, 910, -1, + 8059, -1, 929, 788, -1, 8165, -1, 168, + 768, -1, 901, -1, 96, -1, 8060, 837, + -1, 8060, 953, -1, 969, 837, -1, 969, + 953, -1, 974, 837, -1, 974, 953, -1, + 969, 834, -1, 8182, 837, -1, 969, 834, + 953, -1, 927, 768, -1, 8056, -1, 908, + -1, 8057, -1, 937, 768, -1, 8060, -1, + 911, -1, 8061, -1, 937, 837, -1, 8179, + -1, 180, -1, 32, 788, -1, 8194, -1, + 8195, -1, 8208, -1, 32, 819, -1, 46, + -1, 46, 46, -1, 46, 46, 46, -1, + 8242, 8242, -1, 8242, 8242, 8242, -1, 8245, + 8245, -1, 8245, 8245, 8245, -1, 33, 33, + -1, 32, 773, -1, 63, 63, -1, 63, + 33, -1, 33, 63, -1, 8242, 8242, 8242, + 8242, -1, 48, -1, 52, -1, 53, -1, + 54, -1, 55, -1, 56, -1, 57, -1, + 43, -1, 8722, -1, 61, -1, 40, -1, + 41, -1, 82, 115, -1, 97, 47, 99, + -1, 97, 47, 115, -1, 67, -1, 176, + 67, -1, 99, 47, 111, -1, 99, 47, + 117, -1, 400, -1, 176, 70, -1, 78, + 111, -1, 81, -1, 83, 77, -1, 84, + 69, 76, -1, 84, 77, -1, 90, -1, + 937, -1, 197, -1, 70, -1, 8526, -1, + 1488, -1, 1489, -1, 1490, -1, 1491, -1, + 70, 65, 88, -1, 915, -1, 928, -1, + 8721, -1, 49, 8260, 51, -1, 50, 8260, + 51, -1, 49, 8260, 53, -1, 50, 8260, + 53, -1, 51, 8260, 53, -1, 52, 8260, + 53, -1, 49, 8260, 54, -1, 53, 8260, + 54, -1, 49, 8260, 56, -1, 51, 8260, + 56, -1, 53, 8260, 56, -1, 55, 8260, + 56, -1, 49, 8260, -1, 8560, -1, 73, + 73, -1, 8561, -1, 73, 73, 73, -1, + 8562, -1, 73, 86, -1, 8563, -1, 86, + -1, 8564, -1, 86, 73, -1, 8565, -1, + 86, 73, 73, -1, 8566, -1, 86, 73, + 73, 73, -1, 8567, -1, 73, 88, -1, + 8568, -1, 88, -1, 8569, -1, 88, 73, + -1, 8570, -1, 88, 73, 73, -1, 8571, + -1, 8572, -1, 8573, -1, 8574, -1, 8575, + -1, 105, 105, -1, 105, 105, 105, -1, + 105, 118, -1, 118, 105, -1, 118, 105, + 105, -1, 118, 105, 105, 105, -1, 105, + 120, -1, 120, 105, -1, 120, 105, 105, + -1, 8580, -1, 8592, 824, -1, 8594, 824, + -1, 8596, 824, -1, 8656, 824, -1, 8660, + 824, -1, 8658, 824, -1, 8707, 824, -1, + 8712, 824, -1, 8715, 824, -1, 8739, 824, + -1, 8741, 824, -1, 8747, 8747, -1, 8747, + 8747, 8747, -1, 8750, 8750, -1, 8750, 8750, + 8750, -1, 8764, 824, -1, 8771, 824, -1, + 8773, 824, -1, 8776, 824, -1, 61, 824, + -1, 8801, 824, -1, 8781, 824, -1, 60, + 824, -1, 62, 824, -1, 8804, 824, -1, + 8805, 824, -1, 8818, 824, -1, 8819, 824, + -1, 8822, 824, -1, 8823, 824, -1, 8826, + 824, -1, 8827, 824, -1, 8834, 824, -1, + 8835, 824, -1, 8838, 824, -1, 8839, 824, + -1, 8866, 824, -1, 8872, 824, -1, 8873, + 824, -1, 8875, 824, -1, 8828, 824, -1, + 8829, 824, -1, 8849, 824, -1, 8850, 824, + -1, 8882, 824, -1, 8883, 824, -1, 8884, + 824, -1, 8885, 824, -1, 12296, -1, 12297, + -1, 49, 48, -1, 49, 49, -1, 49, + 50, -1, 49, 51, -1, 49, 52, -1, + 49, 53, -1, 49, 54, -1, 49, 55, + -1, 49, 56, -1, 49, 57, -1, 50, + 48, -1, 40, 49, 41, -1, 40, 50, + 41, -1, 40, 51, 41, -1, 40, 52, + 41, -1, 40, 53, 41, -1, 40, 54, + 41, -1, 40, 55, 41, -1, 40, 56, + 41, -1, 40, 57, 41, -1, 40, 49, + 48, 41, -1, 40, 49, 49, 41, -1, + 40, 49, 50, 41, -1, 40, 49, 51, + 41, -1, 40, 49, 52, 41, -1, 40, + 49, 53, 41, -1, 40, 49, 54, 41, + -1, 40, 49, 55, 41, -1, 40, 49, + 56, 41, -1, 40, 49, 57, 41, -1, + 40, 50, 48, 41, -1, 49, 46, -1, + 50, 46, -1, 51, 46, -1, 52, 46, + -1, 53, 46, -1, 54, 46, -1, 55, + 46, -1, 56, 46, -1, 57, 46, -1, + 49, 48, 46, -1, 49, 49, 46, -1, + 49, 50, 46, -1, 49, 51, 46, -1, + 49, 52, 46, -1, 49, 53, 46, -1, + 49, 54, 46, -1, 49, 55, 46, -1, + 49, 56, 46, -1, 49, 57, 46, -1, + 50, 48, 46, -1, 40, 97, 41, -1, + 40, 98, 41, -1, 40, 99, 41, -1, + 40, 100, 41, -1, 40, 101, 41, -1, + 40, 102, 41, -1, 40, 103, 41, -1, + 40, 104, 41, -1, 40, 105, 41, -1, + 40, 106, 41, -1, 40, 107, 41, -1, + 40, 108, 41, -1, 40, 109, 41, -1, + 40, 110, 41, -1, 40, 111, 41, -1, + 40, 112, 41, -1, 40, 113, 41, -1, + 40, 114, 41, -1, 40, 115, 41, -1, + 40, 116, 41, -1, 40, 117, 41, -1, + 40, 118, 41, -1, 40, 119, 41, -1, + 40, 120, 41, -1, 40, 121, 41, -1, + 40, 122, 41, -1, 9424, -1, 9425, -1, + 9426, -1, 9427, -1, 9428, -1, 9429, -1, + 9430, -1, 9431, -1, 9432, -1, 9433, -1, + 9434, -1, 9435, -1, 9436, -1, 9437, -1, + 9438, -1, 9439, -1, 9440, -1, 9441, -1, + 83, -1, 9442, -1, 9443, -1, 9444, -1, + 9445, -1, 9446, -1, 9447, -1, 89, -1, + 9448, -1, 9449, -1, 8747, 8747, 8747, 8747, + -1, 58, 58, 61, -1, 61, 61, -1, + 61, 61, 61, -1, 10973, 824, -1, 11312, + -1, 11313, -1, 11314, -1, 11315, -1, 11316, + -1, 11317, -1, 11318, -1, 11319, -1, 11320, + -1, 11321, -1, 11322, -1, 11323, -1, 11324, + -1, 11325, -1, 11326, -1, 11327, -1, 11328, + -1, 11329, -1, 11330, -1, 11331, -1, 11332, + -1, 11333, -1, 11334, -1, 11335, -1, 11336, + -1, 11337, -1, 11338, -1, 11339, -1, 11340, + -1, 11341, -1, 11342, -1, 11343, -1, 11344, + -1, 11345, -1, 11346, -1, 11347, -1, 11348, + -1, 11349, -1, 11350, -1, 11351, -1, 11352, + -1, 11353, -1, 11354, -1, 11355, -1, 11356, + -1, 11357, -1, 11358, -1, 11361, -1, 619, + -1, 7549, -1, 637, -1, 11368, -1, 11370, + -1, 11372, -1, 11382, -1, 11393, -1, 11395, + -1, 11397, -1, 11399, -1, 11401, -1, 11403, + -1, 11405, -1, 11407, -1, 11409, -1, 11411, + -1, 11413, -1, 11415, -1, 11417, -1, 11419, + -1, 11421, -1, 11423, -1, 11425, -1, 11427, + -1, 11429, -1, 11431, -1, 11433, -1, 11435, + -1, 11437, -1, 11439, -1, 11441, -1, 11443, + -1, 11445, -1, 11447, -1, 11449, -1, 11451, + -1, 11453, -1, 11455, -1, 11457, -1, 11459, + -1, 11461, -1, 11463, -1, 11465, -1, 11467, + -1, 11469, -1, 11471, -1, 11473, -1, 11475, + -1, 11477, -1, 11479, -1, 11481, -1, 11483, + -1, 11485, -1, 11487, -1, 11489, -1, 11491, + -1, 11617, -1, 27597, -1, 40863, -1, 19968, + -1, 20008, -1, 20022, -1, 20031, -1, 20057, + -1, 20101, -1, 20108, -1, 20128, -1, 20154, + -1, 20799, -1, 20837, -1, 20843, -1, 20866, + -1, 20886, -1, 20907, -1, 20960, -1, 20981, + -1, 20992, -1, 21147, -1, 21241, -1, 21269, + -1, 21274, -1, 21304, -1, 21313, -1, 21340, + -1, 21353, -1, 21378, -1, 21430, -1, 21448, + -1, 21475, -1, 22231, -1, 22303, -1, 22763, + -1, 22786, -1, 22794, -1, 22805, -1, 22823, + -1, 22899, -1, 23376, -1, 23424, -1, 23544, + -1, 23567, -1, 23586, -1, 23608, -1, 23662, + -1, 23665, -1, 24027, -1, 24037, -1, 24049, + -1, 24062, -1, 24178, -1, 24186, -1, 24191, + -1, 24308, -1, 24318, -1, 24331, -1, 24339, + -1, 24400, -1, 24417, -1, 24435, -1, 24515, + -1, 25096, -1, 25142, -1, 25163, -1, 25903, + -1, 25908, -1, 25991, -1, 26007, -1, 26020, + -1, 26041, -1, 26080, -1, 26085, -1, 26352, + -1, 26376, -1, 26408, -1, 27424, -1, 27490, + -1, 27513, -1, 27571, -1, 27595, -1, 27604, + -1, 27611, -1, 27663, -1, 27668, -1, 27700, + -1, 28779, -1, 29226, -1, 29238, -1, 29243, + -1, 29247, -1, 29255, -1, 29273, -1, 29275, + -1, 29356, -1, 29572, -1, 29577, -1, 29916, + -1, 29926, -1, 29976, -1, 29983, -1, 29992, + -1, 30000, -1, 30091, -1, 30098, -1, 30326, + -1, 30333, -1, 30382, -1, 30399, -1, 30446, + -1, 30683, -1, 30690, -1, 30707, -1, 31034, + -1, 31160, -1, 31166, -1, 31348, -1, 31435, + -1, 31481, -1, 31859, -1, 31992, -1, 32566, + -1, 32593, -1, 32650, -1, 32701, -1, 32769, + -1, 32780, -1, 32786, -1, 32819, -1, 32895, + -1, 32905, -1, 33251, -1, 33258, -1, 33267, + -1, 33276, -1, 33292, -1, 33307, -1, 33311, + -1, 33390, -1, 33394, -1, 33400, -1, 34381, + -1, 34411, -1, 34880, -1, 34892, -1, 34915, + -1, 35198, -1, 35211, -1, 35282, -1, 35328, + -1, 35895, -1, 35910, -1, 35925, -1, 35960, + -1, 35997, -1, 36196, -1, 36208, -1, 36275, + -1, 36523, -1, 36554, -1, 36763, -1, 36784, + -1, 36789, -1, 37009, -1, 37193, -1, 37318, + -1, 37324, -1, 37329, -1, 38263, -1, 38272, + -1, 38428, -1, 38582, -1, 38585, -1, 38632, + -1, 38737, -1, 38750, -1, 38754, -1, 38761, + -1, 38859, -1, 38893, -1, 38899, -1, 38913, + -1, 39080, -1, 39131, -1, 39135, -1, 39318, + -1, 39321, -1, 39340, -1, 39592, -1, 39640, + -1, 39647, -1, 39717, -1, 39727, -1, 39730, + -1, 39740, -1, 39770, -1, 40165, -1, 40565, + -1, 40575, -1, 40613, -1, 40635, -1, 40643, + -1, 40653, -1, 40657, -1, 40697, -1, 40701, + -1, 40718, -1, 40723, -1, 40736, -1, 40763, + -1, 40778, -1, 40786, -1, 40845, -1, 40860, + -1, 40864, -1, 12306, -1, 21316, -1, 21317, + -1, 12363, 12441, -1, 12365, 12441, -1, 12367, + 12441, -1, 12369, 12441, -1, 12371, 12441, -1, + 12373, 12441, -1, 12375, 12441, -1, 12377, 12441, + -1, 12379, 12441, -1, 12381, 12441, -1, 12383, + 12441, -1, 12385, 12441, -1, 12388, 12441, -1, + 12390, 12441, -1, 12392, 12441, -1, 12399, 12441, + -1, 12399, 12442, -1, 12402, 12441, -1, 12402, + 12442, -1, 12405, 12441, -1, 12405, 12442, -1, + 12408, 12441, -1, 12408, 12442, -1, 12411, 12441, + -1, 12411, 12442, -1, 12358, 12441, -1, 32, + 12441, -1, 32, 12442, -1, 12445, 12441, -1, + 12424, 12426, -1, 12459, 12441, -1, 12461, 12441, + -1, 12463, 12441, -1, 12465, 12441, -1, 12467, + 12441, -1, 12469, 12441, -1, 12471, 12441, -1, + 12473, 12441, -1, 12475, 12441, -1, 12477, 12441, + -1, 12479, 12441, -1, 12481, 12441, -1, 12484, + 12441, -1, 12486, 12441, -1, 12488, 12441, -1, + 12495, 12441, -1, 12495, 12442, -1, 12498, 12441, + -1, 12498, 12442, -1, 12501, 12441, -1, 12501, + 12442, -1, 12504, 12441, -1, 12504, 12442, -1, + 12507, 12441, -1, 12507, 12442, -1, 12454, 12441, + -1, 12527, 12441, -1, 12528, 12441, -1, 12529, + 12441, -1, 12530, 12441, -1, 12541, 12441, -1, + 12467, 12488, -1, 4352, -1, 4353, -1, 4522, + -1, 4354, -1, 4524, -1, 4525, -1, 4355, + -1, 4356, -1, 4357, -1, 4528, -1, 4529, + -1, 4530, -1, 4531, -1, 4532, -1, 4533, + -1, 4378, -1, 4358, -1, 4359, -1, 4360, + -1, 4385, -1, 4361, -1, 4362, -1, 4363, + -1, 4364, -1, 4365, -1, 4366, -1, 4367, + -1, 4368, -1, 4369, -1, 4370, -1, 4449, + -1, 4450, -1, 4451, -1, 4452, -1, 4453, + -1, 4454, -1, 4455, -1, 4456, -1, 4457, + -1, 4458, -1, 4459, -1, 4460, -1, 4461, + -1, 4462, -1, 4463, -1, 4464, -1, 4465, + -1, 4466, -1, 4467, -1, 4468, -1, 4469, + -1, 4448, -1, 4372, -1, 4373, -1, 4551, + -1, 4552, -1, 4556, -1, 4558, -1, 4563, + -1, 4567, -1, 4569, -1, 4380, -1, 4573, + -1, 4575, -1, 4381, -1, 4382, -1, 4384, + -1, 4386, -1, 4387, -1, 4391, -1, 4393, + -1, 4395, -1, 4396, -1, 4397, -1, 4398, + -1, 4399, -1, 4402, -1, 4406, -1, 4416, + -1, 4423, -1, 4428, -1, 4593, -1, 4594, + -1, 4439, -1, 4440, -1, 4441, -1, 4484, + -1, 4485, -1, 4488, -1, 4497, -1, 4498, + -1, 4500, -1, 4510, -1, 4513, -1, 19977, + -1, 22235, -1, 19978, -1, 20013, -1, 19979, + -1, 30002, -1, 19993, -1, 19969, -1, 22825, + -1, 22320, -1, 40, 4352, 41, -1, 40, + 4354, 41, -1, 40, 4355, 41, -1, 40, + 4357, 41, -1, 40, 4358, 41, -1, 40, + 4359, 41, -1, 40, 4361, 41, -1, 40, + 4363, 41, -1, 40, 4364, 41, -1, 40, + 4366, 41, -1, 40, 4367, 41, -1, 40, + 4368, 41, -1, 40, 4369, 41, -1, 40, + 4370, 41, -1, 40, 4352, 4449, 41, -1, + 40, 4354, 4449, 41, -1, 40, 4355, 4449, + 41, -1, 40, 4357, 4449, 41, -1, 40, + 4358, 4449, 41, -1, 40, 4359, 4449, 41, + -1, 40, 4361, 4449, 41, -1, 40, 4363, + 4449, 41, -1, 40, 4364, 4449, 41, -1, + 40, 4366, 4449, 41, -1, 40, 4367, 4449, + 41, -1, 40, 4368, 4449, 41, -1, 40, + 4369, 4449, 41, -1, 40, 4370, 4449, 41, + -1, 40, 4364, 4462, 41, -1, 40, 4363, + 4457, 4364, 4453, 4523, 41, -1, 40, 4363, + 4457, 4370, 4462, 41, -1, 40, 19968, 41, + -1, 40, 20108, 41, -1, 40, 19977, 41, + -1, 40, 22235, 41, -1, 40, 20116, 41, + -1, 40, 20845, 41, -1, 40, 19971, 41, + -1, 40, 20843, 41, -1, 40, 20061, 41, + -1, 40, 21313, 41, -1, 40, 26376, 41, + -1, 40, 28779, 41, -1, 40, 27700, 41, + -1, 40, 26408, 41, -1, 40, 37329, 41, + -1, 40, 22303, 41, -1, 40, 26085, 41, + -1, 40, 26666, 41, -1, 40, 26377, 41, + -1, 40, 31038, 41, -1, 40, 21517, 41, + -1, 40, 29305, 41, -1, 40, 36001, 41, + -1, 40, 31069, 41, -1, 40, 21172, 41, + -1, 40, 20195, 41, -1, 40, 21628, 41, + -1, 40, 23398, 41, -1, 40, 30435, 41, + -1, 40, 20225, 41, -1, 40, 36039, 41, + -1, 40, 21332, 41, -1, 40, 31085, 41, + -1, 40, 20241, 41, -1, 40, 33258, 41, + -1, 40, 33267, 41, -1, 80, 84, 69, + -1, 50, 49, -1, 50, 50, -1, 50, + 51, -1, 50, 52, -1, 50, 53, -1, + 50, 54, -1, 50, 55, -1, 50, 56, + -1, 50, 57, -1, 51, 48, -1, 51, + 49, -1, 51, 50, -1, 51, 51, -1, + 51, 52, -1, 51, 53, -1, 4352, 4449, + -1, 4354, 4449, -1, 4355, 4449, -1, 4357, + 4449, -1, 4358, 4449, -1, 4359, 4449, -1, + 4361, 4449, -1, 4363, 4449, -1, 4364, 4449, + -1, 4366, 4449, -1, 4367, 4449, -1, 4368, + 4449, -1, 4369, 4449, -1, 4370, 4449, -1, + 4366, 4449, 4535, 4352, 4457, -1, 4364, 4462, + 4363, 4468, -1, 4363, 4462, -1, 20116, -1, + 20845, -1, 19971, -1, 20061, -1, 26666, -1, + 26377, -1, 31038, -1, 21517, -1, 29305, -1, + 36001, -1, 31069, -1, 21172, -1, 31192, -1, + 30007, -1, 36969, -1, 20778, -1, 21360, -1, + 27880, -1, 38917, -1, 20241, -1, 20889, -1, + 27491, -1, 24038, -1, 21491, -1, 21307, -1, + 23447, -1, 23398, -1, 30435, -1, 20225, -1, + 36039, -1, 21332, -1, 22812, -1, 51, 54, + -1, 51, 55, -1, 51, 56, -1, 51, + 57, -1, 52, 48, -1, 52, 49, -1, + 52, 50, -1, 52, 51, -1, 52, 52, + -1, 52, 53, -1, 52, 54, -1, 52, + 55, -1, 52, 56, -1, 52, 57, -1, + 53, 48, -1, 49, 26376, -1, 50, 26376, + -1, 51, 26376, -1, 52, 26376, -1, 53, + 26376, -1, 54, 26376, -1, 55, 26376, -1, + 56, 26376, -1, 57, 26376, -1, 49, 48, + 26376, -1, 49, 49, 26376, -1, 49, 50, + 26376, -1, 72, 103, -1, 101, 114, 103, + -1, 101, 86, -1, 76, 84, 68, -1, + 12450, -1, 12452, -1, 12454, -1, 12456, -1, + 12458, -1, 12459, -1, 12461, -1, 12463, -1, + 12465, -1, 12467, -1, 12469, -1, 12471, -1, + 12473, -1, 12475, -1, 12477, -1, 12479, -1, + 12481, -1, 12484, -1, 12486, -1, 12488, -1, + 12490, -1, 12491, -1, 12492, -1, 12493, -1, + 12494, -1, 12495, -1, 12498, -1, 12501, -1, + 12504, -1, 12507, -1, 12510, -1, 12511, -1, + 12512, -1, 12513, -1, 12514, -1, 12516, -1, + 12518, -1, 12520, -1, 12521, -1, 12522, -1, + 12523, -1, 12524, -1, 12525, -1, 12527, -1, + 12528, -1, 12529, -1, 12530, -1, 12450, 12497, + 12540, 12488, -1, 12450, 12523, 12501, 12449, -1, + 12450, 12531, 12506, 12450, -1, 12450, 12540, 12523, + -1, 12452, 12491, 12531, 12464, -1, 12452, 12531, + 12481, -1, 12454, 12457, 12531, -1, 12456, 12473, + 12463, 12540, 12489, -1, 12456, 12540, 12459, 12540, + -1, 12458, 12531, 12473, -1, 12458, 12540, 12512, + -1, 12459, 12452, 12522, -1, 12459, 12521, 12483, + 12488, -1, 12459, 12525, 12522, 12540, -1, 12460, + 12525, 12531, -1, 12460, 12531, 12510, -1, 12462, + 12460, -1, 12462, 12491, 12540, -1, 12461, 12517, + 12522, 12540, -1, 12462, 12523, 12480, 12540, -1, + 12461, 12525, -1, 12461, 12525, 12464, 12521, 12512, + -1, 12461, 12525, 12513, 12540, 12488, 12523, -1, + 12461, 12525, 12527, 12483, 12488, -1, 12464, 12521, + 12512, -1, 12464, 12521, 12512, 12488, 12531, -1, + 12463, 12523, 12476, 12452, 12525, -1, 12463, 12525, + 12540, 12493, -1, 12465, 12540, 12473, -1, 12467, + 12523, 12490, -1, 12467, 12540, 12509, -1, 12469, + 12452, 12463, 12523, -1, 12469, 12531, 12481, 12540, + 12512, -1, 12471, 12522, 12531, 12464, -1, 12475, + 12531, 12481, -1, 12475, 12531, 12488, -1, 12480, + 12540, 12473, -1, 12487, 12471, -1, 12489, 12523, + -1, 12488, 12531, -1, 12490, 12494, -1, 12494, + 12483, 12488, -1, 12495, 12452, 12484, -1, 12497, + 12540, 12475, 12531, 12488, -1, 12497, 12540, 12484, + -1, 12496, 12540, 12524, 12523, -1, 12500, 12450, + 12473, 12488, 12523, -1, 12500, 12463, 12523, -1, + 12500, 12467, -1, 12499, 12523, -1, 12501, 12449, + 12521, 12483, 12489, -1, 12501, 12451, 12540, 12488, + -1, 12502, 12483, 12471, 12455, 12523, -1, 12501, + 12521, 12531, -1, 12504, 12463, 12479, 12540, 12523, + -1, 12506, 12477, -1, 12506, 12491, 12498, -1, + 12504, 12523, 12484, -1, 12506, 12531, 12473, -1, + 12506, 12540, 12472, -1, 12505, 12540, 12479, -1, + 12509, 12452, 12531, 12488, -1, 12508, 12523, 12488, + -1, 12507, 12531, -1, 12509, 12531, 12489, -1, + 12507, 12540, 12523, -1, 12507, 12540, 12531, -1, + 12510, 12452, 12463, 12525, -1, 12510, 12452, 12523, + -1, 12510, 12483, 12495, -1, 12510, 12523, 12463, + -1, 12510, 12531, 12471, 12519, 12531, -1, 12511, + 12463, 12525, 12531, -1, 12511, 12522, -1, 12511, + 12522, 12496, 12540, 12523, -1, 12513, 12460, -1, + 12513, 12460, 12488, 12531, -1, 12513, 12540, 12488, + 12523, -1, 12516, 12540, 12489, -1, 12516, 12540, + 12523, -1, 12518, 12450, 12531, -1, 12522, 12483, + 12488, 12523, -1, 12522, 12521, -1, 12523, 12500, + 12540, -1, 12523, 12540, 12502, 12523, -1, 12524, + 12512, -1, 12524, 12531, 12488, 12466, 12531, -1, + 12527, 12483, 12488, -1, 48, 28857, -1, 49, + 28857, -1, 50, 28857, -1, 51, 28857, -1, + 52, 28857, -1, 53, 28857, -1, 54, 28857, + -1, 55, 28857, -1, 56, 28857, -1, 57, + 28857, -1, 49, 48, 28857, -1, 49, 49, + 28857, -1, 49, 50, 28857, -1, 49, 51, + 28857, -1, 49, 52, 28857, -1, 49, 53, + 28857, -1, 49, 54, 28857, -1, 49, 55, + 28857, -1, 49, 56, 28857, -1, 49, 57, + 28857, -1, 50, 48, 28857, -1, 50, 49, + 28857, -1, 50, 50, 28857, -1, 50, 51, + 28857, -1, 50, 52, 28857, -1, 104, 80, + 97, -1, 100, 97, -1, 65, 85, -1, + 98, 97, 114, -1, 111, 86, -1, 112, + 99, -1, 100, 109, -1, 100, 109, 178, + -1, 100, 109, 179, -1, 73, 85, -1, + 24179, 25104, -1, 26157, 21644, -1, 22823, 27491, + -1, 26126, 27835, -1, 26666, 24335, 20250, 31038, + -1, 112, 65, -1, 110, 65, -1, 956, + 65, -1, 109, 65, -1, 107, 65, -1, + 75, 66, -1, 77, 66, -1, 71, 66, + -1, 99, 97, 108, -1, 107, 99, 97, + 108, -1, 112, 70, -1, 110, 70, -1, + 956, 70, -1, 956, 103, -1, 109, 103, + -1, 107, 103, -1, 72, 122, -1, 107, + 72, 122, -1, 77, 72, 122, -1, 71, + 72, 122, -1, 84, 72, 122, -1, 956, + 8467, -1, 109, 8467, -1, 100, 8467, -1, + 107, 8467, -1, 102, 109, -1, 110, 109, + -1, 956, 109, -1, 109, 109, -1, 99, + 109, -1, 107, 109, -1, 109, 109, 178, + -1, 99, 109, 178, -1, 109, 178, -1, + 107, 109, 178, -1, 109, 109, 179, -1, + 99, 109, 179, -1, 109, 179, -1, 107, + 109, 179, -1, 109, 8725, 115, -1, 109, + 8725, 115, 178, -1, 80, 97, -1, 107, + 80, 97, -1, 77, 80, 97, -1, 71, + 80, 97, -1, 114, 97, 100, -1, 114, + 97, 100, 8725, 115, -1, 114, 97, 100, + 8725, 115, 178, -1, 112, 115, -1, 110, + 115, -1, 956, 115, -1, 109, 115, -1, + 112, 86, -1, 110, 86, -1, 956, 86, + -1, 109, 86, -1, 107, 86, -1, 77, + 86, -1, 112, 87, -1, 110, 87, -1, + 956, 87, -1, 109, 87, -1, 107, 87, + -1, 77, 87, -1, 107, 937, -1, 77, + 937, -1, 97, 46, 109, 46, -1, 66, + 113, -1, 99, 99, -1, 99, 100, -1, + 67, 8725, 107, 103, -1, 67, 111, 46, + -1, 100, 66, -1, 71, 121, -1, 104, + 97, -1, 72, 80, -1, 105, 110, -1, + 75, 75, -1, 75, 77, -1, 107, 116, + -1, 108, 109, -1, 108, 110, -1, 108, + 111, 103, -1, 108, 120, -1, 109, 98, + -1, 109, 105, 108, -1, 109, 111, 108, + -1, 80, 72, -1, 112, 46, 109, 46, + -1, 80, 80, 77, -1, 80, 82, -1, + 115, 114, -1, 83, 118, -1, 87, 98, + -1, 86, 8725, 109, -1, 65, 8725, 109, + -1, 49, 26085, -1, 50, 26085, -1, 51, + 26085, -1, 52, 26085, -1, 53, 26085, -1, + 54, 26085, -1, 55, 26085, -1, 56, 26085, + -1, 57, 26085, -1, 49, 48, 26085, -1, + 49, 49, 26085, -1, 49, 50, 26085, -1, + 49, 51, 26085, -1, 49, 52, 26085, -1, + 49, 53, 26085, -1, 49, 54, 26085, -1, + 49, 55, 26085, -1, 49, 56, 26085, -1, + 49, 57, 26085, -1, 50, 48, 26085, -1, + 50, 49, 26085, -1, 50, 50, 26085, -1, + 50, 51, 26085, -1, 50, 52, 26085, -1, + 50, 53, 26085, -1, 50, 54, 26085, -1, + 50, 55, 26085, -1, 50, 56, 26085, -1, + 50, 57, 26085, -1, 51, 48, 26085, -1, + 51, 49, 26085, -1, 103, 97, 108, -1, + 35912, -1, 26356, -1, 36040, -1, 28369, -1, + 20018, -1, 21477, -1, 22865, -1, 21895, -1, + 22856, -1, 25078, -1, 30313, -1, 32645, -1, + 34367, -1, 34746, -1, 35064, -1, 37007, -1, + 27138, -1, 27931, -1, 28889, -1, 29662, -1, + 33853, -1, 37226, -1, 39409, -1, 20098, -1, + 21365, -1, 27396, -1, 29211, -1, 34349, -1, + 40478, -1, 23888, -1, 28651, -1, 34253, -1, + 35172, -1, 25289, -1, 33240, -1, 34847, -1, + 24266, -1, 26391, -1, 28010, -1, 29436, -1, + 37070, -1, 20358, -1, 20919, -1, 21214, -1, + 25796, -1, 27347, -1, 29200, -1, 30439, -1, + 34310, -1, 34396, -1, 36335, -1, 38706, -1, + 39791, -1, 40442, -1, 30860, -1, 31103, -1, + 32160, -1, 33737, -1, 37636, -1, 35542, -1, + 22751, -1, 24324, -1, 31840, -1, 32894, -1, + 29282, -1, 30922, -1, 36034, -1, 38647, -1, + 22744, -1, 23650, -1, 27155, -1, 28122, -1, + 28431, -1, 32047, -1, 32311, -1, 38475, -1, + 21202, -1, 32907, -1, 20956, -1, 20940, -1, + 31260, -1, 32190, -1, 33777, -1, 38517, -1, + 35712, -1, 25295, -1, 35582, -1, 20025, -1, + 23527, -1, 24594, -1, 29575, -1, 30064, -1, + 21271, -1, 30971, -1, 20415, -1, 24489, -1, + 19981, -1, 27852, -1, 25976, -1, 32034, -1, + 21443, -1, 22622, -1, 30465, -1, 33865, -1, + 35498, -1, 27578, -1, 27784, -1, 25342, -1, + 33509, -1, 25504, -1, 30053, -1, 20142, -1, + 20841, -1, 20937, -1, 26753, -1, 31975, -1, + 33391, -1, 35538, -1, 37327, -1, 21237, -1, + 21570, -1, 24300, -1, 26053, -1, 28670, -1, + 31018, -1, 38317, -1, 39530, -1, 40599, -1, + 40654, -1, 26310, -1, 27511, -1, 36706, -1, + 24180, -1, 24976, -1, 25088, -1, 25754, -1, + 28451, -1, 29001, -1, 29833, -1, 31178, -1, + 32244, -1, 32879, -1, 36646, -1, 34030, -1, + 36899, -1, 37706, -1, 21015, -1, 21155, -1, + 21693, -1, 28872, -1, 35010, -1, 24265, -1, + 24565, -1, 25467, -1, 27566, -1, 31806, -1, + 29557, -1, 20196, -1, 22265, -1, 23994, -1, + 24604, -1, 29618, -1, 29801, -1, 32666, -1, + 32838, -1, 37428, -1, 38646, -1, 38728, -1, + 38936, -1, 20363, -1, 31150, -1, 37300, -1, + 38584, -1, 24801, -1, 20102, -1, 20698, -1, + 23534, -1, 23615, -1, 26009, -1, 29134, -1, + 30274, -1, 34044, -1, 36988, -1, 26248, -1, + 38446, -1, 21129, -1, 26491, -1, 26611, -1, + 27969, -1, 28316, -1, 29705, -1, 30041, -1, + 30827, -1, 32016, -1, 39006, -1, 25134, -1, + 38520, -1, 20523, -1, 23833, -1, 28138, -1, + 36650, -1, 24459, -1, 24900, -1, 26647, -1, + 38534, -1, 21033, -1, 21519, -1, 23653, -1, + 26131, -1, 26446, -1, 26792, -1, 27877, -1, + 29702, -1, 30178, -1, 32633, -1, 35023, -1, + 35041, -1, 38626, -1, 21311, -1, 28346, -1, + 21533, -1, 29136, -1, 29848, -1, 34298, -1, + 38563, -1, 40023, -1, 40607, -1, 26519, -1, + 28107, -1, 33256, -1, 31520, -1, 31890, -1, + 29376, -1, 28825, -1, 35672, -1, 20160, -1, + 33590, -1, 21050, -1, 20999, -1, 24230, -1, + 25299, -1, 31958, -1, 23429, -1, 27934, -1, + 26292, -1, 36667, -1, 38477, -1, 24275, -1, + 20800, -1, 21952, -1, 22618, -1, 26228, -1, + 20958, -1, 29482, -1, 30410, -1, 31036, -1, + 31070, -1, 31077, -1, 31119, -1, 38742, -1, + 31934, -1, 34322, -1, 35576, -1, 36920, -1, + 37117, -1, 39151, -1, 39164, -1, 39208, -1, + 40372, -1, 20398, -1, 20711, -1, 20813, -1, + 21193, -1, 21220, -1, 21329, -1, 21917, -1, + 22022, -1, 22120, -1, 22592, -1, 22696, -1, + 23652, -1, 24724, -1, 24936, -1, 24974, -1, + 25074, -1, 25935, -1, 26082, -1, 26257, -1, + 26757, -1, 28023, -1, 28186, -1, 28450, -1, + 29038, -1, 29227, -1, 29730, -1, 30865, -1, + 31049, -1, 31048, -1, 31056, -1, 31062, -1, + 31117, -1, 31118, -1, 31296, -1, 31361, -1, + 31680, -1, 32265, -1, 32321, -1, 32626, -1, + 32773, -1, 33261, -1, 33401, -1, 33879, -1, + 35088, -1, 35222, -1, 35585, -1, 35641, -1, + 36051, -1, 36104, -1, 36790, -1, 38627, -1, + 38911, -1, 38971, -1, 20006, -1, 20917, -1, + 20840, -1, 20352, -1, 20805, -1, 20864, -1, + 21191, -1, 21242, -1, 21845, -1, 21913, -1, + 21986, -1, 22707, -1, 22852, -1, 22868, -1, + 23138, -1, 23336, -1, 24274, -1, 24281, -1, + 24425, -1, 24493, -1, 24792, -1, 24910, -1, + 24840, -1, 24928, -1, 25140, -1, 25540, -1, + 25628, -1, 25682, -1, 25942, -1, 26395, -1, + 26454, -1, 28379, -1, 28363, -1, 28702, -1, + 30631, -1, 29237, -1, 29359, -1, 29809, -1, + 29958, -1, 30011, -1, 30237, -1, 30239, -1, + 30427, -1, 30452, -1, 30538, -1, 30528, -1, + 30924, -1, 31409, -1, 31867, -1, 32091, -1, + 32574, -1, 33618, -1, 33775, -1, 34681, -1, + 35137, -1, 35206, -1, 35519, -1, 35531, -1, + 35565, -1, 35722, -1, 36664, -1, 36978, -1, + 37273, -1, 37494, -1, 38524, -1, 38875, -1, + 38923, -1, 39698, -1, 141386, -1, 141380, -1, + 144341, -1, 15261, -1, 16408, -1, 16441, -1, + 152137, -1, 154832, -1, 163539, -1, 40771, -1, + 40846, -1, 102, 102, -1, 102, 105, -1, + 102, 108, -1, 102, 102, 105, -1, 102, + 102, 108, -1, 383, 116, -1, 115, 116, + -1, 1396, 1398, -1, 1396, 1381, -1, 1396, + 1387, -1, 1406, 1398, -1, 1396, 1389, -1, + 1497, 1460, -1, 1522, 1463, -1, 1506, -1, + 1492, -1, 1499, -1, 1500, -1, 1501, -1, + 1512, -1, 1514, -1, 1513, 1473, -1, 1513, + 1474, -1, 64329, 1473, -1, 64329, 1474, -1, + 1488, 1463, -1, 1488, 1464, -1, 1488, 1468, + -1, 1489, 1468, -1, 1490, 1468, -1, 1491, + 1468, -1, 1492, 1468, -1, 1493, 1468, -1, + 1494, 1468, -1, 1496, 1468, -1, 1497, 1468, + -1, 1498, 1468, -1, 1499, 1468, -1, 1500, + 1468, -1, 1502, 1468, -1, 1504, 1468, -1, + 1505, 1468, -1, 1507, 1468, -1, 1508, 1468, + -1, 1510, 1468, -1, 1511, 1468, -1, 1512, + 1468, -1, 1513, 1468, -1, 1514, 1468, -1, + 1493, 1465, -1, 1489, 1471, -1, 1499, 1471, + -1, 1508, 1471, -1, 1488, 1500, -1, 1649, + -1, 1659, -1, 1662, -1, 1664, -1, 1658, + -1, 1663, -1, 1657, -1, 1700, -1, 1702, + -1, 1668, -1, 1667, -1, 1670, -1, 1671, + -1, 1677, -1, 1676, -1, 1678, -1, 1672, + -1, 1688, -1, 1681, -1, 1705, -1, 1711, + -1, 1715, -1, 1713, -1, 1722, -1, 1723, + -1, 1728, -1, 1729, -1, 1726, -1, 1746, + -1, 1747, -1, 1709, -1, 1735, -1, 1734, + -1, 1736, -1, 1655, -1, 1739, -1, 1733, + -1, 1737, -1, 1744, -1, 1609, -1, 1574, + 1575, -1, 1574, 1749, -1, 1574, 1608, -1, + 1574, 1735, -1, 1574, 1734, -1, 1574, 1736, + -1, 1574, 1744, -1, 1574, 1609, -1, 1740, + -1, 1574, 1580, -1, 1574, 1581, -1, 1574, + 1605, -1, 1574, 1610, -1, 1576, 1580, -1, + 1576, 1581, -1, 1576, 1582, -1, 1576, 1605, + -1, 1576, 1609, -1, 1576, 1610, -1, 1578, + 1580, -1, 1578, 1581, -1, 1578, 1582, -1, + 1578, 1605, -1, 1578, 1609, -1, 1578, 1610, + -1, 1579, 1580, -1, 1579, 1605, -1, 1579, + 1609, -1, 1579, 1610, -1, 1580, 1581, -1, + 1580, 1605, -1, 1581, 1580, -1, 1581, 1605, + -1, 1582, 1580, -1, 1582, 1581, -1, 1582, + 1605, -1, 1587, 1580, -1, 1587, 1581, -1, + 1587, 1582, -1, 1587, 1605, -1, 1589, 1581, + -1, 1589, 1605, -1, 1590, 1580, -1, 1590, + 1581, -1, 1590, 1582, -1, 1590, 1605, -1, + 1591, 1581, -1, 1591, 1605, -1, 1592, 1605, + -1, 1593, 1580, -1, 1593, 1605, -1, 1594, + 1580, -1, 1594, 1605, -1, 1601, 1580, -1, + 1601, 1581, -1, 1601, 1582, -1, 1601, 1605, + -1, 1601, 1609, -1, 1601, 1610, -1, 1602, + 1581, -1, 1602, 1605, -1, 1602, 1609, -1, + 1602, 1610, -1, 1603, 1575, -1, 1603, 1580, + -1, 1603, 1581, -1, 1603, 1582, -1, 1603, + 1604, -1, 1603, 1605, -1, 1603, 1609, -1, + 1603, 1610, -1, 1604, 1580, -1, 1604, 1581, + -1, 1604, 1582, -1, 1604, 1605, -1, 1604, + 1609, -1, 1604, 1610, -1, 1605, 1580, -1, + 1605, 1581, -1, 1605, 1582, -1, 1605, 1605, + -1, 1605, 1609, -1, 1605, 1610, -1, 1606, + 1580, -1, 1606, 1581, -1, 1606, 1582, -1, + 1606, 1605, -1, 1606, 1609, -1, 1606, 1610, + -1, 1607, 1580, -1, 1607, 1605, -1, 1607, + 1609, -1, 1607, 1610, -1, 1610, 1580, -1, + 1610, 1581, -1, 1610, 1582, -1, 1610, 1605, + -1, 1610, 1609, -1, 1610, 1610, -1, 1584, + 1648, -1, 1585, 1648, -1, 1609, 1648, -1, + 32, 1612, 1617, -1, 32, 1613, 1617, -1, + 32, 1614, 1617, -1, 32, 1615, 1617, -1, + 32, 1616, 1617, -1, 32, 1617, 1648, -1, + 1574, 1585, -1, 1574, 1586, -1, 1574, 1606, + -1, 1576, 1585, -1, 1576, 1586, -1, 1576, + 1606, -1, 1578, 1585, -1, 1578, 1586, -1, + 1578, 1606, -1, 1579, 1585, -1, 1579, 1586, + -1, 1579, 1606, -1, 1605, 1575, -1, 1606, + 1585, -1, 1606, 1586, -1, 1606, 1606, -1, + 1610, 1585, -1, 1610, 1586, -1, 1610, 1606, + -1, 1574, 1582, -1, 1574, 1607, -1, 1576, + 1607, -1, 1578, 1607, -1, 1589, 1582, -1, + 1604, 1607, -1, 1606, 1607, -1, 1607, 1648, + -1, 1610, 1607, -1, 1579, 1607, -1, 1587, + 1607, -1, 1588, 1605, -1, 1588, 1607, -1, + 1600, 1614, 1617, -1, 1600, 1615, 1617, -1, + 1600, 1616, 1617, -1, 1591, 1609, -1, 1591, + 1610, -1, 1593, 1609, -1, 1593, 1610, -1, + 1594, 1609, -1, 1594, 1610, -1, 1587, 1609, + -1, 1587, 1610, -1, 1588, 1609, -1, 1588, + 1610, -1, 1581, 1609, -1, 1581, 1610, -1, + 1580, 1609, -1, 1580, 1610, -1, 1582, 1609, + -1, 1582, 1610, -1, 1589, 1609, -1, 1589, + 1610, -1, 1590, 1609, -1, 1590, 1610, -1, + 1588, 1580, -1, 1588, 1581, -1, 1588, 1582, + -1, 1588, 1585, -1, 1587, 1585, -1, 1589, + 1585, -1, 1590, 1585, -1, 1575, 1611, -1, + 1578, 1580, 1605, -1, 1578, 1581, 1580, -1, + 1578, 1581, 1605, -1, 1578, 1582, 1605, -1, + 1578, 1605, 1580, -1, 1578, 1605, 1581, -1, + 1578, 1605, 1582, -1, 1580, 1605, 1581, -1, + 1581, 1605, 1610, -1, 1581, 1605, 1609, -1, + 1587, 1581, 1580, -1, 1587, 1580, 1581, -1, + 1587, 1580, 1609, -1, 1587, 1605, 1581, -1, + 1587, 1605, 1580, -1, 1587, 1605, 1605, -1, + 1589, 1581, 1581, -1, 1589, 1605, 1605, -1, + 1588, 1581, 1605, -1, 1588, 1580, 1610, -1, + 1588, 1605, 1582, -1, 1588, 1605, 1605, -1, + 1590, 1581, 1609, -1, 1590, 1582, 1605, -1, + 1591, 1605, 1581, -1, 1591, 1605, 1605, -1, + 1591, 1605, 1610, -1, 1593, 1580, 1605, -1, + 1593, 1605, 1605, -1, 1593, 1605, 1609, -1, + 1594, 1605, 1605, -1, 1594, 1605, 1610, -1, + 1594, 1605, 1609, -1, 1601, 1582, 1605, -1, + 1602, 1605, 1581, -1, 1602, 1605, 1605, -1, + 1604, 1581, 1605, -1, 1604, 1581, 1610, -1, + 1604, 1581, 1609, -1, 1604, 1580, 1580, -1, + 1604, 1582, 1605, -1, 1604, 1605, 1581, -1, + 1605, 1581, 1580, -1, 1605, 1581, 1605, -1, + 1605, 1581, 1610, -1, 1605, 1580, 1581, -1, + 1605, 1580, 1605, -1, 1605, 1582, 1580, -1, + 1605, 1582, 1605, -1, 1605, 1580, 1582, -1, + 1607, 1605, 1580, -1, 1607, 1605, 1605, -1, + 1606, 1581, 1605, -1, 1606, 1581, 1609, -1, + 1606, 1580, 1605, -1, 1606, 1580, 1609, -1, + 1606, 1605, 1610, -1, 1606, 1605, 1609, -1, + 1610, 1605, 1605, -1, 1576, 1582, 1610, -1, + 1578, 1580, 1610, -1, 1578, 1580, 1609, -1, + 1578, 1582, 1610, -1, 1578, 1582, 1609, -1, + 1578, 1605, 1610, -1, 1578, 1605, 1609, -1, + 1580, 1605, 1610, -1, 1580, 1581, 1609, -1, + 1580, 1605, 1609, -1, 1587, 1582, 1609, -1, + 1589, 1581, 1610, -1, 1588, 1581, 1610, -1, + 1590, 1581, 1610, -1, 1604, 1580, 1610, -1, + 1604, 1605, 1610, -1, 1610, 1581, 1610, -1, + 1610, 1580, 1610, -1, 1610, 1605, 1610, -1, + 1605, 1605, 1610, -1, 1602, 1605, 1610, -1, + 1606, 1581, 1610, -1, 1593, 1605, 1610, -1, + 1603, 1605, 1610, -1, 1606, 1580, 1581, -1, + 1605, 1582, 1610, -1, 1604, 1580, 1605, -1, + 1603, 1605, 1605, -1, 1580, 1581, 1610, -1, + 1581, 1580, 1610, -1, 1605, 1580, 1610, -1, + 1601, 1605, 1610, -1, 1576, 1581, 1610, -1, + 1587, 1582, 1610, -1, 1606, 1580, 1610, -1, + 1589, 1604, 1746, -1, 1602, 1604, 1746, -1, + 1575, 1604, 1604, 1607, -1, 1575, 1603, 1576, + 1585, -1, 1605, 1581, 1605, 1583, -1, 1589, + 1604, 1593, 1605, -1, 1585, 1587, 1608, 1604, + -1, 1593, 1604, 1610, 1607, -1, 1608, 1587, + 1604, 1605, -1, 1589, 1604, 1609, -1, 1589, + 1604, 1609, 32, 1575, 1604, 1604, 1607, 32, + 1593, 1604, 1610, 1607, 32, 1608, 1587, 1604, + 1605, -1, 1580, 1604, 32, 1580, 1604, 1575, + 1604, 1607, -1, 1585, 1740, 1575, 1604, -1, + 44, -1, 12289, -1, 12290, -1, 58, -1, + 33, -1, 63, -1, 12310, -1, 12311, -1, + 8230, -1, 8229, -1, 8212, -1, 8211, -1, + 95, -1, 123, -1, 125, -1, 12308, -1, + 12309, -1, 12304, -1, 12305, -1, 12298, -1, + 12299, -1, 12300, -1, 12301, -1, 12302, -1, + 12303, -1, 91, -1, 93, -1, 8254, -1, + 35, -1, 38, -1, 42, -1, 45, -1, + 60, -1, 62, -1, 92, -1, 36, -1, + 37, -1, 64, -1, 32, 1611, -1, 1600, + 1611, -1, 32, 1612, -1, 32, 1613, -1, + 32, 1614, -1, 1600, 1614, -1, 32, 1615, + -1, 1600, 1615, -1, 32, 1616, -1, 1600, + 1616, -1, 32, 1617, -1, 1600, 1617, -1, + 32, 1618, -1, 1600, 1618, -1, 1569, -1, + 1570, -1, 1571, -1, 1572, -1, 1573, -1, + 1574, -1, 1575, -1, 1576, -1, 1577, -1, + 1578, -1, 1579, -1, 1580, -1, 1581, -1, + 1582, -1, 1583, -1, 1584, -1, 1585, -1, + 1586, -1, 1587, -1, 1588, -1, 1589, -1, + 1590, -1, 1591, -1, 1592, -1, 1593, -1, + 1594, -1, 1601, -1, 1602, -1, 1603, -1, + 1604, -1, 1605, -1, 1606, -1, 1607, -1, + 1608, -1, 1610, -1, 1604, 1570, -1, 1604, + 1571, -1, 1604, 1573, -1, 1604, 1575, -1, + 34, -1, 39, -1, 47, -1, 65345, -1, + 65346, -1, 65347, -1, 65348, -1, 65349, -1, + 65350, -1, 65351, -1, 65352, -1, 65353, -1, + 65354, -1, 65355, -1, 65356, -1, 65357, -1, + 65358, -1, 65359, -1, 65360, -1, 65361, -1, + 65362, -1, 65363, -1, 65364, -1, 65365, -1, + 65366, -1, 65367, -1, 65368, -1, 65369, -1, + 65370, -1, 94, -1, 124, -1, 126, -1, + 10629, -1, 10630, -1, 12539, -1, 12449, -1, + 12451, -1, 12453, -1, 12455, -1, 12457, -1, + 12515, -1, 12517, -1, 12519, -1, 12483, -1, + 12540, -1, 12531, -1, 12441, -1, 12442, -1, + 12644, -1, 12593, -1, 12594, -1, 12595, -1, + 12596, -1, 12597, -1, 12598, -1, 12599, -1, + 12600, -1, 12601, -1, 12602, -1, 12603, -1, + 12604, -1, 12605, -1, 12606, -1, 12607, -1, + 12608, -1, 12609, -1, 12610, -1, 12611, -1, + 12612, -1, 12613, -1, 12614, -1, 12615, -1, + 12616, -1, 12617, -1, 12618, -1, 12619, -1, + 12620, -1, 12621, -1, 12622, -1, 12623, -1, + 12624, -1, 12625, -1, 12626, -1, 12627, -1, + 12628, -1, 12629, -1, 12630, -1, 12631, -1, + 12632, -1, 12633, -1, 12634, -1, 12635, -1, + 12636, -1, 12637, -1, 12638, -1, 12639, -1, + 12640, -1, 12641, -1, 12642, -1, 12643, -1, + 162, -1, 163, -1, 172, -1, 175, -1, + 166, -1, 165, -1, 8361, -1, 9474, -1, + 8592, -1, 8593, -1, 8594, -1, 8595, -1, + 9632, -1, 9675, -1, 66600, -1, 66601, -1, + 66602, -1, 66603, -1, 66604, -1, 66605, -1, + 66606, -1, 66607, -1, 66608, -1, 66609, -1, + 66610, -1, 66611, -1, 66612, -1, 66613, -1, + 66614, -1, 66615, -1, 66616, -1, 66617, -1, + 66618, -1, 66619, -1, 66620, -1, 66621, -1, + 66622, -1, 66623, -1, 66624, -1, 66625, -1, + 66626, -1, 66627, -1, 66628, -1, 66629, -1, + 66630, -1, 66631, -1, 66632, -1, 66633, -1, + 66634, -1, 66635, -1, 66636, -1, 66637, -1, + 66638, -1, 66639, -1, 119127, 119141, -1, 119128, + 119141, -1, 119135, 119150, -1, 119135, 119151, -1, + 119135, 119152, -1, 119135, 119153, -1, 119135, 119154, + -1, 119225, 119141, -1, 119226, 119141, -1, 119227, + 119150, -1, 119228, 119150, -1, 119227, 119151, -1, + 119228, 119151, -1, 305, -1, 567, -1, 913, + -1, 914, -1, 916, -1, 917, -1, 918, + -1, 919, -1, 921, -1, 922, -1, 923, + -1, 924, -1, 925, -1, 926, -1, 927, + -1, 929, -1, 1012, -1, 932, -1, 934, + -1, 935, -1, 936, -1, 8711, -1, 8706, + -1, 1013, -1, 977, -1, 1008, -1, 981, + -1, 1009, -1, 982, -1, 988, -1, 20029, + -1, 20024, -1, 20033, -1, 131362, -1, 20320, + -1, 20411, -1, 20482, -1, 20602, -1, 20633, + -1, 20687, -1, 13470, -1, 132666, -1, 20820, + -1, 20836, -1, 20855, -1, 132380, -1, 13497, + -1, 20839, -1, 20877, -1, 132427, -1, 20887, + -1, 20900, -1, 20172, -1, 20908, -1, 168415, + -1, 20995, -1, 13535, -1, 21051, -1, 21062, + -1, 21106, -1, 21111, -1, 13589, -1, 21253, + -1, 21254, -1, 21321, -1, 21338, -1, 21363, + -1, 21373, -1, 21375, -1, 133676, -1, 28784, + -1, 21450, -1, 21471, -1, 133987, -1, 21483, + -1, 21489, -1, 21510, -1, 21662, -1, 21560, + -1, 21576, -1, 21608, -1, 21666, -1, 21750, + -1, 21776, -1, 21843, -1, 21859, -1, 21892, + -1, 21931, -1, 21939, -1, 21954, -1, 22294, + -1, 22295, -1, 22097, -1, 22132, -1, 22766, + -1, 22478, -1, 22516, -1, 22541, -1, 22411, + -1, 22578, -1, 22577, -1, 22700, -1, 136420, + -1, 22770, -1, 22775, -1, 22790, -1, 22810, + -1, 22818, -1, 22882, -1, 136872, -1, 136938, + -1, 23020, -1, 23067, -1, 23079, -1, 23000, + -1, 23142, -1, 14062, -1, 14076, -1, 23304, + -1, 23358, -1, 137672, -1, 23491, -1, 23512, + -1, 23539, -1, 138008, -1, 23551, -1, 23558, + -1, 24403, -1, 14209, -1, 23648, -1, 23744, + -1, 23693, -1, 138724, -1, 23875, -1, 138726, + -1, 23918, -1, 23915, -1, 23932, -1, 24033, + -1, 24034, -1, 14383, -1, 24061, -1, 24104, + -1, 24125, -1, 24169, -1, 14434, -1, 139651, + -1, 14460, -1, 24240, -1, 24243, -1, 24246, + -1, 172946, -1, 140081, -1, 33281, -1, 24354, + -1, 14535, -1, 144056, -1, 156122, -1, 24418, + -1, 24427, -1, 14563, -1, 24474, -1, 24525, + -1, 24535, -1, 24569, -1, 24705, -1, 14650, + -1, 14620, -1, 141012, -1, 24775, -1, 24904, + -1, 24908, -1, 24954, -1, 25010, -1, 24996, + -1, 25007, -1, 25054, -1, 25104, -1, 25115, + -1, 25181, -1, 25265, -1, 25300, -1, 25424, + -1, 142092, -1, 25405, -1, 25340, -1, 25448, + -1, 25475, -1, 25572, -1, 142321, -1, 25634, + -1, 25541, -1, 25513, -1, 14894, -1, 25705, + -1, 25726, -1, 25757, -1, 25719, -1, 14956, + -1, 25964, -1, 143370, -1, 26083, -1, 26360, + -1, 26185, -1, 15129, -1, 15112, -1, 15076, + -1, 20882, -1, 20885, -1, 26368, -1, 26268, + -1, 32941, -1, 17369, -1, 26401, -1, 26462, + -1, 26451, -1, 144323, -1, 15177, -1, 26618, + -1, 26501, -1, 26706, -1, 144493, -1, 26766, + -1, 26655, -1, 26900, -1, 26946, -1, 27043, + -1, 27114, -1, 27304, -1, 145059, -1, 27355, + -1, 15384, -1, 27425, -1, 145575, -1, 27476, + -1, 15438, -1, 27506, -1, 27551, -1, 27579, + -1, 146061, -1, 138507, -1, 146170, -1, 27726, + -1, 146620, -1, 27839, -1, 27853, -1, 27751, + -1, 27926, -1, 27966, -1, 28009, -1, 28024, + -1, 28037, -1, 146718, -1, 27956, -1, 28207, + -1, 28270, -1, 15667, -1, 28359, -1, 147153, + -1, 28153, -1, 28526, -1, 147294, -1, 147342, + -1, 28614, -1, 28729, -1, 28699, -1, 15766, + -1, 28746, -1, 28797, -1, 28791, -1, 28845, + -1, 132389, -1, 28997, -1, 148067, -1, 29084, + -1, 148395, -1, 29224, -1, 29264, -1, 149000, + -1, 29312, -1, 29333, -1, 149301, -1, 149524, + -1, 29562, -1, 29579, -1, 16044, -1, 29605, + -1, 16056, -1, 29767, -1, 29788, -1, 29829, + -1, 29898, -1, 16155, -1, 29988, -1, 150582, + -1, 30014, -1, 150674, -1, 139679, -1, 30224, + -1, 151457, -1, 151480, -1, 151620, -1, 16380, + -1, 16392, -1, 151795, -1, 151794, -1, 151833, + -1, 151859, -1, 30494, -1, 30495, -1, 30603, + -1, 16454, -1, 16534, -1, 152605, -1, 30798, + -1, 16611, -1, 153126, -1, 153242, -1, 153285, + -1, 31211, -1, 16687, -1, 31306, -1, 31311, + -1, 153980, -1, 154279, -1, 31470, -1, 16898, + -1, 154539, -1, 31686, -1, 31689, -1, 16935, + -1, 154752, -1, 31954, -1, 17056, -1, 31976, + -1, 31971, -1, 32000, -1, 155526, -1, 32099, + -1, 17153, -1, 32199, -1, 32258, -1, 32325, + -1, 17204, -1, 156200, -1, 156231, -1, 17241, + -1, 156377, -1, 32634, -1, 156478, -1, 32661, + -1, 32762, -1, 156890, -1, 156963, -1, 32864, + -1, 157096, -1, 32880, -1, 144223, -1, 17365, + -1, 32946, -1, 33027, -1, 17419, -1, 33086, + -1, 23221, -1, 157607, -1, 157621, -1, 144275, + -1, 144284, -1, 33284, -1, 36766, -1, 17515, + -1, 33425, -1, 33419, -1, 33437, -1, 21171, + -1, 33457, -1, 33459, -1, 33469, -1, 33510, + -1, 158524, -1, 33565, -1, 33635, -1, 33709, + -1, 33571, -1, 33725, -1, 33767, -1, 33619, + -1, 33738, -1, 33740, -1, 33756, -1, 158774, + -1, 159083, -1, 158933, -1, 17707, -1, 34033, + -1, 34035, -1, 34070, -1, 160714, -1, 34148, + -1, 159532, -1, 17757, -1, 17761, -1, 159665, + -1, 159954, -1, 17771, -1, 34384, -1, 34407, + -1, 34409, -1, 34473, -1, 34440, -1, 34574, + -1, 34530, -1, 34600, -1, 34667, -1, 34694, + -1, 17879, -1, 34785, -1, 34817, -1, 17913, + -1, 34912, -1, 161383, -1, 35031, -1, 35038, + -1, 17973, -1, 35066, -1, 13499, -1, 161966, + -1, 162150, -1, 18110, -1, 18119, -1, 35488, + -1, 162984, -1, 36011, -1, 36033, -1, 36123, + -1, 36215, -1, 163631, -1, 133124, -1, 36299, + -1, 36284, -1, 36336, -1, 133342, -1, 36564, + -1, 165330, -1, 165357, -1, 37012, -1, 37105, + -1, 37137, -1, 165678, -1, 37147, -1, 37432, + -1, 37591, -1, 37592, -1, 37500, -1, 37881, + -1, 37909, -1, 166906, -1, 38283, -1, 18837, + -1, 38327, -1, 167287, -1, 18918, -1, 38595, + -1, 23986, -1, 38691, -1, 168261, -1, 168474, + -1, 19054, -1, 19062, -1, 38880, -1, 168970, + -1, 19122, -1, 169110, -1, 38953, -1, 169398, + -1, 39138, -1, 19251, -1, 39209, -1, 39335, + -1, 39362, -1, 39422, -1, 19406, -1, 170800, + -1, 40000, -1, 40189, -1, 19662, -1, 19693, + -1, 40295, -1, 172238, -1, 19704, -1, 172293, + -1, 172558, -1, 172689, -1, 19798, -1, 40702, + -1, 40709, -1, 40719, -1, 40726, -1, 173568, + -1, }; + +UTF8PROC_DATA +const uint16_t utf8proc_stage1table[] = { + 0, 256, 512, 768, 1024, 1280, 1536, + 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, + 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632, + 5888, 6144, 6400, 6656, 6912, 2048, 7168, 7424, + 7680, 7936, 8192, 8448, 8704, 8960, 9216, 9472, + 9728, 9984, 10240, 10496, 10752, 11008, 11264, 11520, + 11776, 12032, 12288, 12544, 12800, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 13056, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 13312, 13568, 5376, 5376, 5376, 13824, 2048, 2048, + 14080, 14336, 2048, 2048, 2048, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 14592, 14848, 14848, 14848, 14848, 14848, 14848, 14848, + 14848, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15360, 15616, 15872, 16128, 16384, 16640, + 16896, 17152, 17408, 2048, 17664, 17920, 2048, 2048, + 2048, 18176, 18432, 18688, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 5376, 5376, 5376, 18944, 19200, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 19456, 19712, 19968, 20224, 20480, 20736, 20992, + 21248, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, + 5376, 5376, 5376, 5376, 5376, 5376, 5376, 21504, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 21760, 22016, 22272, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 22528, 22784, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, + 2048, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 23040, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 15104, 15104, 15104, 15104, 15104, 15104, 15104, 15104, + 23040, }; + +UTF8PROC_DATA +const uint16_t utf8proc_stage2table[] = { + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 3, 2, 4, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5, 5, 5, + 6, 7, 8, 8, 9, 10, 9, 8, + 8, 11, 12, 8, 13, 14, 15, 14, + 14, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 14, 8, 17, 18, 19, + 8, 8, 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, 11, 8, 12, 46, + 47, 46, 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, 11, 74, 12, 74, + 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 75, 8, 10, 10, 10, 10, 76, + 76, 77, 76, 78, 79, 74, 80, 76, + 81, 82, 83, 84, 85, 86, 87, 76, + 8, 88, 89, 90, 91, 92, 93, 94, + 8, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, + 74, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, + 74, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 213, 298, + 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 213, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 213, 213, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 213, 339, 340, 341, 213, + 342, 339, 339, 339, 339, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, 357, 358, 359, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 433, + 434, 435, 213, 436, 437, 438, 439, 440, + 441, 442, 443, 444, 445, 446, 447, 448, + 449, 450, 451, 452, 453, 213, 213, 213, + 213, 213, 213, 454, 455, 456, 457, 458, + 213, 213, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, + 473, 213, 213, 213, 474, 475, 213, 476, + 477, 213, 478, 213, 479, 213, 213, 213, + 213, 480, 213, 213, 481, 213, 213, 213, + 213, 482, 483, 213, 484, 213, 213, 213, + 485, 213, 213, 486, 213, 213, 487, 213, + 213, 213, 213, 213, 213, 213, 488, 213, + 213, 489, 213, 213, 490, 213, 213, 213, + 213, 491, 492, 493, 494, 495, 213, 213, + 213, 213, 213, 496, 213, 339, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 497, 498, 499, 500, 501, 502, 503, + 504, 505, 506, 506, 507, 507, 507, 507, + 507, 507, 507, 46, 46, 46, 46, 506, + 506, 506, 506, 506, 506, 506, 506, 506, + 506, 507, 507, 46, 46, 46, 46, 46, + 46, 508, 509, 510, 511, 512, 513, 46, + 46, 514, 515, 516, 517, 518, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 507, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 519, 520, 521, 522, 523, 524, 525, + 526, 527, 528, 529, 530, 531, 524, 524, + 532, 524, 533, 524, 534, 535, 536, 537, + 537, 537, 537, 536, 538, 537, 537, 537, + 537, 537, 539, 539, 540, 541, 542, 543, + 544, 545, 537, 537, 537, 537, 546, 547, + 537, 548, 549, 537, 537, 550, 550, 550, + 550, 551, 537, 537, 537, 537, 524, 524, + 524, 552, 553, 554, 555, 556, 557, 524, + 537, 537, 537, 524, 524, 524, 537, 537, + 558, 524, 524, 524, 537, 537, 537, 537, + 524, 536, 537, 537, 524, 559, 560, 560, + 559, 560, 560, 559, 524, 524, 524, 524, + 524, 524, 524, 524, 524, 524, 524, 524, + 524, 0, 0, 0, 0, 561, 46, 0, + 0, 0, 0, 562, 563, 564, 565, 566, + 0, 0, 0, 0, 0, 86, 567, 568, + 569, 570, 571, 572, 0, 573, 0, 574, + 575, 576, 577, 578, 579, 580, 581, 582, + 583, 584, 585, 586, 587, 588, 589, 590, + 591, 592, 593, 0, 594, 595, 596, 597, + 598, 599, 600, 601, 602, 603, 604, 605, + 606, 607, 608, 609, 610, 611, 612, 613, + 614, 615, 616, 617, 618, 619, 620, 621, + 622, 623, 624, 625, 626, 627, 628, 629, + 630, 631, 632, 633, 634, 635, 636, 637, + 0, 638, 639, 640, 641, 642, 643, 644, + 213, 645, 646, 647, 648, 649, 650, 651, + 652, 653, 654, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 213, 672, 673, 74, + 674, 675, 676, 677, 678, 213, 679, 680, + 681, 682, 683, 684, 685, 686, 687, 688, + 689, 690, 691, 692, 693, 694, 695, 696, + 697, 698, 699, 700, 701, 702, 703, 704, + 705, 706, 707, 708, 709, 710, 711, 712, + 713, 714, 715, 716, 717, 718, 719, 720, + 721, 722, 723, 724, 725, 726, 727, 728, + 729, 730, 731, 732, 733, 734, 735, 736, + 737, 738, 739, 740, 741, 742, 743, 744, + 745, 746, 747, 748, 749, 750, 751, 752, + 753, 754, 755, 756, 757, 758, 759, 760, + 761, 762, 763, 764, 765, 766, 767, 768, + 769, 770, 771, 772, 773, 774, 775, 776, + 777, 778, 779, 780, 781, 782, 783, 784, + 785, 786, 787, 788, 789, 790, 791, 792, + 793, 794, 795, 796, 797, 798, 799, 800, + 801, 802, 803, 804, 805, 806, 807, 808, + 809, 810, 811, 812, 524, 524, 524, 524, + 0, 813, 813, 814, 815, 816, 817, 818, + 819, 820, 821, 822, 823, 824, 825, 826, + 827, 828, 829, 830, 831, 832, 833, 834, + 835, 836, 837, 838, 839, 840, 841, 842, + 843, 844, 845, 846, 847, 848, 849, 850, + 851, 852, 853, 854, 855, 856, 857, 858, + 859, 860, 861, 862, 863, 864, 865, 866, + 867, 868, 869, 870, 871, 872, 873, 874, + 875, 876, 877, 878, 879, 880, 881, 882, + 883, 884, 885, 886, 887, 888, 889, 890, + 891, 892, 893, 894, 895, 896, 897, 898, + 899, 900, 901, 902, 903, 904, 905, 906, + 907, 908, 909, 910, 911, 912, 913, 914, + 915, 916, 917, 918, 919, 920, 921, 922, + 923, 924, 925, 926, 927, 928, 929, 930, + 931, 932, 933, 934, 935, 936, 937, 938, + 939, 940, 941, 942, 943, 944, 945, 946, + 947, 948, 949, 950, 951, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 952, 953, 954, 955, 956, 957, + 958, 959, 960, 961, 962, 963, 964, 965, + 966, 967, 968, 969, 970, 971, 972, 973, + 974, 975, 976, 977, 978, 979, 980, 981, + 982, 983, 984, 985, 986, 987, 988, 989, + 0, 0, 507, 990, 990, 990, 990, 990, + 990, 0, 991, 992, 993, 994, 995, 996, + 997, 998, 999, 1000, 1001, 1002, 1003, 1004, + 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, + 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, + 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, + 1029, 0, 990, 1030, 0, 0, 0, 0, + 0, 0, 537, 524, 524, 524, 524, 537, + 524, 524, 524, 1031, 537, 524, 524, 524, + 524, 524, 524, 537, 537, 537, 537, 537, + 537, 524, 524, 537, 524, 524, 1031, 1032, + 524, 1033, 1034, 1035, 1036, 1037, 1038, 1039, + 1040, 1041, 1042, 1042, 1043, 1044, 1045, 1046, + 1047, 1046, 1048, 1049, 1046, 524, 537, 1046, + 1041, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 0, 0, 0, 0, + 0, 1050, 1050, 1050, 1046, 1046, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1051, 1051, 1051, 1051, 0, 0, 0, + 0, 0, 0, 0, 1052, 14, 1053, 76, + 76, 524, 524, 524, 524, 524, 524, 0, + 0, 0, 0, 0, 1053, 0, 0, 1053, + 1053, 0, 1054, 1055, 1056, 1057, 1058, 1059, + 1060, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 0, 0, 0, 0, + 0, 1061, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1062, 1054, 1063, 1064, 1065, 1066, 1067, + 1068, 1069, 1070, 1071, 1072, 1073, 1074, 537, + 524, 524, 524, 524, 524, 537, 524, 524, + 0, 1075, 1075, 1075, 1075, 1075, 1075, 1075, + 1075, 1075, 1075, 9, 1076, 1076, 1053, 1054, + 1054, 1077, 1054, 1054, 1054, 1054, 1078, 1079, + 1080, 1081, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1082, 1083, 1084, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1085, 1086, 1053, 1087, 524, + 524, 524, 524, 524, 524, 524, 1051, 813, + 524, 524, 524, 524, 537, 524, 1061, 1061, + 524, 524, 76, 537, 524, 524, 537, 1054, + 1054, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 1054, 1054, 1054, 1088, 1088, + 1054, 1053, 1053, 1053, 1053, 1053, 1053, 1053, + 1053, 1053, 1053, 1053, 1053, 1053, 1053, 0, + 80, 1054, 1089, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 524, 537, 524, 524, 537, 524, 524, + 537, 537, 537, 524, 537, 537, 524, 537, + 524, 524, 524, 537, 524, 537, 524, 537, + 524, 537, 524, 524, 0, 0, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1090, + 1090, 1090, 1090, 1090, 1090, 1090, 1090, 1090, + 1090, 1090, 1054, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1091, 1091, 1091, 1091, 1091, 1091, 1091, + 1091, 1091, 1091, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 524, 524, 524, 524, + 524, 524, 524, 537, 524, 1092, 1092, 76, + 8, 8, 8, 1092, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1090, 1090, 1093, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 1094, 1095, 339, 339, 339, 339, 339, + 339, 1096, 1097, 339, 1098, 1099, 339, 339, + 339, 339, 339, 0, 0, 1100, 339, 1093, + 1093, 1093, 1090, 1090, 1090, 1090, 1090, 1090, + 1090, 1090, 1093, 1093, 1093, 1093, 1101, 0, + 0, 339, 524, 537, 524, 524, 0, 0, + 0, 1102, 1103, 1104, 1105, 1106, 1107, 1108, + 1109, 339, 339, 1090, 1090, 990, 990, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 990, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 339, 339, 339, 339, + 339, 0, 1090, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 339, 339, 0, 0, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 0, 0, 0, 339, + 339, 339, 339, 0, 0, 1111, 339, 1112, + 1093, 1093, 1090, 1090, 1090, 1090, 0, 0, + 1113, 1093, 0, 0, 1114, 1115, 1101, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 1116, 0, 0, 0, 0, 1117, 1118, 0, + 1119, 339, 339, 1090, 1090, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 339, 339, 10, 10, 1120, 1120, 1120, + 1120, 1120, 1120, 812, 0, 0, 0, 0, + 0, 0, 1090, 1090, 1093, 0, 339, 339, + 339, 339, 339, 339, 0, 0, 0, 0, + 339, 339, 0, 0, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 1121, 0, 339, 1122, + 0, 339, 339, 0, 0, 1111, 0, 1093, + 1093, 1093, 1090, 1090, 0, 0, 0, 0, + 1090, 1090, 0, 0, 1090, 1090, 1101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1123, 1124, 1125, 339, 0, 1126, + 0, 0, 0, 0, 0, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1090, 1090, 339, 339, 339, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1090, 1090, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 339, 339, 339, 0, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 0, 339, 339, + 339, 339, 339, 0, 0, 1111, 339, 1093, + 1093, 1093, 1090, 1090, 1090, 1090, 1090, 0, + 1090, 1090, 1093, 0, 1093, 1093, 1101, 0, + 0, 339, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 1090, 1090, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1090, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 339, 339, 0, 0, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 0, 339, 339, + 339, 339, 339, 0, 0, 1111, 339, 1127, + 1090, 1093, 1090, 1090, 1090, 0, 0, 0, + 1128, 1129, 0, 0, 1130, 1131, 1101, 0, + 0, 0, 0, 0, 0, 0, 0, 1132, + 1133, 0, 0, 0, 0, 1134, 1135, 0, + 339, 339, 339, 0, 0, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 812, 339, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1090, 339, 0, 339, 339, + 339, 339, 339, 339, 0, 0, 0, 339, + 339, 339, 0, 1136, 339, 1137, 339, 0, + 0, 0, 339, 339, 0, 339, 0, 339, + 339, 0, 0, 0, 339, 339, 0, 0, + 0, 339, 339, 339, 0, 0, 0, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 1138, + 1093, 1090, 1093, 1093, 0, 0, 0, 1139, + 1140, 1093, 0, 1141, 1142, 1143, 1101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1144, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1120, 1120, 1120, 76, 76, 76, 76, + 76, 76, 10, 76, 0, 0, 0, 0, + 0, 0, 1093, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 1090, + 1090, 1090, 1093, 1093, 1093, 1093, 0, 1145, + 1090, 1146, 0, 1090, 1090, 1090, 1101, 0, + 0, 0, 0, 0, 0, 0, 1147, 1148, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 0, 0, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 339, 339, + 339, 339, 339, 0, 0, 1111, 339, 1093, + 1149, 1150, 1093, 1151, 1093, 1093, 0, 1152, + 1153, 1154, 0, 1155, 1156, 1090, 1101, 0, + 0, 0, 0, 0, 0, 0, 1157, 1158, + 0, 0, 0, 0, 0, 0, 0, 339, + 0, 339, 339, 1090, 1090, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 0, 76, 76, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 1159, + 1093, 1093, 1090, 1090, 1090, 0, 0, 1160, + 1161, 1093, 0, 1162, 1163, 1164, 1101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1165, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 0, 0, 0, 0, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1093, 1093, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 1166, 0, 0, 0, 0, + 1167, 1093, 1093, 1090, 1090, 1090, 0, 1090, + 0, 1093, 1168, 1169, 1093, 1170, 1171, 1172, + 1173, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1093, 1093, 990, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 1090, 339, 1174, 1090, 1090, 1090, + 1090, 1175, 1175, 1101, 0, 0, 0, 0, + 10, 339, 339, 339, 339, 339, 339, 507, + 1090, 1176, 1176, 1176, 1176, 1090, 1090, 1090, + 990, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 990, 990, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 339, 339, 0, 339, 0, 0, + 339, 339, 0, 339, 0, 0, 339, 0, + 0, 0, 0, 0, 0, 339, 339, 339, + 339, 0, 339, 339, 339, 339, 339, 339, + 339, 0, 339, 339, 339, 0, 339, 0, + 339, 0, 0, 339, 339, 0, 339, 339, + 339, 339, 1090, 339, 1177, 1090, 1090, 1090, + 1090, 1178, 1178, 0, 1090, 1090, 339, 0, + 0, 339, 339, 339, 339, 339, 0, 507, + 0, 1179, 1179, 1179, 1179, 1090, 1090, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 0, 0, 1180, 1181, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 812, 812, 812, 990, 990, 990, + 990, 990, 990, 990, 990, 1182, 990, 990, + 990, 990, 990, 990, 812, 812, 812, 812, + 812, 537, 537, 812, 812, 812, 812, 812, + 812, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 812, 537, 812, + 537, 812, 1183, 11, 12, 11, 12, 1093, + 1093, 339, 339, 339, 1184, 339, 339, 339, + 339, 0, 339, 339, 339, 339, 1185, 339, + 339, 339, 339, 1186, 339, 339, 339, 339, + 1187, 339, 339, 339, 339, 1188, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 1189, 339, 0, 0, 0, 0, + 0, 0, 1190, 1191, 1192, 1193, 1194, 1195, + 1196, 1197, 1198, 1191, 1191, 1191, 1191, 1090, + 1093, 1191, 1199, 524, 524, 1101, 990, 524, + 524, 339, 339, 339, 339, 0, 0, 0, + 0, 1090, 1090, 1090, 1200, 1090, 1090, 1090, + 1090, 0, 1090, 1090, 1090, 1090, 1201, 1090, + 1090, 1090, 1090, 1202, 1090, 1090, 1090, 1090, + 1203, 1090, 1090, 1090, 1090, 1204, 1090, 1090, + 1090, 1090, 1090, 1090, 1090, 1090, 1090, 1090, + 1090, 1090, 1205, 1090, 1090, 1090, 0, 812, + 812, 812, 812, 812, 812, 812, 812, 537, + 812, 812, 812, 812, 812, 812, 0, 0, + 812, 990, 990, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 339, 339, 1206, 1207, + 339, 0, 339, 339, 0, 1093, 1090, 1208, + 1090, 1090, 1093, 1090, 0, 0, 0, 1090, + 1111, 1093, 1101, 0, 0, 0, 0, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 990, 990, 990, 990, 990, + 990, 339, 339, 339, 339, 339, 339, 1093, + 1093, 1090, 1090, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1209, 1210, 1211, 1212, 1213, 1214, 1215, + 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, + 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, + 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, + 1240, 1241, 1242, 1243, 1244, 1245, 1246, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 990, 1247, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 0, + 1248, 1248, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 0, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 0, 0, 0, 0, + 524, 812, 990, 990, 990, 990, 990, 990, + 990, 990, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 990, 990, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 11, 12, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 990, 990, 990, 1249, + 1249, 1249, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 339, + 339, 339, 339, 1090, 1090, 1101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 1090, 1090, 1101, 990, 990, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 1090, 1090, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 339, + 339, 339, 0, 1090, 1090, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 1250, 1250, 1093, + 1090, 1090, 1090, 1090, 1090, 1090, 1090, 1093, + 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1090, + 1093, 1093, 1090, 1090, 1090, 1090, 1090, 1090, + 1090, 1090, 1090, 1101, 1090, 990, 990, 990, + 507, 990, 990, 990, 10, 339, 524, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 0, 0, 0, 0, 0, + 0, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 0, 0, 0, 0, 0, + 0, 8, 8, 8, 8, 8, 8, 1030, + 8, 8, 8, 8, 558, 558, 558, 7, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 507, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 1032, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 0, 1090, 1090, 1090, 1093, 1093, 1093, 1093, + 1090, 1090, 1252, 1252, 1252, 0, 0, 0, + 0, 1093, 1093, 1090, 1093, 1093, 1093, 1093, + 1093, 1093, 1031, 524, 537, 0, 0, 0, + 0, 76, 0, 0, 0, 8, 8, 1110, + 1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 0, 0, 0, 0, 0, + 0, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + 1093, 1093, 339, 339, 339, 339, 339, 339, + 339, 1093, 1093, 0, 0, 0, 0, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 0, 0, 0, 0, 8, + 8, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 524, 537, 1093, 1093, 1093, 0, 0, 990, + 990, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1090, 1090, 1090, 1090, 1093, 1253, 1254, + 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, + 339, 339, 1263, 1264, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 1111, 1265, 1090, + 1090, 1090, 1090, 1266, 1267, 1268, 1269, 1270, + 1271, 1272, 1273, 1274, 1275, 1276, 339, 339, + 339, 339, 339, 339, 339, 0, 0, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 990, 990, 990, 990, 990, + 990, 990, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 524, 537, 524, 524, + 524, 524, 524, 524, 524, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 1277, 1278, 1279, + 507, 1280, 1281, 1282, 1283, 1284, 1285, 1286, + 1287, 1288, 1289, 1290, 507, 1291, 1292, 1293, + 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, + 1302, 1303, 1304, 1305, 1306, 1307, 1308, 507, + 1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316, + 1317, 1318, 1319, 1320, 1321, 1322, 1323, 1324, + 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, + 1333, 1334, 1335, 1336, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 1337, 213, 213, 213, 213, 1338, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 1339, 1340, 1341, 1342, + 1307, 1343, 1344, 1345, 1346, 1347, 1348, 1349, + 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, + 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, + 1366, 1367, 1368, 1369, 1370, 1371, 1372, 1373, + 1374, 524, 524, 537, 524, 524, 524, 524, + 524, 524, 524, 537, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 524, + 537, 1375, 1376, 1377, 1378, 1379, 1380, 1381, + 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, + 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, + 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, + 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, + 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, + 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, + 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1437, + 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, + 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, + 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, + 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, + 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, + 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, + 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, + 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, + 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, + 1518, 1519, 1520, 1521, 1522, 1523, 1524, 1525, + 1526, 1527, 1528, 1529, 1530, 0, 0, 0, + 0, 1531, 1532, 1533, 1534, 1535, 1536, 1537, + 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, + 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, + 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, + 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, + 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, + 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, + 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, + 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, + 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, + 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, + 1618, 1619, 1620, 0, 0, 0, 0, 0, + 0, 1621, 1622, 1623, 1624, 1625, 1626, 1627, + 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, + 1636, 1637, 1638, 1639, 1640, 1641, 1642, 0, + 0, 1643, 1644, 1645, 1646, 1647, 1648, 0, + 0, 1649, 1650, 1651, 1652, 1653, 1654, 1655, + 1656, 1657, 1658, 1659, 1660, 1661, 1662, 1663, + 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, + 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, + 1680, 1681, 1682, 1683, 1684, 1685, 1686, 0, + 0, 1687, 1688, 1689, 1690, 1691, 1692, 0, + 0, 1693, 1694, 1695, 1696, 1697, 1698, 1699, + 1700, 0, 1701, 0, 1702, 0, 1703, 0, + 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, + 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1719, + 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, + 1728, 1729, 1730, 1731, 1732, 1733, 1734, 0, + 0, 1735, 1736, 1737, 1738, 1739, 1740, 1741, + 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749, + 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, + 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, + 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, + 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, + 1782, 1783, 1784, 1785, 1786, 1787, 0, 1788, + 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, + 1797, 1798, 1799, 1800, 1801, 1802, 0, 1803, + 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, + 1812, 1813, 1814, 1815, 1816, 0, 0, 1817, + 1818, 1819, 1820, 1821, 1822, 0, 1823, 1824, + 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, + 1833, 1834, 1835, 1836, 1837, 1838, 1839, 1840, + 1841, 0, 0, 1842, 1843, 1844, 0, 1845, + 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, + 0, 1854, 1855, 1856, 1856, 1856, 1856, 1856, + 1857, 1856, 1856, 1856, 80, 1858, 1858, 1250, + 1859, 1030, 1860, 1030, 1030, 1030, 1030, 8, + 1861, 79, 91, 11, 79, 79, 91, 11, + 79, 8, 8, 8, 8, 1862, 1863, 1864, + 8, 1865, 1866, 1867, 1868, 1869, 1870, 1871, + 75, 9, 9, 9, 1872, 1873, 8, 1874, + 1875, 8, 79, 91, 8, 1876, 8, 1877, + 47, 47, 8, 8, 8, 1878, 11, 12, + 1879, 1880, 1881, 8, 8, 8, 8, 8, + 8, 8, 8, 74, 8, 47, 8, 8, + 1882, 8, 8, 8, 8, 8, 8, 8, + 1856, 80, 80, 80, 80, 0, 0, 0, + 0, 0, 0, 80, 80, 80, 80, 80, + 80, 1883, 1884, 0, 0, 1885, 1886, 1887, + 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, + 1896, 1897, 1898, 1899, 1900, 1901, 1902, 1903, + 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, + 0, 1912, 1913, 1914, 1915, 1916, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 10, 10, 10, 10, 10, 10, + 10, 1917, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 524, 524, 550, 550, 524, 524, 524, + 524, 550, 550, 550, 524, 524, 813, 813, + 813, 813, 524, 813, 813, 813, 550, 550, + 524, 537, 524, 550, 550, 537, 537, 537, + 537, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1918, 1919, 1920, 1921, 76, 1922, 1923, + 1924, 76, 1925, 1926, 1927, 1927, 1927, 1928, + 1929, 1930, 1930, 1931, 1932, 76, 1933, 1934, + 76, 76, 1935, 1936, 1937, 1937, 1937, 76, + 76, 1938, 1939, 1940, 76, 1941, 76, 1942, + 76, 1941, 76, 1943, 1944, 1945, 1920, 82, + 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, + 1954, 1955, 1956, 76, 1957, 1958, 1959, 1960, + 1961, 1962, 74, 74, 74, 74, 1963, 1964, + 1946, 1956, 1965, 76, 74, 76, 76, 1966, + 0, 0, 0, 0, 1967, 1968, 1969, 1970, + 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, + 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, + 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 1249, 1249, 1249, 2012, 2013, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2014, 74, 2015, 74, 2016, 76, 76, + 76, 76, 76, 2017, 2018, 76, 76, 76, + 76, 74, 76, 76, 74, 76, 76, 74, + 76, 76, 76, 76, 76, 76, 76, 2019, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 2020, 2021, + 2022, 2023, 76, 2024, 76, 2025, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 2026, 2026, 2027, 2028, 74, 74, + 74, 2029, 2030, 2026, 2031, 2032, 2026, 74, + 74, 74, 2026, 13, 83, 74, 2026, 2026, + 74, 74, 74, 2026, 2026, 2026, 2026, 74, + 2026, 2026, 2026, 2026, 2033, 2034, 2035, 2036, + 74, 74, 74, 74, 2026, 2037, 2038, 2026, + 2039, 2040, 2026, 2026, 2026, 74, 74, 74, + 74, 74, 2026, 74, 2026, 2041, 2026, 2026, + 2026, 2026, 2042, 2026, 2043, 2044, 2045, 2026, + 2046, 2047, 2048, 2026, 2026, 2026, 2049, 74, + 74, 74, 74, 2026, 2026, 2026, 2026, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 2026, 2050, 2051, 2052, 74, 2053, 2054, 2026, + 2026, 2026, 2026, 2026, 2026, 74, 2055, 2056, + 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, + 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2026, + 2026, 2072, 2073, 2074, 2075, 2076, 2077, 2078, + 2079, 2080, 2081, 2026, 2026, 2026, 74, 74, + 2026, 2026, 2082, 2083, 74, 74, 74, 74, + 74, 2026, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 2084, 2026, 74, 74, 2026, + 2026, 2085, 2086, 2026, 2087, 2088, 2089, 2090, + 2091, 2026, 2026, 2092, 2093, 2094, 2095, 2026, + 2026, 2026, 74, 74, 74, 74, 74, 2026, + 2026, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 2026, 2026, 2026, 2026, 2026, 74, + 74, 2026, 2026, 74, 74, 74, 74, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2096, 2097, 2098, 2099, 2026, 2026, 2026, + 2026, 2026, 2026, 2100, 2101, 2102, 2103, 74, + 74, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 76, 76, 76, 76, 76, 76, 76, + 76, 2026, 2026, 2026, 2026, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 2026, 2026, 76, 76, 76, 76, 76, + 76, 76, 2104, 2105, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 76, 74, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 812, 76, + 76, 76, 76, 76, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 74, 74, 74, + 74, 74, 74, 76, 76, 76, 76, 76, + 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2106, 2107, 2108, 2109, 2110, 2111, 2112, + 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, + 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, + 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, + 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, + 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, + 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, + 2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, + 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, + 2177, 2178, 2179, 2180, 2181, 2182, 2183, 2184, + 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, + 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, + 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, + 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, + 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, + 2225, 2226, 2227, 2228, 2229, 2230, 2231, 2232, + 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, + 2241, 2242, 2243, 2244, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 74, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 74, 74, 74, 74, 74, 74, 74, + 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 812, 76, 76, + 76, 76, 76, 76, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 76, 76, 76, 76, 0, 76, + 76, 76, 76, 0, 0, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 0, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 0, 76, 0, + 76, 76, 76, 76, 0, 0, 0, 76, + 0, 76, 76, 76, 76, 76, 76, 76, + 0, 0, 76, 76, 76, 76, 76, 76, + 76, 11, 12, 11, 12, 11, 12, 11, + 12, 11, 12, 11, 12, 11, 12, 1251, + 1251, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 1251, 1251, 1251, 1251, 1251, + 1251, 1251, 1251, 1251, 1251, 76, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 0, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 0, 2026, 74, 74, 2026, 2026, 11, 12, + 74, 74, 74, 74, 0, 0, 0, 0, + 0, 74, 74, 74, 2026, 2026, 2026, 2026, + 74, 74, 74, 74, 74, 2026, 2026, 2026, + 74, 74, 74, 2026, 2026, 2026, 2026, 11, + 12, 11, 12, 11, 12, 0, 0, 0, + 0, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 11, 12, 11, 12, + 11, 12, 11, 12, 11, 12, 11, 12, + 11, 12, 11, 12, 11, 12, 11, 12, + 11, 12, 74, 74, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 74, 74, 74, 74, 74, 74, 74, + 74, 2026, 74, 74, 74, 74, 74, 74, + 74, 2026, 2026, 2026, 2026, 2026, 2026, 74, + 74, 74, 2026, 74, 74, 74, 74, 2026, + 2026, 2026, 2026, 2026, 74, 2026, 2026, 74, + 74, 11, 12, 11, 12, 2026, 74, 74, + 74, 74, 2026, 74, 2026, 2026, 2026, 74, + 74, 2026, 2026, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 2026, 2026, 2026, + 2026, 2026, 2026, 74, 74, 11, 12, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 2026, 2026, 2245, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 74, 2026, + 2026, 2026, 2026, 74, 74, 2026, 74, 2026, + 74, 74, 2026, 74, 2026, 2026, 2026, 2026, + 74, 74, 74, 74, 74, 2026, 2026, 74, + 74, 74, 74, 74, 74, 2026, 2026, 2026, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 2026, 2026, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 2026, 2026, 74, + 74, 74, 74, 2026, 2026, 2026, 2026, 74, + 2026, 2026, 74, 74, 2026, 2246, 2247, 2248, + 74, 74, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 74, 74, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 74, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, + 74, 74, 74, 74, 74, 2249, 2250, 2026, + 74, 74, 74, 2026, 2026, 2026, 2026, 2026, + 74, 74, 74, 74, 74, 2026, 2026, 2026, + 74, 74, 74, 74, 2026, 74, 74, 74, + 2026, 2026, 2026, 2026, 2026, 74, 2026, 74, + 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2251, 2252, 2253, 2254, 2255, 2256, 2257, + 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, + 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, + 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, + 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, + 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, + 0, 2298, 2299, 2300, 2301, 2302, 2303, 2304, + 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, + 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320, + 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, + 2329, 2330, 2331, 2332, 2333, 2334, 2335, 2336, + 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, + 0, 2345, 2346, 2347, 2348, 2349, 2350, 2351, + 2352, 2353, 2354, 2355, 2356, 2357, 0, 0, + 0, 0, 0, 0, 0, 213, 2358, 2359, + 213, 0, 0, 0, 0, 0, 0, 0, + 0, 2360, 2361, 2362, 2363, 2364, 2365, 2366, + 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, + 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, + 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, + 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, + 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, + 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, + 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, + 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, + 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, + 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, + 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, + 2455, 2456, 2457, 2458, 2459, 213, 76, 76, + 76, 76, 76, 76, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 8, 8, 8, 1251, 8, + 8, 2460, 2461, 2462, 2463, 2464, 2465, 2466, + 2467, 2468, 2469, 2470, 2471, 2472, 2473, 2474, + 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, + 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, + 2491, 2492, 2493, 2494, 2495, 2496, 2497, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2498, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 8, 79, 91, 79, 91, 8, + 8, 8, 79, 91, 8, 79, 91, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1030, 0, 0, 0, 0, 79, 91, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 0, 76, 76, 76, 76, + 2499, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 2500, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2501, 2502, 2503, 2504, 2505, 2506, 2507, + 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, + 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, + 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, + 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, + 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, + 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, + 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, + 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, + 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, + 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2587, + 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, + 2596, 2597, 2598, 2599, 2600, 2601, 2602, 2603, + 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, + 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, + 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, + 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, + 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, + 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, + 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, + 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, + 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, + 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2683, + 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, + 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, + 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, + 2708, 2709, 2710, 2711, 2712, 2713, 2714, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 0, 0, 0, + 0, 2715, 8, 8, 8, 76, 507, 339, + 1249, 11, 12, 11, 12, 11, 12, 11, + 12, 11, 12, 76, 76, 11, 12, 11, + 12, 11, 12, 11, 12, 1030, 11, 12, + 12, 76, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 2716, 1032, 536, 1031, 2717, + 2717, 1030, 507, 507, 507, 507, 507, 2718, + 76, 2719, 2720, 2721, 507, 339, 8, 76, + 76, 0, 339, 339, 339, 339, 339, 2722, + 339, 339, 339, 339, 2723, 2724, 2725, 2726, + 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, + 2735, 2736, 2737, 2738, 2739, 2740, 2741, 2742, + 2743, 2744, 2745, 2746, 339, 2747, 2748, 2749, + 2750, 2751, 2752, 339, 339, 339, 339, 339, + 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, + 2761, 2762, 2763, 2764, 2765, 2766, 2767, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 2768, 339, 339, + 0, 0, 2769, 2770, 2771, 2772, 2773, 2774, + 2775, 1030, 339, 339, 339, 339, 339, 2776, + 339, 339, 339, 339, 2777, 2778, 2779, 2780, + 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, + 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, + 2797, 2798, 2799, 2800, 339, 2801, 2802, 2803, + 2804, 2805, 2806, 339, 339, 339, 339, 339, + 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, + 2815, 2816, 2817, 2818, 2819, 2820, 2821, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 2822, 2823, 2824, 2825, 339, 2826, 339, 339, + 2827, 2828, 2829, 2830, 8, 507, 2831, 2832, + 2833, 0, 0, 0, 0, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 0, 0, 2834, 2835, 2836, 2837, 2838, 2839, + 2840, 2841, 2842, 2843, 2844, 2845, 2846, 2847, + 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, + 2856, 2857, 2858, 2859, 2860, 2861, 2862, 2863, + 2864, 2865, 2866, 2867, 2868, 2869, 2870, 2871, + 2872, 2873, 2874, 2875, 2876, 2877, 2878, 2879, + 2880, 2881, 2882, 2883, 2884, 2885, 2886, 2887, + 2888, 2889, 2890, 2891, 2892, 2893, 2894, 2895, + 2896, 2897, 2898, 2899, 2900, 2901, 2902, 2903, + 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, + 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, + 2920, 2921, 2922, 2923, 2924, 2925, 2926, 2927, + 0, 812, 812, 2928, 2929, 2930, 2931, 2932, + 2933, 2934, 2935, 2936, 2937, 2938, 2939, 2940, + 2941, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 2942, 2943, 2944, 2945, 2946, 2947, 2948, + 2949, 2950, 2951, 2952, 2953, 2954, 2955, 2956, + 2957, 2958, 2959, 2960, 2961, 2962, 2963, 2964, + 2965, 2966, 2967, 2968, 2969, 2970, 2971, 2972, + 0, 2973, 2974, 2975, 2976, 2977, 2978, 2979, + 2980, 2981, 2982, 2983, 2984, 2985, 2986, 2987, + 2988, 2989, 2990, 2991, 2992, 2993, 2994, 2995, + 2996, 2997, 2998, 2999, 3000, 3001, 3002, 3003, + 3004, 3005, 3006, 3007, 3008, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3009, 3010, 3011, 3012, 3013, 3014, 3015, + 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, + 3024, 3025, 3026, 3027, 3028, 3029, 3030, 3031, + 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, + 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, + 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, + 812, 3056, 3057, 3058, 3059, 3060, 3061, 3062, + 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, + 3071, 3072, 3073, 3074, 3075, 3076, 3077, 3078, + 3079, 3080, 3081, 3082, 3083, 3084, 3085, 3086, + 3087, 3088, 3089, 3090, 3091, 3092, 3093, 3094, + 3095, 3096, 3097, 3098, 3099, 3100, 3101, 3102, + 3103, 3104, 3105, 3106, 3107, 3108, 3109, 3110, + 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, + 3119, 3120, 3121, 3122, 3123, 3124, 3125, 3126, + 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3134, + 3135, 3136, 3137, 3138, 3139, 3140, 3141, 3142, + 3143, 3144, 3145, 3146, 3147, 3148, 3149, 3150, + 3151, 3152, 3153, 3154, 3155, 3156, 3157, 3158, + 3159, 3160, 3161, 3162, 3163, 3164, 3165, 3166, + 3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, + 3175, 3176, 3177, 3178, 3179, 3180, 3181, 3182, + 0, 3183, 3184, 3185, 3186, 3187, 3188, 3189, + 3190, 3191, 3192, 3193, 3194, 3195, 3196, 3197, + 3198, 3199, 3200, 3201, 3202, 3203, 3204, 3205, + 3206, 3207, 3208, 3209, 3210, 3211, 3212, 3213, + 3214, 3215, 3216, 3217, 3218, 3219, 3220, 3221, + 3222, 3223, 3224, 3225, 3226, 3227, 3228, 3229, + 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, + 3238, 3239, 3240, 3241, 3242, 3243, 3244, 3245, + 3246, 3247, 3248, 3249, 3250, 3251, 3252, 3253, + 3254, 3255, 3256, 3257, 3258, 3259, 3260, 3261, + 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269, + 3270, 3271, 3272, 3273, 3274, 3275, 3276, 3277, + 3278, 3279, 3280, 3281, 3282, 3283, 3284, 3285, + 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, + 3294, 3295, 3296, 3297, 3298, 3299, 3300, 3301, + 3302, 3303, 3304, 3305, 3306, 3307, 3308, 3309, + 3310, 3311, 3312, 3313, 3314, 3315, 3316, 3317, + 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, + 3326, 3327, 3328, 3329, 3330, 3331, 3332, 3333, + 3334, 3335, 3336, 3337, 3338, 3339, 3340, 3341, + 3342, 3343, 3344, 3345, 3346, 3347, 3348, 3349, + 3350, 3351, 3352, 3353, 3354, 3355, 3356, 3357, + 3358, 3359, 3360, 3361, 3362, 3363, 3364, 3365, + 3366, 3367, 3368, 3369, 3370, 3371, 3372, 3373, + 3374, 3375, 3376, 3377, 3378, 3379, 3380, 3381, + 3382, 3383, 3384, 3385, 3386, 3387, 3388, 3389, + 3390, 3391, 3392, 3393, 3394, 3395, 3396, 3397, + 3398, 3399, 3400, 3401, 3402, 3403, 3404, 3405, + 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413, + 3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, + 3422, 3423, 3424, 3425, 3426, 3427, 3428, 3429, + 3430, 3431, 3432, 3433, 3434, 3435, 3436, 3437, + 3438, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 507, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 506, 506, 506, 506, 0, 0, 0, 0, + 0, 46, 46, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 1252, 339, 339, 339, 1101, + 339, 339, 339, 339, 1090, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 1093, 1093, 1090, 1090, + 1093, 76, 76, 76, 76, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 8, 8, 8, + 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3439, 3439, 3439, 3439, 3439, 3439, 3439, + 3439, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3441, 3442, 3443, 3444, 3445, 3446, 3447, + 3448, 3448, 3449, 3450, 3451, 3452, 3453, 3454, + 3455, 3456, 3457, 3458, 3459, 3460, 3461, 3462, + 3463, 3464, 3465, 3466, 3467, 3468, 3469, 3470, + 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3478, + 3479, 3480, 3481, 3482, 3483, 3484, 3485, 3486, + 3487, 3488, 3489, 3490, 3491, 3492, 3493, 3494, + 3495, 3496, 3497, 3498, 3499, 3500, 3501, 3502, + 3503, 3504, 3505, 3506, 3507, 3508, 3509, 3510, + 3511, 3512, 3513, 3514, 3515, 3516, 3517, 3518, + 3519, 3520, 3521, 3522, 3523, 3524, 3525, 3526, + 3527, 3528, 3529, 3530, 3531, 3460, 3532, 3533, + 3534, 3535, 3536, 3537, 3538, 3539, 3540, 3541, + 3542, 3543, 3544, 3545, 3546, 3547, 3548, 3549, + 3550, 3551, 3552, 3553, 3554, 3555, 3556, 3557, + 3558, 3559, 3560, 3561, 3562, 3563, 3564, 3565, + 3566, 3567, 3568, 3569, 3570, 3571, 3572, 3573, + 3574, 3575, 3576, 3577, 3578, 3579, 3580, 3581, + 3582, 3583, 3584, 3585, 3586, 3587, 3588, 3589, + 3590, 3591, 3592, 3593, 3594, 3595, 3596, 3597, + 3598, 3599, 3550, 3600, 3601, 3602, 3603, 3604, + 3605, 3606, 3607, 3534, 3608, 3609, 3610, 3611, + 3612, 3613, 3614, 3615, 3616, 3617, 3618, 3619, + 3620, 3621, 3622, 3623, 3624, 3625, 3626, 3627, + 3460, 3628, 3629, 3630, 3631, 3632, 3633, 3634, + 3635, 3636, 3637, 3638, 3639, 3640, 3641, 3642, + 3643, 3644, 3645, 3646, 3647, 3648, 3649, 3650, + 3651, 3652, 3653, 3654, 3536, 3655, 3656, 3657, + 3658, 3659, 3660, 3661, 3662, 3663, 3664, 3665, + 3666, 3667, 3668, 3669, 3670, 3671, 3672, 3673, + 3674, 3675, 3676, 3677, 3678, 3679, 3680, 3681, + 3682, 3683, 3684, 3685, 3686, 3687, 3688, 3689, + 3690, 3691, 3692, 3693, 3694, 3695, 3696, 3697, + 3698, 3699, 3700, 3701, 3702, 3703, 3704, 339, + 339, 3705, 339, 3706, 339, 339, 3707, 3708, + 3709, 3710, 3711, 3712, 3713, 3714, 3715, 3716, + 339, 3717, 339, 3718, 339, 339, 3719, 3720, + 339, 339, 339, 3721, 3722, 3723, 3724, 0, + 0, 3725, 3726, 3727, 3728, 3729, 3730, 3731, + 3732, 3733, 3734, 3735, 3736, 3737, 3738, 3739, + 3740, 3741, 3742, 3743, 3744, 3745, 3746, 3747, + 3748, 3749, 3750, 3751, 3752, 3753, 3754, 3755, + 3756, 3757, 3758, 3759, 3760, 3761, 3762, 3763, + 3589, 3764, 3765, 3766, 3767, 3768, 3769, 3769, + 3770, 3771, 3772, 3773, 3774, 3775, 3776, 3777, + 3719, 3778, 3779, 3780, 0, 0, 0, 0, + 0, 3781, 3782, 3783, 3784, 3785, 3786, 3787, + 3788, 3731, 3789, 3790, 3791, 3705, 3792, 3793, + 3794, 3795, 3796, 3797, 3798, 3799, 3800, 3801, + 3802, 3803, 3740, 3804, 3741, 3805, 3806, 3807, + 3808, 3809, 3706, 3481, 3810, 3811, 3812, 3551, + 3638, 3813, 3814, 3748, 3815, 3749, 3816, 3817, + 3818, 3708, 3819, 3820, 3821, 3822, 3823, 3709, + 3824, 3825, 3826, 3827, 3828, 3829, 3763, 3830, + 3831, 3589, 3832, 3767, 3833, 3834, 3835, 3836, + 3837, 3772, 3838, 3718, 3839, 3773, 3532, 3840, + 3774, 3841, 3776, 3842, 3843, 3844, 3845, 3846, + 3778, 3714, 3847, 3779, 3848, 3780, 3849, 3448, + 3850, 3851, 3852, 3853, 3854, 3855, 3856, 3857, + 3858, 3859, 3860, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3861, 3862, 3863, 3864, 3865, 3866, 3867, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3868, 3869, 3870, 3871, + 3872, 0, 0, 0, 0, 0, 3873, 3874, + 3875, 3876, 3877, 3878, 3879, 3880, 3881, 3882, + 3883, 3884, 3885, 3886, 3887, 3888, 3889, 3890, + 3891, 3892, 3893, 3894, 3895, 3896, 3897, 3898, + 0, 3899, 3900, 3901, 3902, 3903, 0, 3904, + 0, 3905, 3906, 0, 3907, 3908, 0, 3909, + 3910, 3911, 3912, 3913, 3914, 3915, 3916, 3917, + 3918, 3919, 3920, 3921, 3922, 3923, 3924, 3925, + 3926, 3927, 3928, 3929, 3930, 3931, 3932, 3933, + 3934, 3935, 3936, 3937, 3938, 3939, 3940, 3941, + 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949, + 3950, 3951, 3952, 3953, 3954, 3955, 3956, 3957, + 3958, 3959, 3960, 3961, 3962, 3963, 3964, 3965, + 3966, 3967, 3968, 3969, 3970, 3971, 3972, 3973, + 3974, 3975, 3976, 3977, 3978, 3979, 3980, 3981, + 3982, 3983, 3984, 3985, 3986, 3987, 3988, 3989, + 3990, 3991, 3992, 3993, 3994, 3995, 3996, 3997, + 3998, 3999, 4000, 4001, 4002, 4003, 4004, 4005, + 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, + 4014, 4015, 4016, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4017, 4018, 4019, 4020, + 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, + 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, + 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, + 4045, 4046, 4047, 4048, 4049, 4050, 4051, 4052, + 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, + 4061, 4062, 4063, 4064, 4055, 4065, 4066, 4067, + 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, + 4076, 4077, 4078, 4079, 4080, 4081, 4082, 4083, + 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, + 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, + 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, + 4108, 4109, 4110, 4111, 4112, 4113, 4114, 4115, + 4116, 4117, 4118, 4119, 4120, 4121, 4122, 4123, + 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, + 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, + 4140, 4141, 4142, 4143, 4144, 4145, 4146, 4147, + 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, + 4156, 4157, 4158, 4159, 4160, 4161, 4162, 4163, + 4164, 4056, 4165, 4166, 4167, 4168, 4169, 4170, + 4171, 4172, 4173, 4174, 4175, 4176, 4177, 4178, + 4179, 4180, 4181, 4182, 4183, 4184, 4185, 4186, + 4187, 4188, 4189, 4190, 4191, 4192, 4193, 4194, + 4195, 4196, 4197, 4198, 4199, 4200, 4201, 4202, + 4203, 4204, 4205, 4206, 4207, 4208, 4209, 4210, + 4211, 4212, 4213, 4214, 4215, 4216, 4217, 4218, + 4219, 4220, 4221, 4222, 4223, 4224, 4225, 4226, + 4227, 4228, 4229, 4230, 4231, 4232, 4233, 4234, + 4235, 4236, 4237, 4238, 4239, 4240, 4241, 4242, + 4243, 4244, 4245, 4246, 4247, 4248, 4249, 4250, + 4251, 4252, 4253, 4254, 4255, 4256, 4257, 4258, + 4259, 4260, 4261, 4262, 4263, 4264, 4265, 4266, + 4267, 4268, 4269, 4270, 4271, 4272, 4273, 4274, + 4275, 4276, 4277, 4278, 4279, 4280, 4281, 4282, + 4283, 4284, 4285, 4286, 4287, 4288, 4289, 4290, + 4291, 4292, 4293, 4294, 4295, 4296, 4297, 4298, + 4299, 4300, 4301, 4302, 4303, 4304, 4305, 4306, + 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, + 4315, 4316, 4317, 4318, 4319, 4320, 4321, 4322, + 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, + 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, + 4339, 4340, 4341, 4342, 4343, 4344, 4345, 4346, + 4347, 4348, 4349, 4350, 4351, 4352, 4353, 4354, + 4355, 4356, 4357, 4358, 4359, 4360, 4361, 4362, + 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, + 4371, 4372, 4373, 4374, 4375, 4376, 4377, 4378, + 4379, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4380, 4381, 4382, 4383, 4384, 4385, 4386, + 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, + 4395, 4396, 4397, 4398, 4399, 4400, 4401, 4402, + 4403, 4404, 4405, 4406, 4407, 4408, 4409, 4410, + 4411, 4412, 4413, 4414, 4415, 4416, 4417, 4418, + 4419, 4420, 4421, 4422, 4423, 4424, 4425, 4426, + 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, + 4435, 4436, 4437, 4438, 4439, 4440, 4441, 4442, + 4443, 0, 0, 4444, 4445, 4446, 4447, 4448, + 4449, 4450, 4451, 4452, 4453, 4454, 4455, 4456, + 4457, 4458, 4459, 4460, 4461, 4462, 4463, 4464, + 4465, 4466, 4467, 4468, 4469, 4470, 4471, 4472, + 4473, 4474, 4475, 4476, 4477, 4478, 4479, 4480, + 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, + 4489, 4490, 4491, 4492, 4493, 4494, 4495, 4496, + 4497, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4498, 4499, 4500, 4501, 4502, 4503, 4504, + 4505, 4506, 4507, 4508, 4509, 4510, 76, 0, + 0, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 4511, 4512, 4513, 4514, 4515, 4516, 4517, + 4518, 4519, 4520, 0, 0, 0, 0, 0, + 0, 524, 524, 524, 524, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4521, 4522, 4523, 4524, 4524, 4525, 4526, + 4527, 4528, 4529, 4530, 4531, 4532, 4533, 4534, + 4535, 4536, 4537, 4538, 4539, 4540, 8, 8, + 4541, 4542, 4543, 4543, 4543, 4543, 4544, 4544, + 4544, 4545, 4546, 4547, 0, 4548, 4549, 4550, + 4551, 4552, 4553, 4554, 4555, 4556, 4557, 4558, + 4559, 4560, 4561, 4562, 4563, 4564, 4565, 4566, + 0, 4567, 4568, 4569, 4570, 0, 0, 0, + 0, 4571, 4572, 4573, 1054, 4574, 0, 4575, + 4576, 4577, 4578, 4579, 4580, 4581, 4582, 4583, + 4584, 4585, 4586, 4587, 4588, 4589, 4590, 4591, + 4592, 4593, 4594, 4595, 4596, 4597, 4598, 4599, + 4600, 4601, 4602, 4603, 4604, 4605, 4606, 4607, + 4608, 4609, 4610, 4611, 4612, 4613, 4614, 4615, + 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, + 4624, 4625, 4626, 4627, 4628, 4629, 4630, 4631, + 4632, 4633, 4634, 4635, 4636, 4637, 4638, 4639, + 4640, 4641, 4642, 4643, 4644, 4645, 4646, 4647, + 4648, 4649, 4650, 4651, 4652, 4653, 4654, 4655, + 4656, 4657, 4658, 4659, 4660, 4661, 4662, 4663, + 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, + 4672, 4673, 4674, 4675, 4676, 4677, 4678, 4679, + 4680, 4681, 4682, 4683, 4684, 4685, 4686, 4687, + 4688, 4689, 4690, 4691, 4692, 4693, 4694, 4695, + 4696, 4697, 4698, 4699, 4700, 4701, 4702, 4703, + 4704, 4705, 4706, 4707, 4708, 4709, 0, 0, + 80, 0, 4710, 4711, 4712, 4713, 4714, 4715, + 4716, 4717, 4718, 4719, 4720, 4721, 4722, 4723, + 4724, 4725, 4726, 4727, 4728, 4729, 4730, 4731, + 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, + 4740, 4741, 4742, 4743, 4744, 4745, 4746, 4747, + 4748, 4749, 4750, 4751, 4752, 4753, 4754, 4755, + 4756, 4757, 4758, 4759, 4760, 4761, 4762, 4763, + 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, + 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, + 4780, 4781, 4782, 4783, 4784, 4785, 4786, 4787, + 4788, 4789, 4790, 4791, 4792, 4793, 4794, 4795, + 4796, 4797, 4798, 4799, 4800, 4801, 4802, 4803, + 4804, 4805, 4806, 4807, 4808, 4809, 4810, 4811, + 4812, 4813, 4814, 4815, 4816, 4817, 4818, 4819, + 4820, 4821, 4822, 4823, 4824, 4825, 4826, 4827, + 4828, 4829, 4830, 4831, 4832, 4833, 4834, 4835, + 4836, 4837, 4838, 4839, 4840, 4841, 4842, 4843, + 4844, 4845, 4846, 4847, 4848, 4849, 4850, 4851, + 4852, 4853, 4854, 4855, 4856, 4857, 4858, 4859, + 4860, 4861, 4862, 4863, 4864, 4865, 4866, 4867, + 4868, 4869, 4870, 4871, 4872, 4873, 4874, 4875, + 4876, 4877, 4878, 4879, 4880, 4881, 4882, 4883, + 4884, 4885, 4886, 4887, 4888, 4889, 4890, 4891, + 4892, 4893, 4894, 4895, 4896, 4897, 4898, 4899, + 0, 0, 0, 4900, 4901, 4902, 4903, 4904, + 4905, 0, 0, 4906, 4907, 4908, 4909, 4910, + 4911, 0, 0, 4912, 4913, 4914, 4915, 4916, + 4917, 0, 0, 4918, 4919, 4920, 0, 0, + 0, 4921, 4922, 4923, 4924, 4925, 4926, 4927, + 0, 4928, 4929, 4930, 4931, 4932, 4933, 4934, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4935, 4935, 4935, 76, 76, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 0, 339, 339, 0, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 0, 0, 0, 0, + 0, 990, 8, 812, 0, 0, 0, 0, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 0, 0, 0, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 4936, 4936, + 4936, 4936, 4936, 4936, 4936, 4936, 1251, 1251, + 1251, 1251, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 1251, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 1120, 1120, 1120, 1120, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 1249, 339, 339, 339, 339, 339, + 339, 339, 339, 1249, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 990, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 990, 1249, 1249, 1249, 1249, 1249, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4937, 4938, 4939, 4940, 4941, 4942, 4943, + 4944, 4945, 4946, 4947, 4948, 4949, 4950, 4951, + 4952, 4953, 4954, 4955, 4956, 4957, 4958, 4959, + 4960, 4961, 4962, 4963, 4964, 4965, 4966, 4967, + 4968, 4969, 4970, 4971, 4972, 4973, 4974, 4975, + 4976, 4977, 4978, 4979, 4980, 4981, 4982, 4983, + 4984, 4985, 4986, 4987, 4988, 4989, 4990, 4991, + 4992, 4993, 4994, 4995, 4996, 4997, 4998, 4999, + 5000, 5001, 5002, 5003, 5004, 5005, 5006, 5007, + 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, + 5016, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 0, + 0, 1110, 1110, 1110, 1110, 1110, 1110, 1110, + 1110, 1110, 1110, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 1050, 1050, 1050, 1050, 1050, 0, + 0, 1050, 0, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 0, + 1050, 1050, 0, 0, 0, 1050, 0, 0, + 1050, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 5017, + 5017, 5017, 5017, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 1090, 1090, 1090, 0, 1090, 1090, + 0, 0, 0, 0, 0, 1090, 537, 1090, + 524, 1050, 1050, 1050, 1050, 0, 1050, 1050, + 1050, 0, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1050, 1050, 1050, 1050, 0, 0, 0, + 0, 524, 550, 537, 0, 0, 0, 0, + 1101, 5017, 5017, 5017, 5017, 5017, 5017, 5017, + 5017, 0, 0, 0, 0, 0, 0, 0, + 0, 1046, 1046, 1046, 1046, 1046, 1046, 1046, + 1046, 1046, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 1249, 1249, 1249, 1249, + 1249, 1249, 1249, 1249, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 990, 990, 990, 990, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 0, 0, 0, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 5018, 5019, 812, 812, 812, 812, 812, 5020, + 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, + 550, 550, 550, 812, 812, 812, 5029, 5030, + 5031, 5032, 5033, 5034, 80, 80, 80, 80, + 80, 80, 80, 80, 537, 537, 537, 537, + 537, 537, 537, 537, 812, 812, 524, 524, + 524, 524, 524, 537, 537, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 524, 524, 524, 524, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 5035, 5036, 5037, 5038, 5039, 5040, + 5041, 5042, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 812, 812, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 524, 524, 524, 76, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, + 1120, 1120, 1120, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 1945, 1920, + 1963, 1947, 1948, 5044, 1927, 1930, 5045, 5046, + 1931, 1950, 1933, 5047, 1935, 1936, 1937, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 1941, 5055, + 5056, 5057, 1964, 1946, 5058, 1926, 0, 1956, + 1965, 5059, 1932, 5060, 5061, 1951, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 0, 1920, + 1963, 0, 0, 5044, 0, 0, 5045, 5046, + 0, 0, 1933, 5047, 1935, 1936, 0, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 1941, 5055, + 5056, 5057, 1964, 0, 5058, 0, 1928, 1956, + 1965, 5059, 1932, 5060, 5061, 0, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 1945, 0, + 1963, 1947, 1948, 5044, 0, 0, 5045, 5046, + 1931, 1950, 1933, 5047, 1935, 1936, 0, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 0, 5055, + 5056, 5057, 1964, 1946, 5058, 1926, 1928, 1956, + 1965, 5059, 1932, 5060, 5061, 1951, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 0, 1963, 1947, 1948, 5044, + 0, 1930, 5045, 5046, 1931, 1950, 0, 5047, + 0, 0, 0, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 0, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 1945, 1920, + 1963, 1947, 1948, 5044, 1927, 1930, 5045, 5046, + 1931, 1950, 1933, 5047, 1935, 1936, 1937, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 1941, 5055, + 5056, 5057, 1964, 1946, 5058, 1926, 1928, 1956, + 1965, 5059, 1932, 5060, 5061, 1951, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 1945, 1920, + 1963, 1947, 1948, 5044, 1927, 1930, 5045, 5046, + 1931, 1950, 1933, 5047, 1935, 1936, 1937, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 1941, 5055, + 5056, 5057, 1964, 1946, 5058, 1926, 1928, 1956, + 1965, 5059, 1932, 5060, 5061, 1951, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5043, 1945, 1920, + 1963, 1947, 1948, 5044, 1927, 1930, 5045, 5046, + 1931, 1950, 1933, 5047, 1935, 1936, 1937, 5048, + 5049, 5050, 5051, 5052, 5053, 5054, 1941, 5055, + 5056, 5057, 1964, 1946, 5058, 1926, 1928, 1956, + 1965, 5059, 1932, 5060, 5061, 1951, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5043, 1945, 1920, 1963, 1947, 1948, 5044, + 1927, 1930, 5045, 5046, 1931, 1950, 1933, 5047, + 1935, 1936, 1937, 5048, 5049, 5050, 5051, 5052, + 5053, 5054, 1941, 5055, 5056, 5057, 1964, 1946, + 5058, 1926, 1928, 1956, 1965, 5059, 1932, 5060, + 5061, 1951, 5062, 5063, 5064, 5065, 5066, 5067, + 5068, 5069, 5070, 5071, 5072, 5073, 5074, 0, + 0, 5075, 5076, 1960, 5077, 5078, 5079, 5080, + 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, + 1961, 5089, 5090, 5091, 5092, 5093, 5094, 5095, + 5096, 5097, 5098, 5099, 5100, 1959, 5101, 5102, + 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, + 5111, 5112, 1958, 5113, 5114, 5115, 5116, 5117, + 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, + 5126, 5127, 5128, 5075, 5076, 1960, 5077, 5078, + 5079, 5080, 5081, 5082, 5083, 5084, 5085, 5086, + 5087, 5088, 1961, 5089, 5090, 5091, 5092, 5093, + 5094, 5095, 5096, 5097, 5098, 5099, 5100, 1959, + 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, + 5109, 5110, 5111, 5112, 1958, 5113, 5114, 5115, + 5116, 5117, 5118, 5119, 5120, 5121, 5122, 5123, + 5124, 5125, 5126, 5127, 5128, 5075, 5076, 1960, + 5077, 5078, 5079, 5080, 5081, 5082, 5083, 5084, + 5085, 5086, 5087, 5088, 1961, 5089, 5090, 5091, + 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, + 5100, 1959, 5101, 5102, 5103, 5104, 5105, 5106, + 5107, 5108, 5109, 5110, 5111, 5112, 1958, 5113, + 5114, 5115, 5116, 5117, 5118, 5119, 5120, 5121, + 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5075, + 5076, 1960, 5077, 5078, 5079, 5080, 5081, 5082, + 5083, 5084, 5085, 5086, 5087, 5088, 1961, 5089, + 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, + 5098, 5099, 5100, 1959, 5101, 5102, 5103, 5104, + 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, + 1958, 5113, 5114, 5115, 5116, 5117, 5118, 5119, + 5120, 5121, 5122, 5123, 5124, 5125, 5126, 5127, + 5128, 5075, 5076, 1960, 5077, 5078, 5079, 5080, + 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, + 1961, 5089, 5090, 5091, 5092, 5093, 5094, 5095, + 5096, 5097, 5098, 5099, 5100, 1959, 5101, 5102, + 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, + 5111, 5112, 1958, 5113, 5114, 5115, 5116, 5117, + 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, + 5126, 5127, 5128, 5129, 5130, 0, 0, 5131, + 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139, + 5140, 5131, 5132, 5133, 5134, 5135, 5136, 5137, + 5138, 5139, 5140, 5131, 5132, 5133, 5134, 5135, + 5136, 5137, 5138, 5139, 5140, 5131, 5132, 5133, + 5134, 5135, 5136, 5137, 5138, 5139, 5140, 5131, + 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139, + 5140, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5141, 5142, 5143, 5144, 5145, 3725, 5146, + 5147, 5148, 5149, 3726, 5150, 5151, 5152, 3727, + 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5160, + 5161, 5162, 5163, 5164, 3782, 5165, 5166, 5167, + 5168, 5169, 5170, 5171, 5172, 5173, 3787, 3728, + 3729, 3788, 5174, 5175, 3538, 5176, 3730, 5177, + 5178, 5179, 5180, 5180, 5180, 5181, 5182, 5183, + 5184, 5185, 5186, 5187, 5188, 5189, 5190, 5191, + 5192, 5193, 5194, 5195, 5196, 5197, 5198, 5198, + 3790, 5199, 5200, 5201, 5202, 3732, 5203, 5204, + 5205, 3691, 5206, 5207, 5208, 5209, 5210, 5211, + 5212, 5213, 5214, 5215, 5216, 5217, 5218, 5219, + 5220, 5221, 5222, 5223, 5224, 5225, 5226, 5227, + 5228, 5229, 5230, 5231, 5231, 5232, 5233, 5234, + 3534, 5235, 5236, 5237, 5238, 5239, 5240, 5241, + 5242, 3737, 5243, 5244, 5245, 5246, 5247, 5248, + 5249, 5250, 5251, 5252, 5253, 5254, 5255, 5256, + 5257, 5258, 5259, 5260, 5261, 5262, 5263, 3480, + 5264, 5265, 5266, 5266, 5267, 5268, 5268, 5269, + 5270, 5271, 5272, 5273, 5274, 5275, 5276, 5277, + 5278, 5279, 5280, 5281, 3738, 5282, 5283, 5284, + 5285, 3802, 5285, 5286, 3740, 5287, 5288, 5289, + 5290, 3741, 3453, 5291, 5292, 5293, 5294, 5295, + 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, + 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, + 5312, 3742, 5313, 5314, 5315, 5316, 5317, 5318, + 3744, 5319, 5320, 5321, 5322, 5323, 5324, 5325, + 5326, 3481, 3810, 5327, 5328, 5329, 5330, 5331, + 5332, 5333, 5334, 3745, 5335, 5336, 5337, 5338, + 3853, 5339, 5340, 5341, 5342, 5343, 5344, 5345, + 5346, 5347, 5348, 5349, 5350, 5351, 3551, 5352, + 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, + 5361, 5362, 3746, 3638, 5363, 5364, 5365, 5366, + 5367, 5368, 5369, 5370, 3814, 5371, 5372, 5373, + 5374, 5375, 5376, 5377, 5378, 3815, 5379, 5380, + 5381, 5382, 5383, 5384, 5385, 5386, 5387, 5388, + 5389, 5390, 3817, 5391, 5392, 5393, 5394, 5395, + 5396, 5397, 5398, 5399, 5400, 5401, 5401, 5402, + 5403, 3819, 5404, 5405, 5406, 5407, 5408, 5409, + 5410, 3537, 5411, 5412, 5413, 5414, 5415, 5416, + 5417, 3825, 5418, 5419, 5420, 5421, 5422, 5423, + 5423, 3826, 3855, 5424, 5425, 5426, 5427, 5428, + 3499, 3828, 5429, 5430, 3757, 5431, 5432, 3713, + 5433, 5434, 3761, 5435, 5436, 5437, 5438, 5438, + 5439, 5440, 5441, 5442, 5443, 5444, 5445, 5446, + 5447, 5448, 5449, 5450, 5451, 5452, 5453, 5454, + 5455, 5456, 5457, 5458, 5459, 5460, 5461, 5462, + 5463, 5464, 5465, 3767, 5466, 5467, 5468, 5469, + 5470, 5471, 5472, 5473, 5474, 5475, 5476, 5477, + 5478, 5479, 5480, 5481, 5267, 5482, 5483, 5484, + 5485, 5486, 5487, 5488, 5489, 5490, 5491, 5492, + 5493, 3555, 5494, 5495, 5496, 5497, 5498, 5499, + 3770, 5500, 5501, 5502, 5503, 5504, 5505, 5506, + 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, + 5515, 5516, 5517, 5518, 5519, 3494, 5520, 5521, + 5522, 5523, 5524, 5525, 3835, 5526, 5527, 5528, + 5529, 5530, 5531, 5532, 5533, 5534, 5535, 5536, + 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, + 5545, 3840, 3841, 5546, 5547, 5548, 5549, 5550, + 5551, 5552, 5553, 5554, 5555, 5556, 5557, 5558, + 3842, 5559, 5560, 5561, 5562, 5563, 5564, 5565, + 5566, 5567, 5568, 5569, 5570, 5571, 5572, 5573, + 5574, 5575, 5576, 5577, 5578, 5579, 5580, 5581, + 5582, 5583, 5584, 5585, 5586, 5587, 5588, 3848, + 3848, 5589, 5590, 5591, 5592, 5593, 5594, 5595, + 5596, 5597, 5598, 3849, 5599, 5600, 5601, 5602, + 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, + 5611, 5612, 5613, 5614, 5615, 5616, 5617, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 3440, + 3440, 3440, 3440, 3440, 3440, 3440, 3440, 0, + 0, }; + +UTF8PROC_DATA +const utf8proc_property_t utf8proc_properties[] = { + {0, 0, 0, 0, NULL, false, -1, -1, -1, -1, -1, false}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_BN, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_S, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_B, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_WS, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_B, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_S, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17580, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 17400, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17640, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 97, -1, 0, -1, false, false, false, false, utf8proc_sequences + 0}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 98, -1, 8640, -1, false, false, false, false, utf8proc_sequences + 2}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 99, -1, 60, -1, false, false, false, false, utf8proc_sequences + 4}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 100, -1, 960, -1, false, false, false, false, utf8proc_sequences + 6}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 101, -1, 120, -1, false, false, false, false, utf8proc_sequences + 8}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 102, -1, 9120, -1, false, false, false, false, utf8proc_sequences + 10}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 103, -1, 1080, -1, false, false, false, false, utf8proc_sequences + 12}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 104, -1, 1200, -1, false, false, false, false, utf8proc_sequences + 14}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 105, -1, 180, -1, false, false, false, false, utf8proc_sequences + 16}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 106, -1, 1320, -1, false, false, false, false, utf8proc_sequences + 18}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 107, -1, 1440, -1, false, false, false, false, utf8proc_sequences + 20}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 108, -1, 1560, -1, false, false, false, false, utf8proc_sequences + 22}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 109, -1, 9480, -1, false, false, false, false, utf8proc_sequences + 24}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 110, -1, 240, -1, false, false, false, false, utf8proc_sequences + 26}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 111, -1, 300, -1, false, false, false, false, utf8proc_sequences + 28}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 112, -1, 9720, -1, false, false, false, false, utf8proc_sequences + 30}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 113, -1, -1, -1, false, false, false, false, utf8proc_sequences + 32}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 114, -1, 1680, -1, false, false, false, false, utf8proc_sequences + 34}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 115, -1, 1800, -1, false, false, false, false, utf8proc_sequences + 36}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 116, -1, 1920, -1, false, false, false, false, utf8proc_sequences + 38}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 117, -1, 360, -1, false, false, false, false, utf8proc_sequences + 40}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 118, -1, 10560, -1, false, false, false, false, utf8proc_sequences + 42}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 119, -1, 2040, -1, false, false, false, false, utf8proc_sequences + 44}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 120, -1, 10680, -1, false, false, false, false, utf8proc_sequences + 46}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 121, -1, 420, -1, false, false, false, false, utf8proc_sequences + 48}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 122, -1, 2160, -1, false, false, false, false, utf8proc_sequences + 50}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 65, -1, 65, 480, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66, -1, 66, 8700, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 67, -1, 67, 540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 68, -1, 68, 1020, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 69, -1, 69, 600, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 70, -1, 70, 9180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 71, -1, 71, 1140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 72, -1, 72, 1260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 73, -1, 73, 660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 74, -1, 74, 1380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 75, -1, 75, 1500, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 76, -1, 76, 1620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 77, -1, 77, 9540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 78, -1, 78, 720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 79, -1, 79, 780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 80, -1, 80, 9780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 81, -1, 81, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 82, -1, 82, 1740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 83, -1, 83, 1860, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 84, -1, 84, 1980, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 85, -1, 85, 840, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 86, -1, 86, 10620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 87, -1, 87, 2100, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 88, -1, 88, 10740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 89, -1, 89, 900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 90, -1, 90, 2220, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_NOBREAK, utf8proc_sequences + 52, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 54, false, -1, -1, -1, 3600, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 0, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PI, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 57, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ET, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ET, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 60, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 62, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 64, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 67, false, 924, -1, 924, -1, -1, false, false, false, false, utf8proc_sequences + 67}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 69, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 72, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 28, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PF, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 74, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 78, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 82, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 86, false, -1, 224, -1, -1, -1, false, false, false, false, utf8proc_sequences + 89}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 91, false, -1, 225, -1, -1, -1, false, false, false, false, utf8proc_sequences + 94}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 96, false, -1, 226, -1, 10860, -1, false, false, false, false, utf8proc_sequences + 99}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 101, false, -1, 227, -1, -1, -1, false, false, false, false, utf8proc_sequences + 104}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 106, false, -1, 228, -1, 2400, -1, false, false, false, false, utf8proc_sequences + 109}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 111, false, -1, 229, -1, 3000, -1, false, false, false, false, utf8proc_sequences + 114}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 230, -1, 2640, -1, false, false, false, false, utf8proc_sequences + 116}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 118, false, -1, 231, -1, 8760, -1, false, false, false, false, utf8proc_sequences + 121}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 123, false, -1, 232, -1, -1, -1, false, false, false, false, utf8proc_sequences + 126}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 128, false, -1, 233, -1, -1, -1, false, false, false, false, utf8proc_sequences + 131}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 133, false, -1, 234, -1, 11220, -1, false, false, false, false, utf8proc_sequences + 136}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 138, false, -1, 235, -1, -1, -1, false, false, false, false, utf8proc_sequences + 141}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 143, false, -1, 236, -1, -1, -1, false, false, false, false, utf8proc_sequences + 146}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 148, false, -1, 237, -1, -1, -1, false, false, false, false, utf8proc_sequences + 151}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 153, false, -1, 238, -1, -1, -1, false, false, false, false, utf8proc_sequences + 156}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 158, false, -1, 239, -1, 9240, -1, false, false, false, false, utf8proc_sequences + 161}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 240, -1, -1, -1, false, false, false, false, utf8proc_sequences + 163}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 165, false, -1, 241, -1, -1, -1, false, false, false, false, utf8proc_sequences + 168}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 170, false, -1, 242, -1, -1, -1, false, false, false, false, utf8proc_sequences + 173}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 175, false, -1, 243, -1, -1, -1, false, false, false, false, utf8proc_sequences + 178}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 180, false, -1, 244, -1, 11460, -1, false, false, false, false, utf8proc_sequences + 183}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 185, false, -1, 245, -1, 3360, -1, false, false, false, false, utf8proc_sequences + 188}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 190, false, -1, 246, -1, 3240, -1, false, false, false, false, utf8proc_sequences + 193}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 248, -1, 3120, -1, false, false, false, false, utf8proc_sequences + 195}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 197, false, -1, 249, -1, -1, -1, false, false, false, false, utf8proc_sequences + 200}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 202, false, -1, 250, -1, -1, -1, false, false, false, false, utf8proc_sequences + 205}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 207, false, -1, 251, -1, -1, -1, false, false, false, false, utf8proc_sequences + 210}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 212, false, -1, 252, -1, 2280, -1, false, false, false, false, utf8proc_sequences + 215}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 217, false, -1, 253, -1, -1, -1, false, false, false, false, utf8proc_sequences + 220}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 254, -1, -1, -1, false, false, false, false, utf8proc_sequences + 222}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 224}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 227, false, 192, -1, 192, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 230, false, 193, -1, 193, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 233, false, 194, -1, 194, 10920, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 236, false, 195, -1, 195, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 239, false, 196, -1, 196, 2460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 242, false, 197, -1, 197, 3060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 198, -1, 198, 2700, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 245, false, 199, -1, 199, 8820, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 248, false, 200, -1, 200, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 251, false, 201, -1, 201, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 254, false, 202, -1, 202, 11280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 257, false, 203, -1, 203, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 260, false, 204, -1, 204, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 263, false, 205, -1, 205, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 266, false, 206, -1, 206, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 269, false, 207, -1, 207, 9300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 208, -1, 208, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 272, false, 209, -1, 209, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 275, false, 210, -1, 210, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 278, false, 211, -1, 211, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 281, false, 212, -1, 212, 11520, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 284, false, 213, -1, 213, 3420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 287, false, 214, -1, 214, 3300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 216, -1, 216, 3180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 290, false, 217, -1, 217, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 293, false, 218, -1, 218, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 296, false, 219, -1, 219, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 299, false, 220, -1, 220, 2340, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 302, false, 221, -1, 221, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 222, -1, 222, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 305, false, 376, -1, 376, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 308, false, -1, 257, -1, -1, -1, false, false, false, false, utf8proc_sequences + 311}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 313, false, 256, -1, 256, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 316, false, -1, 259, -1, 11100, -1, false, false, false, false, utf8proc_sequences + 319}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 321, false, 258, -1, 258, 11160, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 324, false, -1, 261, -1, -1, -1, false, false, false, false, utf8proc_sequences + 327}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 329, false, 260, -1, 260, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 332, false, -1, 263, -1, -1, -1, false, false, false, false, utf8proc_sequences + 335}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 337, false, 262, -1, 262, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 340, false, -1, 265, -1, -1, -1, false, false, false, false, utf8proc_sequences + 343}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 345, false, 264, -1, 264, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 348, false, -1, 267, -1, -1, -1, false, false, false, false, utf8proc_sequences + 351}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 353, false, 266, -1, 266, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 356, false, -1, 269, -1, -1, -1, false, false, false, false, utf8proc_sequences + 359}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 361, false, 268, -1, 268, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 364, false, -1, 271, -1, -1, -1, false, false, false, false, utf8proc_sequences + 367}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 369, false, 270, -1, 270, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 273, -1, -1, -1, false, false, false, false, utf8proc_sequences + 372}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 272, -1, 272, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 374, false, -1, 275, -1, 8880, -1, false, false, false, false, utf8proc_sequences + 377}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 379, false, 274, -1, 274, 8940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 382, false, -1, 277, -1, -1, -1, false, false, false, false, utf8proc_sequences + 385}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 387, false, 276, -1, 276, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 390, false, -1, 279, -1, -1, -1, false, false, false, false, utf8proc_sequences + 393}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 395, false, 278, -1, 278, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 398, false, -1, 281, -1, -1, -1, false, false, false, false, utf8proc_sequences + 401}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 403, false, 280, -1, 280, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 406, false, -1, 283, -1, -1, -1, false, false, false, false, utf8proc_sequences + 409}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 411, false, 282, -1, 282, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 414, false, -1, 285, -1, -1, -1, false, false, false, false, utf8proc_sequences + 417}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 419, false, 284, -1, 284, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 422, false, -1, 287, -1, -1, -1, false, false, false, false, utf8proc_sequences + 425}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 427, false, 286, -1, 286, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 430, false, -1, 289, -1, -1, -1, false, false, false, false, utf8proc_sequences + 433}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 435, false, 288, -1, 288, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 438, false, -1, 291, -1, -1, -1, false, false, false, false, utf8proc_sequences + 441}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 443, false, 290, -1, 290, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 446, false, -1, 293, -1, -1, -1, false, false, false, false, utf8proc_sequences + 449}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 451, false, 292, -1, 292, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 295, -1, -1, -1, false, false, false, false, utf8proc_sequences + 454}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 294, -1, 294, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 456, false, -1, 297, -1, -1, -1, false, false, false, false, utf8proc_sequences + 459}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 461, false, 296, -1, 296, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 464, false, -1, 299, -1, -1, -1, false, false, false, false, utf8proc_sequences + 467}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 469, false, 298, -1, 298, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 472, false, -1, 301, -1, -1, -1, false, false, false, false, utf8proc_sequences + 475}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 477, false, 300, -1, 300, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 480, false, -1, 303, -1, -1, -1, false, false, false, false, utf8proc_sequences + 483}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 485, false, 302, -1, 302, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 488, false, -1, 105, -1, -1, -1, false, false, false, false, utf8proc_sequences + 491}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 73, -1, 73, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 494, false, -1, 307, -1, -1, -1, false, false, false, false, utf8proc_sequences + 497}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 499, false, 306, -1, 306, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 502, false, -1, 309, -1, -1, -1, false, false, false, false, utf8proc_sequences + 505}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 507, false, 308, -1, 308, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 510, false, -1, 311, -1, -1, -1, false, false, false, false, utf8proc_sequences + 513}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 515, false, 310, -1, 310, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 518, false, -1, 314, -1, -1, -1, false, false, false, false, utf8proc_sequences + 521}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 523, false, 313, -1, 313, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 526, false, -1, 316, -1, -1, -1, false, false, false, false, utf8proc_sequences + 529}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 531, false, 315, -1, 315, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 534, false, -1, 318, -1, -1, -1, false, false, false, false, utf8proc_sequences + 537}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 539, false, 317, -1, 317, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 542, false, -1, 320, -1, -1, -1, false, false, false, false, utf8proc_sequences + 545}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 547, false, 319, -1, 319, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 322, -1, -1, -1, false, false, false, false, utf8proc_sequences + 550}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 321, -1, 321, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 552, false, -1, 324, -1, -1, -1, false, false, false, false, utf8proc_sequences + 555}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 557, false, 323, -1, 323, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 560, false, -1, 326, -1, -1, -1, false, false, false, false, utf8proc_sequences + 563}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 565, false, 325, -1, 325, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 568, false, -1, 328, -1, -1, -1, false, false, false, false, utf8proc_sequences + 571}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 573, false, 327, -1, 327, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 576, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 576}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 331, -1, -1, -1, false, false, false, false, utf8proc_sequences + 579}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 330, -1, 330, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 581, false, -1, 333, -1, 9600, -1, false, false, false, false, utf8proc_sequences + 584}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 586, false, 332, -1, 332, 9660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 589, false, -1, 335, -1, -1, -1, false, false, false, false, utf8proc_sequences + 592}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 594, false, 334, -1, 334, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 597, false, -1, 337, -1, -1, -1, false, false, false, false, utf8proc_sequences + 600}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 602, false, 336, -1, 336, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 339, -1, -1, -1, false, false, false, false, utf8proc_sequences + 605}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 338, -1, 338, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 607, false, -1, 341, -1, -1, -1, false, false, false, false, utf8proc_sequences + 610}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 612, false, 340, -1, 340, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 615, false, -1, 343, -1, -1, -1, false, false, false, false, utf8proc_sequences + 618}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 620, false, 342, -1, 342, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 623, false, -1, 345, -1, -1, -1, false, false, false, false, utf8proc_sequences + 626}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 628, false, 344, -1, 344, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 631, false, -1, 347, -1, 9960, -1, false, false, false, false, utf8proc_sequences + 634}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 636, false, 346, -1, 346, 10020, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 639, false, -1, 349, -1, -1, -1, false, false, false, false, utf8proc_sequences + 642}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 644, false, 348, -1, 348, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 647, false, -1, 351, -1, -1, -1, false, false, false, false, utf8proc_sequences + 650}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 652, false, 350, -1, 350, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 655, false, -1, 353, -1, 10080, -1, false, false, false, false, utf8proc_sequences + 658}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 660, false, 352, -1, 352, 10140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 663, false, -1, 355, -1, -1, -1, false, false, false, false, utf8proc_sequences + 666}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 668, false, 354, -1, 354, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 671, false, -1, 357, -1, -1, -1, false, false, false, false, utf8proc_sequences + 674}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 676, false, 356, -1, 356, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 359, -1, -1, -1, false, false, false, false, utf8proc_sequences + 679}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 358, -1, 358, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 681, false, -1, 361, -1, 10320, -1, false, false, false, false, utf8proc_sequences + 684}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 686, false, 360, -1, 360, 10380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 689, false, -1, 363, -1, 10440, -1, false, false, false, false, utf8proc_sequences + 692}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 694, false, 362, -1, 362, 10500, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 697, false, -1, 365, -1, -1, -1, false, false, false, false, utf8proc_sequences + 700}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 702, false, 364, -1, 364, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 705, false, -1, 367, -1, -1, -1, false, false, false, false, utf8proc_sequences + 708}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 710, false, 366, -1, 366, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 713, false, -1, 369, -1, -1, -1, false, false, false, false, utf8proc_sequences + 716}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 718, false, 368, -1, 368, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 721, false, -1, 371, -1, -1, -1, false, false, false, false, utf8proc_sequences + 724}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 726, false, 370, -1, 370, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 729, false, -1, 373, -1, -1, -1, false, false, false, false, utf8proc_sequences + 732}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 734, false, 372, -1, 372, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 737, false, -1, 375, -1, -1, -1, false, false, false, false, utf8proc_sequences + 740}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 742, false, 374, -1, 374, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 745, false, -1, 255, -1, -1, -1, false, false, false, false, utf8proc_sequences + 748}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 750, false, -1, 378, -1, -1, -1, false, false, false, false, utf8proc_sequences + 753}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 755, false, 377, -1, 377, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 758, false, -1, 380, -1, -1, -1, false, false, false, false, utf8proc_sequences + 761}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 763, false, 379, -1, 379, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 766, false, -1, 382, -1, -1, -1, false, false, false, false, utf8proc_sequences + 769}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 771, false, 381, -1, 381, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 36, false, 83, -1, 83, 10800, -1, false, false, false, false, utf8proc_sequences + 36}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 579, -1, 579, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 595, -1, -1, -1, false, false, false, false, utf8proc_sequences + 774}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 387, -1, -1, -1, false, false, false, false, utf8proc_sequences + 776}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 386, -1, 386, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 389, -1, -1, -1, false, false, false, false, utf8proc_sequences + 778}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 388, -1, 388, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 596, -1, -1, -1, false, false, false, false, utf8proc_sequences + 780}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 392, -1, -1, -1, false, false, false, false, utf8proc_sequences + 782}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 391, -1, 391, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 598, -1, -1, -1, false, false, false, false, utf8proc_sequences + 784}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 599, -1, -1, -1, false, false, false, false, utf8proc_sequences + 786}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 396, -1, -1, -1, false, false, false, false, utf8proc_sequences + 788}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 395, -1, 395, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 477, -1, -1, -1, false, false, false, false, utf8proc_sequences + 790}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 601, -1, -1, -1, false, false, false, false, utf8proc_sequences + 792}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 603, -1, -1, -1, false, false, false, false, utf8proc_sequences + 794}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 402, -1, -1, -1, false, false, false, false, utf8proc_sequences + 796}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 401, -1, 401, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 608, -1, -1, -1, false, false, false, false, utf8proc_sequences + 798}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 611, -1, -1, -1, false, false, false, false, utf8proc_sequences + 800}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 502, -1, 502, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 617, -1, -1, -1, false, false, false, false, utf8proc_sequences + 802}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 616, -1, -1, -1, false, false, false, false, utf8proc_sequences + 804}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 409, -1, -1, -1, false, false, false, false, utf8proc_sequences + 806}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 408, -1, 408, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 573, -1, 573, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 623, -1, -1, -1, false, false, false, false, utf8proc_sequences + 808}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 626, -1, -1, -1, false, false, false, false, utf8proc_sequences + 810}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 544, -1, 544, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 629, -1, -1, -1, false, false, false, false, utf8proc_sequences + 812}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 814, false, -1, 417, -1, 11700, -1, false, false, false, false, utf8proc_sequences + 817}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 819, false, 416, -1, 416, 11760, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 419, -1, -1, -1, false, false, false, false, utf8proc_sequences + 822}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 418, -1, 418, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 421, -1, -1, -1, false, false, false, false, utf8proc_sequences + 824}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 420, -1, 420, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 640, -1, -1, -1, false, false, false, false, utf8proc_sequences + 826}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 424, -1, -1, -1, false, false, false, false, utf8proc_sequences + 828}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 423, -1, 423, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 643, -1, -1, -1, false, false, false, false, utf8proc_sequences + 830}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 429, -1, -1, -1, false, false, false, false, utf8proc_sequences + 832}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 428, -1, 428, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 648, -1, -1, -1, false, false, false, false, utf8proc_sequences + 834}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 836, false, -1, 432, -1, 11820, -1, false, false, false, false, utf8proc_sequences + 839}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 841, false, 431, -1, 431, 11880, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 650, -1, -1, -1, false, false, false, false, utf8proc_sequences + 844}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 651, -1, -1, -1, false, false, false, false, utf8proc_sequences + 846}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 436, -1, -1, -1, false, false, false, false, utf8proc_sequences + 848}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 435, -1, 435, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 438, -1, -1, -1, false, false, false, false, utf8proc_sequences + 850}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 437, -1, 437, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 658, -1, 2880, -1, false, false, false, false, utf8proc_sequences + 852}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 441, -1, -1, -1, false, false, false, false, utf8proc_sequences + 854}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 440, -1, 440, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 445, -1, -1, -1, false, false, false, false, utf8proc_sequences + 856}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 444, -1, 444, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 503, -1, 503, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 858, false, -1, 454, 453, -1, -1, false, false, false, false, utf8proc_sequences + 861}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 863, false, 452, 454, 453, -1, -1, false, false, false, false, utf8proc_sequences + 861}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 866, false, 452, -1, 453, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 869, false, -1, 457, 456, -1, -1, false, false, false, false, utf8proc_sequences + 872}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 874, false, 455, 457, 456, -1, -1, false, false, false, false, utf8proc_sequences + 872}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 877, false, 455, -1, 456, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 880, false, -1, 460, 459, -1, -1, false, false, false, false, utf8proc_sequences + 883}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 885, false, 458, 460, 459, -1, -1, false, false, false, false, utf8proc_sequences + 883}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 888, false, 458, -1, 459, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 891, false, -1, 462, -1, -1, -1, false, false, false, false, utf8proc_sequences + 894}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 896, false, 461, -1, 461, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 899, false, -1, 464, -1, -1, -1, false, false, false, false, utf8proc_sequences + 902}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 904, false, 463, -1, 463, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 907, false, -1, 466, -1, -1, -1, false, false, false, false, utf8proc_sequences + 910}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 912, false, 465, -1, 465, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 915, false, -1, 468, -1, -1, -1, false, false, false, false, utf8proc_sequences + 918}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 920, false, 467, -1, 467, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 923, false, -1, 470, -1, -1, -1, false, false, false, false, utf8proc_sequences + 926}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 928, false, 469, -1, 469, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 931, false, -1, 472, -1, -1, -1, false, false, false, false, utf8proc_sequences + 934}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 936, false, 471, -1, 471, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 939, false, -1, 474, -1, -1, -1, false, false, false, false, utf8proc_sequences + 942}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 944, false, 473, -1, 473, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 947, false, -1, 476, -1, -1, -1, false, false, false, false, utf8proc_sequences + 950}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 952, false, 475, -1, 475, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 398, -1, 398, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 955, false, -1, 479, -1, -1, -1, false, false, false, false, utf8proc_sequences + 958}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 960, false, 478, -1, 478, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 963, false, -1, 481, -1, -1, -1, false, false, false, false, utf8proc_sequences + 966}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 968, false, 480, -1, 480, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 971, false, -1, 483, -1, -1, -1, false, false, false, false, utf8proc_sequences + 974}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 976, false, 482, -1, 482, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 485, -1, -1, -1, false, false, false, false, utf8proc_sequences + 979}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 484, -1, 484, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 981, false, -1, 487, -1, -1, -1, false, false, false, false, utf8proc_sequences + 984}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 986, false, 486, -1, 486, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 989, false, -1, 489, -1, -1, -1, false, false, false, false, utf8proc_sequences + 992}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 994, false, 488, -1, 488, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 997, false, -1, 491, -1, 2760, -1, false, false, false, false, utf8proc_sequences + 1000}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1002, false, 490, -1, 490, 2820, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1005, false, -1, 493, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1008}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1010, false, 492, -1, 492, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1013, false, -1, 495, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1016}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1018, false, 494, -1, 494, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1021, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1021}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1024, false, -1, 499, 498, -1, -1, false, false, false, false, utf8proc_sequences + 1027}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1029, false, 497, 499, 498, -1, -1, false, false, false, false, utf8proc_sequences + 1027}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1032, false, 497, -1, 498, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1035, false, -1, 501, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1038}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1040, false, 500, -1, 500, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 405, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1043}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 447, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1045}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1047, false, -1, 505, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1050}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1052, false, 504, -1, 504, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1055, false, -1, 507, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1058}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1060, false, 506, -1, 506, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1063, false, -1, 509, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1066}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1068, false, 508, -1, 508, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1071, false, -1, 511, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1074}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1076, false, 510, -1, 510, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1079, false, -1, 513, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1082}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1084, false, 512, -1, 512, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1087, false, -1, 515, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1090}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1092, false, 514, -1, 514, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1095, false, -1, 517, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1098}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1100, false, 516, -1, 516, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1103, false, -1, 519, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1106}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1108, false, 518, -1, 518, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1111, false, -1, 521, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1114}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1116, false, 520, -1, 520, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1119, false, -1, 523, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1122}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1124, false, 522, -1, 522, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1127, false, -1, 525, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1130}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1132, false, 524, -1, 524, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1135, false, -1, 527, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1138}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1140, false, 526, -1, 526, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1143, false, -1, 529, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1146}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1148, false, 528, -1, 528, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1151, false, -1, 531, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1154}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1156, false, 530, -1, 530, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1159, false, -1, 533, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1162}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1164, false, 532, -1, 532, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1167, false, -1, 535, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1170}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1172, false, 534, -1, 534, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1175, false, -1, 537, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1178}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1180, false, 536, -1, 536, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1183, false, -1, 539, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1186}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1188, false, 538, -1, 538, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 541, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1191}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 540, -1, 540, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1193, false, -1, 543, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1196}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1198, false, 542, -1, 542, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 414, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1201}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 547, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1203}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 546, -1, 546, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 549, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1205}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 548, -1, 548, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1207, false, -1, 551, -1, 2520, -1, false, false, false, false, utf8proc_sequences + 1210}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1212, false, 550, -1, 550, 2580, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1215, false, -1, 553, -1, 9000, -1, false, false, false, false, utf8proc_sequences + 1218}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1220, false, 552, -1, 552, 9060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1223, false, -1, 555, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1226}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1228, false, 554, -1, 554, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1231, false, -1, 557, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1234}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1236, false, 556, -1, 556, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1239, false, -1, 559, -1, 3480, -1, false, false, false, false, utf8proc_sequences + 1242}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1244, false, 558, -1, 558, 3540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1247, false, -1, 561, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1250}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1252, false, 560, -1, 560, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1255, false, -1, 563, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1258}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1260, false, 562, -1, 562, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11365, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1263}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 572, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1265}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 571, -1, 571, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 410, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1267}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11366, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1269}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 578, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1271}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 577, -1, 577, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 384, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1273}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 649, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1275}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 652, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1277}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 583, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1279}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 582, -1, 582, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 585, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1281}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 584, -1, 584, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 587, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1283}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 586, -1, 586, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 589, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1285}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 588, -1, 588, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 591, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1287}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 590, -1, 590, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 385, -1, 385, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 390, -1, 390, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 393, -1, 393, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 394, -1, 394, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 399, -1, 399, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 400, -1, 400, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 403, -1, 403, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 404, -1, 404, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 407, -1, 407, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 406, -1, 406, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11362, -1, 11362, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 412, -1, 412, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 413, -1, 413, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 415, -1, 415, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11364, -1, 11364, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 422, -1, 422, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 425, -1, 425, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 430, -1, 430, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 580, -1, 580, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 433, -1, 433, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 434, -1, 434, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 581, -1, 581, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 439, -1, 439, 2940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 14, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1289, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 18, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 34, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1291, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1293, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1295, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 44, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 48, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1297, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1300, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1303, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1306, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1309, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1312, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 800, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 22, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 36, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 46, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1315, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 0, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 2, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 3, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 7, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 8, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 10, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 4, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 46, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 5, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 12, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 11, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 14, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 15, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 47, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 48, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 232, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 216, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 13, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 40, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 45, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 39, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 16, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 6, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 9, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 42, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 44, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 43, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 41, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 1, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 1, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 51, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 1317, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 1319, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 49, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 1321, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 1323, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 240, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, 921, -1, 921, -1, 50, false, false, false, true, utf8proc_sequences + 1326}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, true, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 233, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 234, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 1328, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1330, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1021, -1, 1021, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1022, -1, 1022, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1023, -1, 1023, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 1333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 1335, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1338, false, -1, 940, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1341}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 1343, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1345, false, -1, 941, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1348}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1350, false, -1, 942, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1353}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1355, false, -1, 943, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1358}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1360, false, -1, 972, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1363}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1365, false, -1, 973, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1368}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1370, false, -1, 974, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1373}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1375, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1378}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 945, -1, 3660, -1, false, false, false, false, utf8proc_sequences + 1382}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 946, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1384}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 947, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1386}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 948, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1388}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 949, -1, 3720, -1, false, false, false, false, utf8proc_sequences + 1390}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 950, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1392}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 951, -1, 3780, -1, false, false, false, false, utf8proc_sequences + 1394}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 952, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1396}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 953, -1, 3840, -1, false, false, false, false, utf8proc_sequences + 1326}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 954, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1398}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 955, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1400}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 956, -1, -1, -1, false, false, false, false, utf8proc_sequences + 67}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 957, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1402}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 958, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1404}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 959, -1, 3900, -1, false, false, false, false, utf8proc_sequences + 1406}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 960, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1408}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 961, -1, 16260, -1, false, false, false, false, utf8proc_sequences + 1410}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 963, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1412}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 964, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1414}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 965, -1, 3960, -1, false, false, false, false, utf8proc_sequences + 1416}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 966, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1418}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 967, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1420}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 968, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1422}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 969, -1, 4020, -1, false, false, false, false, utf8proc_sequences + 1424}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1426, false, -1, 970, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1429}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1431, false, -1, 971, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1434}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1436, false, 902, -1, 902, 15780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1439, false, 904, -1, 904, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1442, false, 905, -1, 905, 15960, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1445, false, 906, -1, 906, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1448, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1451}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 913, -1, 913, 4140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 914, -1, 914, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 915, -1, 915, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 916, -1, 916, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 917, -1, 917, 4200, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 918, -1, 918, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 919, -1, 919, 4260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 920, -1, 920, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 921, -1, 921, 4320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 922, -1, 922, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 923, -1, 923, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 924, -1, 924, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 925, -1, 925, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 926, -1, 926, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 927, -1, 927, 4500, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 928, -1, 928, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 929, -1, 929, 16200, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 931, -1, 931, -1, -1, false, false, false, false, utf8proc_sequences + 1412}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 931, -1, 931, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 932, -1, 932, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 933, -1, 933, 4440, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 934, -1, 934, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 935, -1, 935, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 936, -1, 936, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 937, -1, 937, 4560, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1455, false, 938, -1, 938, 4080, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1458, false, 939, -1, 939, 4380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1461, false, 908, -1, 908, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1464, false, 910, -1, 910, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1467, false, 911, -1, 911, 16380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1384, false, 914, -1, 914, -1, -1, false, false, false, false, utf8proc_sequences + 1384}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1396, false, 920, -1, 920, -1, -1, false, false, false, false, utf8proc_sequences + 1396}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1470, false, -1, -1, -1, 4620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1472, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1475, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1418, false, 934, -1, 934, -1, -1, false, false, false, false, utf8proc_sequences + 1418}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1408, false, 928, -1, 928, -1, -1, false, false, false, false, utf8proc_sequences + 1408}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 985, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1478}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 984, -1, 984, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 987, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1480}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 986, -1, 986, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 989, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1482}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 988, -1, 988, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 991, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1484}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 990, -1, 990, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 993, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1486}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 992, -1, 992, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 995, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1488}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 994, -1, 994, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 997, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1490}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 996, -1, 996, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 999, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1492}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 998, -1, 998, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1001, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1494}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1000, -1, 1000, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1003, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1496}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1002, -1, 1002, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1005, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1498}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1004, -1, 1004, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1007, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1500}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1006, -1, 1006, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1398, false, 922, -1, 922, -1, -1, false, false, false, false, utf8proc_sequences + 1398}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1410, false, 929, -1, 929, -1, -1, false, false, false, false, utf8proc_sequences + 1410}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1502, false, 1017, -1, 1017, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1504, false, -1, 952, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1396}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1390, false, 917, -1, 917, -1, -1, false, false, false, false, utf8proc_sequences + 1390}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1016, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1506}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1015, -1, 1015, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 1508, false, -1, 1010, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1510}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1019, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1512}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1018, -1, 1018, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 891, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1514}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 892, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1516}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 893, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1518}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1520, false, -1, 1104, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1523}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1525, false, -1, 1105, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1528}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1106, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1530}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1532, false, -1, 1107, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1535}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1108, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1537}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1109, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1539}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1110, -1, 4800, -1, false, false, false, false, utf8proc_sequences + 1541}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1543, false, -1, 1111, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1546}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1112, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1548}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1113, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1550}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1114, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1552}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1115, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1554}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1556, false, -1, 1116, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1559}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1561, false, -1, 1117, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1564}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1566, false, -1, 1118, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1569}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1119, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1571}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1072, -1, 5640, -1, false, false, false, false, utf8proc_sequences + 1573}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1073, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1575}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1074, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1577}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1075, -1, 4740, -1, false, false, false, false, utf8proc_sequences + 1579}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1076, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1581}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1077, -1, 4680, -1, false, false, false, false, utf8proc_sequences + 1583}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1078, -1, 5520, -1, false, false, false, false, utf8proc_sequences + 1585}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1079, -1, 5880, -1, false, false, false, false, utf8proc_sequences + 1587}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1080, -1, 4920, -1, false, false, false, false, utf8proc_sequences + 1589}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1591, false, -1, 1081, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1594}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1082, -1, 4860, -1, false, false, false, false, utf8proc_sequences + 1596}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1083, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1598}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1084, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1600}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1085, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1602}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1086, -1, 6000, -1, false, false, false, false, utf8proc_sequences + 1604}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1087, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1606}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1088, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1608}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1089, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1610}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1090, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1612}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1091, -1, 4980, -1, false, false, false, false, utf8proc_sequences + 1614}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1092, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1616}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1093, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1618}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1094, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1620}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1095, -1, 6360, -1, false, false, false, false, utf8proc_sequences + 1622}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1096, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1624}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1097, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1626}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1098, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1628}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1099, -1, 6480, -1, false, false, false, false, utf8proc_sequences + 1630}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1100, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1632}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1101, -1, 6240, -1, false, false, false, false, utf8proc_sequences + 1634}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1102, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1636}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1103, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1638}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1040, -1, 1040, 5700, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1041, -1, 1041, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1042, -1, 1042, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1043, -1, 1043, 5160, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1044, -1, 1044, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1045, -1, 1045, 5100, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1046, -1, 1046, 5580, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1047, -1, 1047, 5940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1048, -1, 1048, 5040, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1640, false, 1049, -1, 1049, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1050, -1, 1050, 5280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1051, -1, 1051, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1052, -1, 1052, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1053, -1, 1053, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1054, -1, 1054, 6060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1055, -1, 1055, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1056, -1, 1056, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1057, -1, 1057, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1058, -1, 1058, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1059, -1, 1059, 5340, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1060, -1, 1060, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1061, -1, 1061, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1062, -1, 1062, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1063, -1, 1063, 6420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1064, -1, 1064, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1065, -1, 1065, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1066, -1, 1066, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1067, -1, 1067, 6540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1068, -1, 1068, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1069, -1, 1069, 6300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1070, -1, 1070, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1071, -1, 1071, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1643, false, 1024, -1, 1024, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1646, false, 1025, -1, 1025, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1026, -1, 1026, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1649, false, 1027, -1, 1027, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1028, -1, 1028, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1029, -1, 1029, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1030, -1, 1030, 5220, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1652, false, 1031, -1, 1031, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1032, -1, 1032, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1033, -1, 1033, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1034, -1, 1034, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1035, -1, 1035, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1655, false, 1036, -1, 1036, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1658, false, 1037, -1, 1037, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1661, false, 1038, -1, 1038, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1039, -1, 1039, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1121, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1664}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1120, -1, 1120, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1123, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1666}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1122, -1, 1122, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1125, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1668}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1124, -1, 1124, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1127, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1670}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1126, -1, 1126, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1129, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1672}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1128, -1, 1128, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1131, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1674}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1130, -1, 1130, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1133, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1676}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1132, -1, 1132, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1135, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1678}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1134, -1, 1134, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1137, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1680}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1136, -1, 1136, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1139, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1682}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1138, -1, 1138, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1141, -1, 5400, -1, false, false, false, false, utf8proc_sequences + 1684}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1140, -1, 1140, 5460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1686, false, -1, 1143, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1689}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1691, false, 1142, -1, 1142, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1145, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1694}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1144, -1, 1144, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1147, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1696}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1146, -1, 1146, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1149, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1698}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1148, -1, 1148, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1151, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1700}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1150, -1, 1150, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1153, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1702}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1152, -1, 1152, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ME, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1163, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1704}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1162, -1, 1162, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1165, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1706}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1164, -1, 1164, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1167, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1708}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1166, -1, 1166, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1169, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1710}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1168, -1, 1168, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1171, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1712}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1170, -1, 1170, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1173, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1714}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1172, -1, 1172, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1175, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1716}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1174, -1, 1174, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1177, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1718}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1176, -1, 1176, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1179, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1720}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1178, -1, 1178, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1181, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1722}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1180, -1, 1180, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1183, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1724}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1182, -1, 1182, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1185, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1726}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1184, -1, 1184, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1187, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1728}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1186, -1, 1186, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1189, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1730}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1188, -1, 1188, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1191, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1732}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1190, -1, 1190, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1193, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1734}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1192, -1, 1192, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1195, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1736}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1194, -1, 1194, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1197, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1738}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1196, -1, 1196, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1199, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1740}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1198, -1, 1198, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1201, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1742}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1200, -1, 1200, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1203, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1744}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1202, -1, 1202, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1205, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1746}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1204, -1, 1204, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1207, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1748}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1206, -1, 1206, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1209, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1750}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1208, -1, 1208, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1211, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1752}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1210, -1, 1210, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1213, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1754}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1212, -1, 1212, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1215, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1756}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1214, -1, 1214, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1231, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1758}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1760, false, -1, 1218, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1763}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1765, false, 1217, -1, 1217, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1220, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1768}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1219, -1, 1219, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1222, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1770}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1221, -1, 1221, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1224, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1772}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1223, -1, 1223, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1226, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1774}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1225, -1, 1225, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1228, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1776}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1227, -1, 1227, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1230, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1778}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1229, -1, 1229, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1216, -1, 1216, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1780, false, -1, 1233, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1783}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1785, false, 1232, -1, 1232, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1788, false, -1, 1235, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1791}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1793, false, 1234, -1, 1234, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1237, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1796}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1236, -1, 1236, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1798, false, -1, 1239, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1801}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1803, false, 1238, -1, 1238, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1241, -1, 5760, -1, false, false, false, false, utf8proc_sequences + 1806}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1240, -1, 1240, 5820, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1808, false, -1, 1243, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1811}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1813, false, 1242, -1, 1242, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1816, false, -1, 1245, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1819}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1821, false, 1244, -1, 1244, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1824, false, -1, 1247, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1827}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1829, false, 1246, -1, 1246, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1249, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1832}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1248, -1, 1248, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1834, false, -1, 1251, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1837}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1839, false, 1250, -1, 1250, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1842, false, -1, 1253, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1845}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1847, false, 1252, -1, 1252, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1850, false, -1, 1255, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1853}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1855, false, 1254, -1, 1254, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1257, -1, 6120, -1, false, false, false, false, utf8proc_sequences + 1858}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1256, -1, 1256, 6180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1860, false, -1, 1259, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1863}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1865, false, 1258, -1, 1258, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1868, false, -1, 1261, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1871}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1873, false, 1260, -1, 1260, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1876, false, -1, 1263, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1879}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1881, false, 1262, -1, 1262, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1884, false, -1, 1265, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1887}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1889, false, 1264, -1, 1264, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1892, false, -1, 1267, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1895}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1897, false, 1266, -1, 1266, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1900, false, -1, 1269, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1903}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1905, false, 1268, -1, 1268, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1271, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1908}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1270, -1, 1270, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1910, false, -1, 1273, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1913}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1915, false, 1272, -1, 1272, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1275, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1918}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1274, -1, 1274, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1277, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1920}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1276, -1, 1276, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1279, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1922}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1278, -1, 1278, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1281, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1924}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1280, -1, 1280, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1283, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1926}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1282, -1, 1282, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1285, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1928}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1284, -1, 1284, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1287, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1930}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1286, -1, 1286, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1289, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1932}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1288, -1, 1288, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1291, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1934}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1290, -1, 1290, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1293, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1936}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1292, -1, 1292, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1295, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1938}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1294, -1, 1294, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1297, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1940}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1296, -1, 1296, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1299, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1942}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1298, -1, 1298, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1377, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1944}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1378, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1946}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1379, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1948}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1380, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1950}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1381, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1952}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1382, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1954}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1383, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1956}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1384, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1958}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1385, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1960}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1386, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1962}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1387, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1964}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1388, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1966}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1389, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1968}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1390, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1970}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1391, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1972}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1392, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1974}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1393, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1976}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1394, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1978}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1395, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1980}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1396, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1982}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1397, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1984}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1398, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1986}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1399, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1988}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1400, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1990}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1401, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1992}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1402, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1994}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1403, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1996}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1404, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1998}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1405, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2000}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1406, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2002}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1407, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2004}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1408, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2006}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1409, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2008}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1410, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2010}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1411, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2012}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1412, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2014}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1413, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2016}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 1414, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2018}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1329, -1, 1329, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1330, -1, 1330, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1331, -1, 1331, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1332, -1, 1332, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1333, -1, 1333, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1334, -1, 1334, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1335, -1, 1335, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1336, -1, 1336, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1337, -1, 1337, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1338, -1, 1338, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1339, -1, 1339, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1340, -1, 1340, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1341, -1, 1341, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1342, -1, 1342, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1343, -1, 1343, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1344, -1, 1344, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1345, -1, 1345, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1346, -1, 1346, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1347, -1, 1347, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1348, -1, 1348, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1349, -1, 1349, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1350, -1, 1350, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1351, -1, 1351, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1352, -1, 1352, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1353, -1, 1353, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1354, -1, 1354, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1355, -1, 1355, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1356, -1, 1356, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1357, -1, 1357, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1358, -1, 1358, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1359, -1, 1359, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1360, -1, 1360, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1361, -1, 1361, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1362, -1, 1362, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1363, -1, 1363, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1364, -1, 1364, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1365, -1, 1365, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 1366, -1, 1366, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2020, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2020}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 222, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 228, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 10, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 11, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 12, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 13, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 14, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 15, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 16, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 17, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 18, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 19, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 20, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 21, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 22, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 23, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 24, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 25, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2023, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2026, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2032, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6600, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 27, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 28, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 29, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 30, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 31, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 32, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 33, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 34, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 17, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 18, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 19, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_AN, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_AN, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 35, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6840, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2053, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, utf8proc_sequences + 2056, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, 6780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_AL, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 36, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 6960, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7020, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2062, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7080, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2065, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 7, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 20, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 9, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2068, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2071, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2074, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2077, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2080, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2083, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2086, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2089, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 7, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 21, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2092, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 22, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2098, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2101, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2104, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2107, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2110, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2113, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2116, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2119, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2122, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 24, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7200, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2128, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 23, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 25, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2134, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2137, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 27, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2149, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 26, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, 7440, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2152, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 84, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 91, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 28, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7500, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2155, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 31, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7560, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2161, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2164, false, -1, -1, -1, 7620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2167, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 29, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 30, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 32, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7680, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2170, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2173, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2176, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 33, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 9, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 34, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 35, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7800, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2182, false, -1, -1, -1, 7860, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2188, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 36, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2191, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 103, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 107, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2194, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 118, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 122, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2197, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NOBREAK, utf8proc_sequences + 2203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 216, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2205, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2208, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2211, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2214, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2217, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2220, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 129, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 130, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2223, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 132, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2226, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2229, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2232, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2235, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2238, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2241, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2244, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2247, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2250, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2253, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2256, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, utf8proc_sequences + 2259, false, -1, -1, -1, -1, -1, true, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7920, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2262, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 37, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11520, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2265}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11521, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2267}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11522, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2269}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11523, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2271}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11524, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2273}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11525, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2275}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11526, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2277}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11527, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2279}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11528, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2281}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11529, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2283}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11530, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2285}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11531, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2287}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11532, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2289}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11533, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2291}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11534, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2293}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11535, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2295}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11536, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2297}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11537, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2299}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11538, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2301}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11539, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2303}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11540, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2305}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11541, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2307}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11542, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2309}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11543, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2311}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11544, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2313}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11545, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2315}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11546, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2317}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11547, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2319}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11548, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2321}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11549, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2323}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11550, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2325}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11551, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2327}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11552, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2329}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11553, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2331}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11554, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2333}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11555, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2335}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11556, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2337}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11557, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2339}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2341, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, true, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 7980, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2343, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8040, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2346, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8100, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2349, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8160, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2352, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8220, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2355, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2358, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 38, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, 8340, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2361, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, 8400, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2364, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 8520, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2367, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2370, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, 8580, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2373, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 9, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2376, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2378, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2380, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2388, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2390, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2394, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2396, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2400, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2402, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2406, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2408, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2412, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2414, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2416, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 0, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2418, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2422, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 8, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 792, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 794, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2424, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 12, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 20, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 24, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 579, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 28, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 780, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2426, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2428, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 30, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 38, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 40, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2430, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 808, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 42, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2432, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1388, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1418, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 16, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 34, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 40, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 42, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 1384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 1386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 1410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 1418, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 1420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1602, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11363, -1, 11363, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2434, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2436, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 163, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 10, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2438, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2442, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 804, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 802, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2444, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2446, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2448, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2450, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2452, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2456, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2458, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 810, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2460, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2462, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 812, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2464, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2466, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 830, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2468, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1275, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 844, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 846, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1277, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 50, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2472, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 2474, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 852, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 1396, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2476, false, -1, 7681, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2479}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2481, false, 7680, -1, 7680, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2484, false, -1, 7683, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2487}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2489, false, 7682, -1, 7682, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2492, false, -1, 7685, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2495}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2497, false, 7684, -1, 7684, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2500, false, -1, 7687, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2503}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2505, false, 7686, -1, 7686, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2508, false, -1, 7689, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2511}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2513, false, 7688, -1, 7688, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2516, false, -1, 7691, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2519}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2521, false, 7690, -1, 7690, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2524, false, -1, 7693, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2527}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2529, false, 7692, -1, 7692, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2532, false, -1, 7695, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2535}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2537, false, 7694, -1, 7694, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2540, false, -1, 7697, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2543}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2545, false, 7696, -1, 7696, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2548, false, -1, 7699, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2551}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2553, false, 7698, -1, 7698, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2556, false, -1, 7701, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2559}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2561, false, 7700, -1, 7700, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2564, false, -1, 7703, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2567}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2569, false, 7702, -1, 7702, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2572, false, -1, 7705, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2575}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2577, false, 7704, -1, 7704, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2580, false, -1, 7707, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2583}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2585, false, 7706, -1, 7706, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2588, false, -1, 7709, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2591}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2593, false, 7708, -1, 7708, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2596, false, -1, 7711, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2599}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2601, false, 7710, -1, 7710, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2604, false, -1, 7713, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2607}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2609, false, 7712, -1, 7712, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2612, false, -1, 7715, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2615}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2617, false, 7714, -1, 7714, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2620, false, -1, 7717, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2623}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2625, false, 7716, -1, 7716, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2628, false, -1, 7719, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2631}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2633, false, 7718, -1, 7718, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2636, false, -1, 7721, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2639}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2641, false, 7720, -1, 7720, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2644, false, -1, 7723, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2647}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2649, false, 7722, -1, 7722, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2652, false, -1, 7725, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2655}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2657, false, 7724, -1, 7724, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2660, false, -1, 7727, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2663}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2665, false, 7726, -1, 7726, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2668, false, -1, 7729, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2671}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2673, false, 7728, -1, 7728, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2676, false, -1, 7731, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2679}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2681, false, 7730, -1, 7730, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2684, false, -1, 7733, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2687}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2689, false, 7732, -1, 7732, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2692, false, -1, 7735, -1, 9360, -1, false, false, false, false, utf8proc_sequences + 2695}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2697, false, 7734, -1, 7734, 9420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2700, false, -1, 7737, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2703}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2705, false, 7736, -1, 7736, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2708, false, -1, 7739, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2711}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2713, false, 7738, -1, 7738, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2716, false, -1, 7741, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2719}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2721, false, 7740, -1, 7740, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2724, false, -1, 7743, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2727}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2729, false, 7742, -1, 7742, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2732, false, -1, 7745, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2735}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2737, false, 7744, -1, 7744, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2740, false, -1, 7747, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2743}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2745, false, 7746, -1, 7746, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2748, false, -1, 7749, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2751}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2753, false, 7748, -1, 7748, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2756, false, -1, 7751, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2759}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2761, false, 7750, -1, 7750, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2764, false, -1, 7753, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2767}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2769, false, 7752, -1, 7752, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2772, false, -1, 7755, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2775}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2777, false, 7754, -1, 7754, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2780, false, -1, 7757, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2783}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2785, false, 7756, -1, 7756, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2788, false, -1, 7759, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2791}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2793, false, 7758, -1, 7758, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2796, false, -1, 7761, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2799}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2801, false, 7760, -1, 7760, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2804, false, -1, 7763, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2807}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2809, false, 7762, -1, 7762, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2812, false, -1, 7765, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2815}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2817, false, 7764, -1, 7764, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2820, false, -1, 7767, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2823}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2825, false, 7766, -1, 7766, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2828, false, -1, 7769, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2831}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2833, false, 7768, -1, 7768, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2836, false, -1, 7771, -1, 9840, -1, false, false, false, false, utf8proc_sequences + 2839}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2841, false, 7770, -1, 7770, 9900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2844, false, -1, 7773, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2847}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2849, false, 7772, -1, 7772, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2852, false, -1, 7775, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2855}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2857, false, 7774, -1, 7774, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2860, false, -1, 7777, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2863}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2865, false, 7776, -1, 7776, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2868, false, -1, 7779, -1, 10200, -1, false, false, false, false, utf8proc_sequences + 2871}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2873, false, 7778, -1, 7778, 10260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2876, false, -1, 7781, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2879}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2881, false, 7780, -1, 7780, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2884, false, -1, 7783, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2887}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2889, false, 7782, -1, 7782, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2892, false, -1, 7785, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2895}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2897, false, 7784, -1, 7784, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2900, false, -1, 7787, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2903}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2905, false, 7786, -1, 7786, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2908, false, -1, 7789, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2911}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2913, false, 7788, -1, 7788, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2916, false, -1, 7791, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2919}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2921, false, 7790, -1, 7790, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2924, false, -1, 7793, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2927}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2929, false, 7792, -1, 7792, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2932, false, -1, 7795, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2935}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2937, false, 7794, -1, 7794, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2940, false, -1, 7797, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2943}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2945, false, 7796, -1, 7796, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2948, false, -1, 7799, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2951}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2953, false, 7798, -1, 7798, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2956, false, -1, 7801, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2959}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2961, false, 7800, -1, 7800, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2964, false, -1, 7803, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2967}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2969, false, 7802, -1, 7802, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2972, false, -1, 7805, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2975}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2977, false, 7804, -1, 7804, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2980, false, -1, 7807, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2983}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2985, false, 7806, -1, 7806, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2988, false, -1, 7809, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2991}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2993, false, 7808, -1, 7808, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2996, false, -1, 7811, -1, -1, -1, false, false, false, false, utf8proc_sequences + 2999}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3001, false, 7810, -1, 7810, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3004, false, -1, 7813, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3007}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3009, false, 7812, -1, 7812, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3012, false, -1, 7815, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3015}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3017, false, 7814, -1, 7814, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3020, false, -1, 7817, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3023}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3025, false, 7816, -1, 7816, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3028, false, -1, 7819, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3031}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3033, false, 7818, -1, 7818, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3036, false, -1, 7821, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3039}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3041, false, 7820, -1, 7820, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3044, false, -1, 7823, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3047}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3049, false, 7822, -1, 7822, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3052, false, -1, 7825, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3055}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3057, false, 7824, -1, 7824, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3060, false, -1, 7827, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3063}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3065, false, 7826, -1, 7826, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3068, false, -1, 7829, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3071}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3073, false, 7828, -1, 7828, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3076, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3076}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3079, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3079}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3082, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3082}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3085, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3085}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 3088, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3088}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3091, false, 7776, -1, 7776, -1, -1, false, false, false, false, utf8proc_sequences + 2863}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3094, false, -1, 7841, -1, 10980, -1, false, false, false, false, utf8proc_sequences + 3097}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3099, false, 7840, -1, 7840, 11040, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3102, false, -1, 7843, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3105}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3107, false, 7842, -1, 7842, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3110, false, -1, 7845, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3113}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3115, false, 7844, -1, 7844, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3118, false, -1, 7847, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3121}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3123, false, 7846, -1, 7846, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3126, false, -1, 7849, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3129}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3131, false, 7848, -1, 7848, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3134, false, -1, 7851, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3137}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3139, false, 7850, -1, 7850, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3142, false, -1, 7853, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3145}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3147, false, 7852, -1, 7852, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3150, false, -1, 7855, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3153}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3155, false, 7854, -1, 7854, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3158, false, -1, 7857, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3161}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3163, false, 7856, -1, 7856, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3166, false, -1, 7859, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3169}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3171, false, 7858, -1, 7858, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3174, false, -1, 7861, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3177}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3179, false, 7860, -1, 7860, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3182, false, -1, 7863, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3185}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3187, false, 7862, -1, 7862, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3190, false, -1, 7865, -1, 11340, -1, false, false, false, false, utf8proc_sequences + 3193}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3195, false, 7864, -1, 7864, 11400, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3198, false, -1, 7867, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3201}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3203, false, 7866, -1, 7866, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3206, false, -1, 7869, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3209}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3211, false, 7868, -1, 7868, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3214, false, -1, 7871, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3217}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3219, false, 7870, -1, 7870, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3222, false, -1, 7873, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3225}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3227, false, 7872, -1, 7872, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3230, false, -1, 7875, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3233}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3235, false, 7874, -1, 7874, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3238, false, -1, 7877, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3241}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3243, false, 7876, -1, 7876, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3246, false, -1, 7879, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3249}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3251, false, 7878, -1, 7878, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3254, false, -1, 7881, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3257}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3259, false, 7880, -1, 7880, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3262, false, -1, 7883, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3265}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3267, false, 7882, -1, 7882, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3270, false, -1, 7885, -1, 11580, -1, false, false, false, false, utf8proc_sequences + 3273}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3275, false, 7884, -1, 7884, 11640, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3278, false, -1, 7887, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3281}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3283, false, 7886, -1, 7886, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3286, false, -1, 7889, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3289}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3291, false, 7888, -1, 7888, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3294, false, -1, 7891, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3297}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3299, false, 7890, -1, 7890, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3302, false, -1, 7893, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3305}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3307, false, 7892, -1, 7892, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3310, false, -1, 7895, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3313}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3315, false, 7894, -1, 7894, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3318, false, -1, 7897, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3321}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3323, false, 7896, -1, 7896, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3326, false, -1, 7899, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3329}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3331, false, 7898, -1, 7898, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3334, false, -1, 7901, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3337}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3339, false, 7900, -1, 7900, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3342, false, -1, 7903, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3345}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3347, false, 7902, -1, 7902, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3350, false, -1, 7905, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3353}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3355, false, 7904, -1, 7904, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3358, false, -1, 7907, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3361}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3363, false, 7906, -1, 7906, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3366, false, -1, 7909, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3369}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3371, false, 7908, -1, 7908, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3374, false, -1, 7911, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3377}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3379, false, 7910, -1, 7910, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3382, false, -1, 7913, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3385}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3387, false, 7912, -1, 7912, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3390, false, -1, 7915, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3393}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3395, false, 7914, -1, 7914, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3398, false, -1, 7917, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3401}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3403, false, 7916, -1, 7916, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3406, false, -1, 7919, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3409}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3411, false, 7918, -1, 7918, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3414, false, -1, 7921, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3417}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3419, false, 7920, -1, 7920, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3422, false, -1, 7923, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3425}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3427, false, 7922, -1, 7922, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3430, false, -1, 7925, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3433}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3435, false, 7924, -1, 7924, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3438, false, -1, 7927, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3441}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3443, false, 7926, -1, 7926, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3446, false, -1, 7929, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3449}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3451, false, 7928, -1, 7928, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3454, false, 7944, -1, 7944, 11940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3457, false, 7945, -1, 7945, 12000, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3460, false, 7946, -1, 7946, 13560, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3463, false, 7947, -1, 7947, 13620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3466, false, 7948, -1, 7948, 13680, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3469, false, 7949, -1, 7949, 13740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3472, false, 7950, -1, 7950, 13800, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3475, false, 7951, -1, 7951, 13860, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3478, false, -1, 7936, -1, 12060, -1, false, false, false, false, utf8proc_sequences + 3481}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3483, false, -1, 7937, -1, 12120, -1, false, false, false, false, utf8proc_sequences + 3486}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3488, false, -1, 7938, -1, 13920, -1, false, false, false, false, utf8proc_sequences + 3491}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3493, false, -1, 7939, -1, 13980, -1, false, false, false, false, utf8proc_sequences + 3496}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3498, false, -1, 7940, -1, 14040, -1, false, false, false, false, utf8proc_sequences + 3501}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3503, false, -1, 7941, -1, 14100, -1, false, false, false, false, utf8proc_sequences + 3506}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3508, false, -1, 7942, -1, 14160, -1, false, false, false, false, utf8proc_sequences + 3511}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3513, false, -1, 7943, -1, 14220, -1, false, false, false, false, utf8proc_sequences + 3516}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3518, false, 7960, -1, 7960, 12180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3521, false, 7961, -1, 7961, 12240, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3524, false, 7962, -1, 7962, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3527, false, 7963, -1, 7963, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3530, false, 7964, -1, 7964, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3533, false, 7965, -1, 7965, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3536, false, -1, 7952, -1, 12300, -1, false, false, false, false, utf8proc_sequences + 3539}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3541, false, -1, 7953, -1, 12360, -1, false, false, false, false, utf8proc_sequences + 3544}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3546, false, -1, 7954, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3549}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3551, false, -1, 7955, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3554}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3556, false, -1, 7956, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3559}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3561, false, -1, 7957, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3564}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3566, false, 7976, -1, 7976, 12420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3569, false, 7977, -1, 7977, 12480, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3572, false, 7978, -1, 7978, 14280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3575, false, 7979, -1, 7979, 14340, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3578, false, 7980, -1, 7980, 14400, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3581, false, 7981, -1, 7981, 14460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3584, false, 7982, -1, 7982, 14520, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3587, false, 7983, -1, 7983, 14580, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3590, false, -1, 7968, -1, 12540, -1, false, false, false, false, utf8proc_sequences + 3593}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3595, false, -1, 7969, -1, 12600, -1, false, false, false, false, utf8proc_sequences + 3598}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3600, false, -1, 7970, -1, 14640, -1, false, false, false, false, utf8proc_sequences + 3603}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3605, false, -1, 7971, -1, 14700, -1, false, false, false, false, utf8proc_sequences + 3608}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3610, false, -1, 7972, -1, 14760, -1, false, false, false, false, utf8proc_sequences + 3613}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3615, false, -1, 7973, -1, 14820, -1, false, false, false, false, utf8proc_sequences + 3618}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3620, false, -1, 7974, -1, 14880, -1, false, false, false, false, utf8proc_sequences + 3623}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3625, false, -1, 7975, -1, 14940, -1, false, false, false, false, utf8proc_sequences + 3628}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3630, false, 7992, -1, 7992, 12660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3633, false, 7993, -1, 7993, 12720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3636, false, 7994, -1, 7994, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3639, false, 7995, -1, 7995, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3642, false, 7996, -1, 7996, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3645, false, 7997, -1, 7997, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3648, false, 7998, -1, 7998, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3651, false, 7999, -1, 7999, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3654, false, -1, 7984, -1, 12780, -1, false, false, false, false, utf8proc_sequences + 3657}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3659, false, -1, 7985, -1, 12840, -1, false, false, false, false, utf8proc_sequences + 3662}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3664, false, -1, 7986, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3667}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3669, false, -1, 7987, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3672}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3674, false, -1, 7988, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3677}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3679, false, -1, 7989, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3682}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3684, false, -1, 7990, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3687}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3689, false, -1, 7991, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3692}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3694, false, 8008, -1, 8008, 12900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3697, false, 8009, -1, 8009, 12960, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3700, false, 8010, -1, 8010, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3703, false, 8011, -1, 8011, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3706, false, 8012, -1, 8012, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3709, false, 8013, -1, 8013, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3712, false, -1, 8000, -1, 13020, -1, false, false, false, false, utf8proc_sequences + 3715}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3717, false, -1, 8001, -1, 13080, -1, false, false, false, false, utf8proc_sequences + 3720}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3722, false, -1, 8002, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3725}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3727, false, -1, 8003, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3730}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3732, false, -1, 8004, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3735}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3737, false, -1, 8005, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3740}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3742, false, -1, -1, -1, 13140, -1, false, false, false, false, utf8proc_sequences + 3742}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3745, false, 8025, -1, 8025, 13200, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3748, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3751}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3755, false, 8027, -1, 8027, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3758, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3761}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3765, false, 8029, -1, 8029, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3768, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3771}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3775, false, 8031, -1, 8031, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3778, false, -1, 8017, -1, 13260, -1, false, false, false, false, utf8proc_sequences + 3781}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3783, false, -1, 8019, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3786}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3788, false, -1, 8021, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3791}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3793, false, -1, 8023, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3796}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3798, false, 8040, -1, 8040, 13320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3801, false, 8041, -1, 8041, 13380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3804, false, 8042, -1, 8042, 15000, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3807, false, 8043, -1, 8043, 15060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3810, false, 8044, -1, 8044, 15120, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3813, false, 8045, -1, 8045, 15180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3816, false, 8046, -1, 8046, 15240, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3819, false, 8047, -1, 8047, 15300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3822, false, -1, 8032, -1, 13440, -1, false, false, false, false, utf8proc_sequences + 3825}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3827, false, -1, 8033, -1, 13500, -1, false, false, false, false, utf8proc_sequences + 3830}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3832, false, -1, 8034, -1, 15360, -1, false, false, false, false, utf8proc_sequences + 3835}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3837, false, -1, 8035, -1, 15420, -1, false, false, false, false, utf8proc_sequences + 3840}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3842, false, -1, 8036, -1, 15480, -1, false, false, false, false, utf8proc_sequences + 3845}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3847, false, -1, 8037, -1, 15540, -1, false, false, false, false, utf8proc_sequences + 3850}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3852, false, -1, 8038, -1, 15600, -1, false, false, false, false, utf8proc_sequences + 3855}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3857, false, -1, 8039, -1, 15660, -1, false, false, false, false, utf8proc_sequences + 3860}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3862, false, 8122, -1, 8122, 15720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1341, false, 8123, -1, 8123, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3865, false, 8136, -1, 8136, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1348, false, 8137, -1, 8137, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3868, false, 8138, -1, 8138, 15900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1353, false, 8139, -1, 8139, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3871, false, 8154, -1, 8154, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1358, false, 8155, -1, 8155, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3874, false, 8184, -1, 8184, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1363, false, 8185, -1, 8185, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3877, false, 8170, -1, 8170, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1368, false, 8171, -1, 8171, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3880, false, 8186, -1, 8186, 16320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1373, false, 8187, -1, 8187, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3883, false, 8072, -1, 8072, -1, -1, false, false, false, false, utf8proc_sequences + 3886}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3889, false, 8073, -1, 8073, -1, -1, false, false, false, false, utf8proc_sequences + 3892}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3895, false, 8074, -1, 8074, -1, -1, false, false, false, false, utf8proc_sequences + 3898}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3901, false, 8075, -1, 8075, -1, -1, false, false, false, false, utf8proc_sequences + 3904}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3907, false, 8076, -1, 8076, -1, -1, false, false, false, false, utf8proc_sequences + 3910}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3913, false, 8077, -1, 8077, -1, -1, false, false, false, false, utf8proc_sequences + 3916}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3919, false, 8078, -1, 8078, -1, -1, false, false, false, false, utf8proc_sequences + 3922}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3925, false, 8079, -1, 8079, -1, -1, false, false, false, false, utf8proc_sequences + 3928}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3931, false, -1, 8064, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3934}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3936, false, -1, 8065, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3939}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3941, false, -1, 8066, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3944}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3946, false, -1, 8067, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3949}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3951, false, -1, 8068, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3954}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3956, false, -1, 8069, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3959}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3961, false, -1, 8070, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3964}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3966, false, -1, 8071, -1, -1, -1, false, false, false, false, utf8proc_sequences + 3969}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3971, false, 8088, -1, 8088, -1, -1, false, false, false, false, utf8proc_sequences + 3974}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3977, false, 8089, -1, 8089, -1, -1, false, false, false, false, utf8proc_sequences + 3980}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3983, false, 8090, -1, 8090, -1, -1, false, false, false, false, utf8proc_sequences + 3986}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3989, false, 8091, -1, 8091, -1, -1, false, false, false, false, utf8proc_sequences + 3992}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 3995, false, 8092, -1, 8092, -1, -1, false, false, false, false, utf8proc_sequences + 3998}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4001, false, 8093, -1, 8093, -1, -1, false, false, false, false, utf8proc_sequences + 4004}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4007, false, 8094, -1, 8094, -1, -1, false, false, false, false, utf8proc_sequences + 4010}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4013, false, 8095, -1, 8095, -1, -1, false, false, false, false, utf8proc_sequences + 4016}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4019, false, -1, 8080, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4022}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4024, false, -1, 8081, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4027}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4029, false, -1, 8082, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4032}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4034, false, -1, 8083, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4037}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4039, false, -1, 8084, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4042}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4044, false, -1, 8085, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4047}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4049, false, -1, 8086, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4052}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4054, false, -1, 8087, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4057}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4059, false, 8104, -1, 8104, -1, -1, false, false, false, false, utf8proc_sequences + 4062}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4065, false, 8105, -1, 8105, -1, -1, false, false, false, false, utf8proc_sequences + 4068}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4071, false, 8106, -1, 8106, -1, -1, false, false, false, false, utf8proc_sequences + 4074}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4077, false, 8107, -1, 8107, -1, -1, false, false, false, false, utf8proc_sequences + 4080}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4083, false, 8108, -1, 8108, -1, -1, false, false, false, false, utf8proc_sequences + 4086}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4089, false, 8109, -1, 8109, -1, -1, false, false, false, false, utf8proc_sequences + 4092}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4095, false, 8110, -1, 8110, -1, -1, false, false, false, false, utf8proc_sequences + 4098}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4101, false, 8111, -1, 8111, -1, -1, false, false, false, false, utf8proc_sequences + 4104}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4107, false, -1, 8096, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4110}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4112, false, -1, 8097, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4115}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4117, false, -1, 8098, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4120}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4122, false, -1, 8099, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4125}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4127, false, -1, 8100, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4130}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4132, false, -1, 8101, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4135}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4137, false, -1, 8102, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4140}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4142, false, -1, 8103, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4145}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4147, false, 8120, -1, 8120, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4150, false, 8121, -1, 8121, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4153, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4156}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4159, false, 8124, -1, 8124, -1, -1, false, false, false, false, utf8proc_sequences + 4162}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4165, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4168}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4171, false, -1, -1, -1, 15840, -1, false, false, false, false, utf8proc_sequences + 4171}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4174, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4177}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4181, false, -1, 8112, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4184}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4186, false, -1, 8113, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4189}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4191, false, -1, 8048, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4194}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4196, false, -1, 8049, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4198}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4200, false, -1, 8115, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4203}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4205, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 1326, false, 921, -1, 921, -1, -1, false, false, false, false, utf8proc_sequences + 1326}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4205, false, -1, -1, -1, 16080, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4208, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4211, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4214, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4217}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4220, false, 8140, -1, 8140, -1, -1, false, false, false, false, utf8proc_sequences + 4223}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4226, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4229}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4232, false, -1, -1, -1, 16020, -1, false, false, false, false, utf8proc_sequences + 4232}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4235, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4238}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4242, false, -1, 8050, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4245}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4247, false, -1, 8051, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4249}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4251, false, -1, 8052, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4254}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4256, false, -1, 8053, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4258}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4260, false, -1, 8131, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4263}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4265, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4268, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4271, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4274, false, 8152, -1, 8152, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4277, false, 8153, -1, 8153, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4280, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4283}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4287, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1378}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4289, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4289}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4292, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4295}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4299, false, -1, 8144, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4302}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4304, false, -1, 8145, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4307}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4309, false, -1, 8054, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4312}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4314, false, -1, 8055, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4316}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4318, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4321, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4324, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4327, false, 8168, -1, 8168, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4330, false, 8169, -1, 8169, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4333, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4336}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4340, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1451}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4342, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4342}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4345, false, 8172, -1, 8172, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4348, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4348}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4351, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4354}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4358, false, -1, 8160, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4361}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4363, false, -1, 8161, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4366}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4368, false, -1, 8058, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4371}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4373, false, -1, 8059, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4375}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4377, false, -1, 8165, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4380}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4385, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4387, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4389, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4392}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4395, false, 8188, -1, 8188, -1, -1, false, false, false, false, utf8proc_sequences + 4398}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4401, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4404}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4407, false, -1, -1, -1, 16440, -1, false, false, false, false, utf8proc_sequences + 4407}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4410, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4413}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4417, false, -1, 8056, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4420}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4422, false, -1, 8057, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4424}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4426, false, -1, 8060, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4429}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4431, false, -1, 8061, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4433}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4435, false, -1, 8179, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4438}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4442, false, -1, -1, -1, 16140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, utf8proc_sequences + 4445, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, utf8proc_sequences + 4447, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 52, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_NOBREAK, utf8proc_sequences + 52, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, true, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NOBREAK, utf8proc_sequences + 4449, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4451, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4456, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4459, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZL, 0, UTF8PROC_BIDI_CLASS_WS, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_ZP, 0, UTF8PROC_BIDI_CLASS_B, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_LRE, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_RLE, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_PDF, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_LRO, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_RLO, 0, NULL, false, -1, -1, -1, -1, -1, false, true, true, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4463, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4466, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4473, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4477, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4480, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_CS, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4483, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4486, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4489, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4492, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 16, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4513, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4517, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4519, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 26, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 72, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 60, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 62, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4513, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4517, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 4519, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 0, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 8, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 28, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 46, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, utf8proc_sequences + 792, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4521, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4524, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4528, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4534, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4537, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4541, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4545, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4547, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 12, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2390, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 14, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 22, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2402, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4550, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2408, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4553, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4555, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4558, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 4562, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4565, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4567, false, -1, 969, -1, -1, -1, false, false, false, false, utf8proc_sequences + 1424}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 2396, false, -1, 107, -1, -1, -1, false, false, false, false, utf8proc_sequences + 20}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 4569, false, -1, 229, -1, -1, -1, false, false, false, false, utf8proc_sequences + 114}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2380, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4571, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 8526, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4573}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2400, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 28, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4577, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4579, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4581, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 16, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4583, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1408, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4587, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4589, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4591, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 6, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 18, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 8498, -1, 8498, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4593, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4597, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4601, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4605, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4609, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4613, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4617, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4621, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4625, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4629, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4633, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4637, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, utf8proc_sequences + 4641, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2392, false, -1, 8560, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4644}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4646, false, -1, 8561, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4649}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4651, false, -1, 8562, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4655}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4657, false, -1, 8563, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4660}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4662, false, -1, 8564, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4664}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4666, false, -1, 8565, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4669}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4671, false, -1, 8566, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4675}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4677, false, -1, 8567, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4682}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4684, false, -1, 8568, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4687}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4689, false, -1, 8569, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4691}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4693, false, -1, 8570, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4696}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4698, false, -1, 8571, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4702}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2398, false, -1, 8572, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4704}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4532, false, -1, 8573, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4706}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2382, false, -1, 8574, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4708}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 2400, false, -1, 8575, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4710}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 16, false, 8544, -1, 8544, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4712, false, 8545, -1, 8545, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4715, false, 8546, -1, 8546, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4719, false, 8547, -1, 8547, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 42, false, 8548, -1, 8548, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4722, false, 8549, -1, 8549, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4725, false, 8550, -1, 8550, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4729, false, 8551, -1, 8551, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4734, false, 8552, -1, 8552, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 46, false, 8553, -1, 8553, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4737, false, 8554, -1, 8554, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4740, false, 8555, -1, 8555, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 22, false, 8556, -1, 8556, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4, false, 8557, -1, 8557, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6, false, 8558, -1, 8558, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 24, false, 8559, -1, 8559, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 8580, -1, -1, -1, false, false, false, false, utf8proc_sequences + 4744}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 8579, -1, 8579, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16500, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16560, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4746, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4749, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4752, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4755, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4758, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4761, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16680, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16800, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 16740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 16860, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4764, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 16920, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4767, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 16980, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4770, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 17040, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4773, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 17100, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4776, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4779, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4782, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4786, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4789, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17160, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4793, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17220, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4796, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4799, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17340, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4802, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 17520, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4805, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 17460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4808, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17700, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17760, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4811, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4814, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4817, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4820, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4823, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17820, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17880, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4826, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4829, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 17940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18000, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4832, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4835, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18120, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4838, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4841, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18240, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4844, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4847, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18360, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4850, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4853, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18840, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18480, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18600, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4856, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4859, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4862, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4865, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 18960, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 19020, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, true, -1, -1, -1, 19080, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4868, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4871, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4874, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4877, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4880, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4883, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4886, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4889, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4892, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 4894, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 72, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 60, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 62, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4896, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4899, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4905, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4911, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4917, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4923, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4929, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4933, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4937, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4941, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4945, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4949, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4953, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4957, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4970, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4975, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4985, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4990, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 4995, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5000, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5005, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5010, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5015, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5020, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5023, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5026, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5032, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5051, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5055, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5063, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5067, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5075, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5079, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5083, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5111, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5147, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5151, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5155, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5159, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5163, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5167, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5171, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5175, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5183, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5187, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5191, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2376, false, -1, 9424, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5195}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2380, false, -1, 9425, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5197}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4532, false, -1, 9426, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5199}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2382, false, -1, 9427, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5201}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2384, false, -1, 9428, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5203}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4571, false, -1, 9429, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5205}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2388, false, -1, 9430, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5207}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2390, false, -1, 9431, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5209}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2392, false, -1, 9432, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5211}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2394, false, -1, 9433, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5213}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2396, false, -1, 9434, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5215}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2398, false, -1, 9435, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5217}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2400, false, -1, 9436, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5219}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2402, false, -1, 9437, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5221}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2404, false, -1, 9438, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5223}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2408, false, -1, 9439, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5225}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4553, false, -1, 9440, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5227}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2410, false, -1, 9441, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5229}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5231, false, -1, 9442, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5233}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2412, false, -1, 9443, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5235}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2414, false, -1, 9444, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5237}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4662, false, -1, 9445, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5239}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2416, false, -1, 9446, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5241}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4689, false, -1, 9447, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5243}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5245, false, -1, 9448, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5247}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4565, false, -1, 9449, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5249}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 0, false, 9398, -1, 9398, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 2, false, 9399, -1, 9399, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4, false, 9400, -1, 9400, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6, false, 9401, -1, 9401, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 8, false, 9402, -1, 9402, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 10, false, 9403, -1, 9403, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 12, false, 9404, -1, 9404, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 14, false, 9405, -1, 9405, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 16, false, 9406, -1, 9406, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 18, false, 9407, -1, 9407, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 20, false, 9408, -1, 9408, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 22, false, 9409, -1, 9409, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 24, false, 9410, -1, 9410, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 26, false, 9411, -1, 9411, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 28, false, 9412, -1, 9412, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 30, false, 9413, -1, 9413, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 32, false, 9414, -1, 9414, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 34, false, 9415, -1, 9415, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 36, false, 9416, -1, 9416, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 38, false, 9417, -1, 9417, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 40, false, 9418, -1, 9418, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 42, false, 9419, -1, 9419, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 44, false, 9420, -1, 9420, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 46, false, 9421, -1, 9421, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 48, false, 9422, -1, 9422, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 50, false, 9423, -1, 9423, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 4497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5251, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5256, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5260, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, utf8proc_sequences + 5267, true, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, 19140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11312, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5270}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11313, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5272}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11314, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5274}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11315, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5276}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11316, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5278}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11317, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5280}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11318, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5282}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11319, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5284}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11320, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5286}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11321, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5288}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11322, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5290}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11323, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5292}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11324, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5294}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11325, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5296}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11326, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5298}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11327, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5300}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11328, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5302}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11329, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5304}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11330, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5306}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11331, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5308}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11332, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5310}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11333, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5312}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11334, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5314}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11335, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5316}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11336, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5318}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11337, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5320}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11338, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5322}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11339, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5324}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11340, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5326}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11341, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5328}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11342, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5330}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11343, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5332}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11344, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5334}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11345, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5336}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11346, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5338}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11347, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5340}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11348, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5342}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11349, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5344}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11350, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5346}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11351, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5348}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11352, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5350}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11353, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5352}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11354, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5354}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11355, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5356}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11356, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5358}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11357, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5360}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11358, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5362}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11264, -1, 11264, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11265, -1, 11265, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11266, -1, 11266, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11267, -1, 11267, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11268, -1, 11268, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11269, -1, 11269, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11270, -1, 11270, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11271, -1, 11271, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11272, -1, 11272, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11273, -1, 11273, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11274, -1, 11274, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11275, -1, 11275, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11276, -1, 11276, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11277, -1, 11277, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11278, -1, 11278, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11279, -1, 11279, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11280, -1, 11280, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11281, -1, 11281, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11282, -1, 11282, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11283, -1, 11283, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11284, -1, 11284, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11285, -1, 11285, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11286, -1, 11286, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11287, -1, 11287, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11288, -1, 11288, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11289, -1, 11289, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11290, -1, 11290, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11291, -1, 11291, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11292, -1, 11292, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11293, -1, 11293, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11294, -1, 11294, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11295, -1, 11295, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11296, -1, 11296, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11297, -1, 11297, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11298, -1, 11298, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11299, -1, 11299, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11300, -1, 11300, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11301, -1, 11301, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11302, -1, 11302, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11303, -1, 11303, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11304, -1, 11304, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11305, -1, 11305, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11306, -1, 11306, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11307, -1, 11307, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11308, -1, 11308, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11309, -1, 11309, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11310, -1, 11310, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11361, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5364}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11360, -1, 11360, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 619, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5366}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 7549, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5368}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 637, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5370}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 570, -1, 570, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 574, -1, 574, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11368, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5372}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11367, -1, 11367, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11370, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5374}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11369, -1, 11369, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11372, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5376}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11371, -1, 11371, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11382, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5378}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11381, -1, 11381, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11393, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5380}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11392, -1, 11392, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11395, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5382}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11394, -1, 11394, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11397, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5384}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11396, -1, 11396, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11399, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5386}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11398, -1, 11398, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11401, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5388}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11400, -1, 11400, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11403, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5390}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11402, -1, 11402, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11405, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5392}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11404, -1, 11404, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11407, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5394}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11406, -1, 11406, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11409, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5396}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11408, -1, 11408, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11411, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5398}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11410, -1, 11410, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11413, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5400}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11412, -1, 11412, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11415, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5402}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11414, -1, 11414, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11417, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5404}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11416, -1, 11416, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11419, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5406}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11418, -1, 11418, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11421, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5408}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11420, -1, 11420, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11423, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5410}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11422, -1, 11422, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11425, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5412}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11424, -1, 11424, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11427, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5414}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11426, -1, 11426, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11429, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5416}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11428, -1, 11428, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11431, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5418}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11430, -1, 11430, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11433, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5420}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11432, -1, 11432, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11435, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5422}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11434, -1, 11434, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11437, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5424}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11436, -1, 11436, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11439, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5426}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11438, -1, 11438, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11441, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5428}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11440, -1, 11440, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11443, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5430}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11442, -1, 11442, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11445, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5432}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11444, -1, 11444, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11447, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5434}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11446, -1, 11446, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11449, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5436}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11448, -1, 11448, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11451, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5438}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11450, -1, 11450, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11453, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5440}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11452, -1, 11452, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11455, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5442}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11454, -1, 11454, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11457, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5444}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11456, -1, 11456, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11459, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5446}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11458, -1, 11458, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11461, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5448}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11460, -1, 11460, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11463, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5450}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11462, -1, 11462, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11465, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5452}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11464, -1, 11464, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11467, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5454}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11466, -1, 11466, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11469, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5456}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11468, -1, 11468, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11471, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5458}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11470, -1, 11470, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11473, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5460}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11472, -1, 11472, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11475, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5462}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11474, -1, 11474, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11477, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5464}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11476, -1, 11476, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11479, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5466}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11478, -1, 11478, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11481, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5468}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11480, -1, 11480, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11483, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5470}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11482, -1, 11482, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11485, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5472}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11484, -1, 11484, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11487, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5474}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11486, -1, 11486, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11489, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5476}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11488, -1, 11488, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 11491, -1, -1, -1, false, false, false, false, utf8proc_sequences + 5478}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 11490, -1, 11490, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4256, -1, 4256, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4257, -1, 4257, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4258, -1, 4258, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4259, -1, 4259, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4260, -1, 4260, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4261, -1, 4261, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4262, -1, 4262, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4263, -1, 4263, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4264, -1, 4264, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4265, -1, 4265, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4266, -1, 4266, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4267, -1, 4267, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4268, -1, 4268, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4269, -1, 4269, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4270, -1, 4270, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4271, -1, 4271, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4272, -1, 4272, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4273, -1, 4273, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4274, -1, 4274, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4275, -1, 4275, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4276, -1, 4276, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4277, -1, 4277, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4278, -1, 4278, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4279, -1, 4279, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4280, -1, 4280, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4281, -1, 4281, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4282, -1, 4282, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4283, -1, 4283, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4284, -1, 4284, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4285, -1, 4285, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4286, -1, 4286, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4287, -1, 4287, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4288, -1, 4288, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4289, -1, 4289, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4290, -1, 4290, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4291, -1, 4291, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4292, -1, 4292, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 4293, -1, 4293, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 5480, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5484, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5486, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5490, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5492, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5494, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5496, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5498, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5500, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5502, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5504, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5506, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5510, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5512, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5514, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5516, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5518, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5520, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5522, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5524, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5526, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5528, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5530, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5534, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5536, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5538, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5540, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5542, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5544, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5546, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5548, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5550, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5552, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5554, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5556, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5558, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5560, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5562, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5564, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5566, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5568, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5570, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5572, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5574, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5576, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5578, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5580, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5582, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5584, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5586, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5588, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5590, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5592, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5594, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5596, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5598, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5600, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5602, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5604, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5606, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5608, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5610, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5612, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5614, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5616, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5618, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5620, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5622, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5624, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5626, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5628, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5630, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5632, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5634, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5636, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5638, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5640, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5642, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5644, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5646, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5648, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5650, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5652, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5654, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5656, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5658, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5660, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5662, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5664, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5666, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5668, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5670, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5672, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5674, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5676, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5678, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5680, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5682, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5684, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5686, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5688, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5690, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5692, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5694, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5696, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5698, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5700, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5702, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5704, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5706, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5708, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5710, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5712, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5714, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5716, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5718, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5720, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5722, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5724, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5726, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5728, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5730, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5732, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5734, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5736, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5738, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5740, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5742, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5744, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5746, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5748, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5750, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5752, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5754, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5756, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5758, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5760, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5762, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5764, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5766, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5768, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5770, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5772, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5774, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5776, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5778, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5780, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5782, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5784, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5786, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5788, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5790, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5792, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5794, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5796, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5798, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5800, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5802, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5804, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5806, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5808, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5810, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5812, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5814, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5816, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5818, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5820, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5822, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5824, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5826, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5828, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5830, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5832, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5834, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5836, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5838, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5840, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5842, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5844, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5846, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5848, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5850, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5852, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5854, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5856, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5858, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5860, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5862, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5864, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5866, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5868, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5870, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5872, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5874, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5876, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5878, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5880, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5882, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5884, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5886, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5888, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5890, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5892, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5894, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5896, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5898, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5900, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 52, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 218, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 224, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20400, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19200, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5923, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19380, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5929, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19440, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5932, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19500, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5935, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19560, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5938, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19620, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5941, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19680, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19740, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5947, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19800, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19860, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5953, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19920, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 19980, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20040, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20100, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5968, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20160, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5971, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5974, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20220, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5977, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20280, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5983, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5986, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20340, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5989, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5992, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5995, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 8, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 52, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MN, 8, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, 53, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 5998, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6001, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20460, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6004, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 6007, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21720, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20520, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6010, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20580, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6013, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20640, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6016, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20700, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6019, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20760, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6022, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20820, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6025, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20880, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6028, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 20940, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6031, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21000, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6034, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21060, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6037, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21120, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6040, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21180, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6043, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21240, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6046, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21300, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6049, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21360, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6052, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21420, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6055, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6058, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21480, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6061, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6064, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21540, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6067, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6070, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21600, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6073, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6076, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21660, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6079, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6082, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21780, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21840, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21900, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 21960, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6085, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6088, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6094, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 22020, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6100, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 6103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6106, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6108, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6110, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6112, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6114, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6116, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6118, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6120, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6124, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6126, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6128, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6130, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6132, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6134, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6136, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6138, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6142, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6144, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6148, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6152, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6154, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6156, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6160, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6162, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6166, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6168, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6170, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6172, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6174, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6176, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6178, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6180, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6184, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6186, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6188, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6190, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6192, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6194, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6196, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6198, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6202, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6204, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6206, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6208, false, -1, -1, -1, -1, -1, false, true, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6210, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6212, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6214, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6216, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6218, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6220, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6222, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6224, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6226, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6228, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6230, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6232, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6234, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6236, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6238, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6240, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6242, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6244, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6246, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6248, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6250, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6252, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6254, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6256, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6258, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6260, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6262, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6264, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6266, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6268, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6270, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6272, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6274, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6276, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6278, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6280, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6282, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6284, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6286, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6288, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6290, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6292, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 5486, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 5498, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6294, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6296, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6298, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6300, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6302, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6304, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 5494, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6306, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6308, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6310, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 6312, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, utf8proc_sequences + 5502, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6314, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6318, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6322, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6326, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6330, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6334, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6338, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6342, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6346, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6350, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6354, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6358, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6362, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6366, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6370, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6375, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6380, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6385, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6390, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6395, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6400, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6405, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6415, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6425, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6430, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6435, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6445, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6453, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6460, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6464, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6468, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6472, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6476, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6480, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6484, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6492, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6496, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6500, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6504, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6512, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6516, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6520, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6524, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6528, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6536, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6540, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6544, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6548, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6552, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6556, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6560, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6564, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6568, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6572, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6576, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6580, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6584, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6588, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6592, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6596, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6600, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6604, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6608, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6611, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6614, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6617, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6620, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6623, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6626, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6629, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6632, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6635, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6638, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6641, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6644, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6647, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6650, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6106, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6112, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6118, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6138, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6152, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6156, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6160, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6162, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6653, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6656, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6659, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6662, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6665, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6668, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6671, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6674, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6677, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6680, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6683, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6686, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6689, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6692, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6695, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6701, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6706, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5486, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5498, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6294, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6296, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6709, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6711, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6713, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6715, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5632, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5656, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5654, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5634, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5818, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5548, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5628, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6717, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6719, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6721, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6723, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6725, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6727, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6729, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6731, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6733, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6735, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 5560, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6737, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6739, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6741, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6743, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6745, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6747, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6749, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6751, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6298, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6300, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6302, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6753, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6755, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6757, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6759, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6761, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6763, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6765, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6767, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6769, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6771, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6773, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6776, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6779, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6782, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6785, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6788, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6791, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6794, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6797, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6800, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6803, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6806, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6809, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6812, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6815, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6818, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6821, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6824, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6827, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6830, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6833, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6836, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6839, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6842, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6845, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6849, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 6853, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6857, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6860, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6864, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6867, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6871, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6873, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6875, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6877, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6879, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6881, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6883, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6885, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6887, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6889, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6891, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6893, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6895, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6897, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6899, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6901, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6903, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6905, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6907, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6909, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6911, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6913, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6915, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6917, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6919, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6921, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6923, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6925, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6927, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6929, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6931, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6933, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6935, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6937, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6939, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6941, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6943, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6945, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6947, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6949, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6951, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6953, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6955, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6957, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, utf8proc_sequences + 6963, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6970, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6975, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6984, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6989, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6993, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 6997, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7008, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7012, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7016, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7020, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7025, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7030, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7034, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7045, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7055, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7058, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7064, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7077, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7081, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7093, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7098, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7102, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7106, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7110, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7126, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7130, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7134, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7138, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7144, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7147, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7154, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7168, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7173, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7183, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7186, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7189, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7195, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7206, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7210, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7216, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7219, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7223, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7227, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7231, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7235, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7244, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7248, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7251, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7255, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7259, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7268, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7272, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7276, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7280, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7286, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7291, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7294, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7300, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7303, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7308, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7313, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7317, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7321, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7325, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7330, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7337, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7342, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7345, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7351, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7355, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7358, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7361, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7364, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7367, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7370, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7373, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7376, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7379, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7385, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7389, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7393, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7397, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7405, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7409, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7413, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7417, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7421, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7425, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7429, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7433, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7437, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7441, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7445, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7449, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7452, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7455, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7459, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7462, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7465, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7468, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7472, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7476, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7491, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7496, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7502, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7514, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7517, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7520, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7524, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7529, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7535, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7538, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7541, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7544, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7547, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7550, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7554, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7558, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7562, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7566, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7569, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7572, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7578, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7581, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7584, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7587, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7590, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7593, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7596, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7600, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7604, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7607, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7611, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7615, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7619, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7622, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7626, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7630, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7635, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7638, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7642, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7646, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7650, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7654, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7660, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7667, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7670, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7673, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7676, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7679, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7682, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7685, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7688, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7691, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7694, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7697, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7700, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7703, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7706, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7709, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7712, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7715, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7718, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7721, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7726, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7729, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7732, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7735, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7740, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7744, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7747, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7750, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7753, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7756, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7759, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7762, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7765, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7768, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7771, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7774, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7778, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7781, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7784, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7788, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7792, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7795, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7800, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7804, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7807, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7810, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7813, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7816, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7820, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7824, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7827, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7830, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7833, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7836, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7839, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7842, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7845, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7848, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7851, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7855, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7859, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7863, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7867, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7871, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7875, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7879, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7883, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7887, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7891, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7895, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7899, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7903, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7907, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7911, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7915, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7919, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7923, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7927, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7931, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 7935, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, utf8proc_sequences + 7939, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CS, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, true, false, false, NULL}, + {UTF8PROC_CATEGORY_CO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7943, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7945, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5802, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7947, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7949, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7951, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7953, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7955, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5818, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7957, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7963, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7967, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7969, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7971, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7973, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7975, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7977, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7979, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7981, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7983, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7985, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7987, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7989, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7991, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7993, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7995, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7997, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 7999, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8001, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8005, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8007, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8009, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8011, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8013, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8015, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8017, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8019, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8021, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8023, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8025, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8027, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8031, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8033, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8037, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5734, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8039, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8043, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8045, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8049, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8051, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8053, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8055, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8057, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5880, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8061, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8063, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8065, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8067, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8069, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8073, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8075, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8077, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8079, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8081, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8083, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8085, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8093, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8105, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8109, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8111, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8117, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8129, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8133, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8145, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8147, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8149, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8151, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8153, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5806, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8155, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8157, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8159, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8161, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8163, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8165, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8167, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8169, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8171, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8173, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8175, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8177, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8181, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8183, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5560, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8187, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8189, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8191, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8193, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8195, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8197, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8199, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5522, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8201, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8205, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8207, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8209, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8211, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8213, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8215, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8217, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8219, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8221, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8223, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8225, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8227, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8229, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8231, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8233, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8235, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8237, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8241, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8243, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8245, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8247, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8249, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8251, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8253, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8255, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8257, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8259, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8261, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8265, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8267, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8271, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8273, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8275, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8277, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8279, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8281, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8283, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8285, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8287, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8289, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8291, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8293, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8295, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8297, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8299, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8301, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8303, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8305, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8307, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8309, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8311, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8313, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8315, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8317, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8319, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8321, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8323, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8325, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8327, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8329, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8331, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6711, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8335, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8337, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8339, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8341, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8343, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8345, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8347, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8349, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8351, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8353, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8355, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8357, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8359, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8361, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8363, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8365, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8367, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8369, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8371, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8373, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8375, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5816, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8377, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8379, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8381, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8383, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8385, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8387, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8389, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8391, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8393, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8395, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8397, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8399, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5718, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8403, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8405, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8407, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8409, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8411, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8413, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8415, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8417, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8419, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8421, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8423, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8425, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8427, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8429, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8431, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8433, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5772, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8435, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5778, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8437, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8439, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8441, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8443, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8445, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8447, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8449, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8451, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8453, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8455, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8457, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8459, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8461, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8463, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5732, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8465, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8467, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8469, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8471, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8473, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8475, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8477, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8481, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8483, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8487, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8489, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8491, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8493, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8495, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5574, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8513, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8517, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8519, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8521, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8523, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8525, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8527, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8529, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8531, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8533, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6721, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8535, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8537, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8539, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8541, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 6729, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8543, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8545, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8547, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8549, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8551, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8553, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8555, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8557, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8559, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8561, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8563, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8565, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8567, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8569, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8571, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8573, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8577, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8579, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8581, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8583, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8585, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8587, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8589, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8591, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8593, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8595, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8597, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8599, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8601, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8603, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8605, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8607, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8609, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8611, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8613, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8615, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8617, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8619, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8621, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8623, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8625, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8627, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8629, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8631, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8633, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8635, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8637, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8639, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8641, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8643, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8645, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8647, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5640, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8649, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8651, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8653, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8655, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8657, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8659, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8661, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8663, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8665, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8667, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8669, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8671, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8673, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8675, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8677, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8679, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8681, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8683, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8685, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8687, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8689, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8691, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8693, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8695, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8697, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8699, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8701, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8703, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8705, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8707, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8709, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8711, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8713, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8715, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8717, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8719, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8721, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8723, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8725, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8727, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8729, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8731, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8733, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8735, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8737, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8739, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8741, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 8743, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8745, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8745}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8748, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8748}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8751, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8751}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8754, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8754}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8758, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8758}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8762, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8765}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8765, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8765}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8768, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8768}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8771, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8771}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8774, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8774}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8777, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8777}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8780, false, -1, -1, -1, -1, -1, false, false, false, false, utf8proc_sequences + 8780}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8783, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MN, 26, UTF8PROC_BIDI_CLASS_NSM, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8786, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8789, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4581, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8791, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8793, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8795, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8797, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8799, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 8801, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8803, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8806, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8809, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8812, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8815, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8818, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8821, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8824, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8827, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8830, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8833, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8836, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8839, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8842, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8845, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8848, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8851, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8854, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8857, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8860, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8863, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8866, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8869, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8872, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8875, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8878, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8881, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8884, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8887, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8890, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8893, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, utf8proc_sequences + 8896, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 8899, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8924, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8924, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8924, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8924, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8928, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8928, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8930, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8930, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8932, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8932, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8934, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8934, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8936, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8936, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8938, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8938, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8940, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8940, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8940, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8940, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8942, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8942, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8942, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8942, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8946, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8946, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8946, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8946, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8948, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8948, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8952, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8952, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8958, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8958, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8960, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8960, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8964, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8964, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8966, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8966, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8968, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8968, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8970, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8972, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8972, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8974, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8974, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8976, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8976, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8978, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8978, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8978, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8978, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 8980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 8980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8982, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8982, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8985, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8985, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8988, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8988, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8991, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8991, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8994, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8994, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8997, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8997, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9000, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9000, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9000, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9006, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9006, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9006, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9006, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9008, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9011, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9014, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9017, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9020, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9023, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9026, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9032, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9053, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9056, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9062, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9065, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9068, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9074, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9077, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9080, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9083, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9086, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9092, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9098, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9104, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9110, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9116, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9128, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9134, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9149, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9152, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9155, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9161, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9167, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9170, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9173, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9176, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9188, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9191, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9194, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9197, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9206, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9209, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9212, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9215, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9218, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9221, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9224, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9227, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9230, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9233, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9236, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9242, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9245, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9248, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9251, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9254, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9257, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9260, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9266, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9272, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9275, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9278, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9281, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9284, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9287, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9291, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9295, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9299, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9303, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9307, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9311, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9314, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9014, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9317, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9017, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9320, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9323, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9326, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9032, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9329, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9332, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9335, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9053, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9338, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9341, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9344, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9062, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9065, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9152, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9155, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9167, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9170, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9188, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9191, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9206, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9209, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9347, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9221, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9350, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9353, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9356, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9242, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9245, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9284, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9359, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9362, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9365, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9272, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9275, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9008, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9011, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9368, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9014, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9371, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9020, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9023, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9026, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9374, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9377, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9068, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9074, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9077, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9080, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9086, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9092, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9098, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9380, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9104, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9110, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9116, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9128, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9134, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9149, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9161, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9173, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9176, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9179, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9194, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9197, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9383, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9212, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9215, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9218, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9221, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9230, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9233, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9236, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9248, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9251, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9389, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9260, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9266, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9014, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9371, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9374, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9377, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9395, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9098, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9185, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9203, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9407, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9411, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9415, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9419, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9422, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9425, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9428, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9431, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9434, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9437, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9443, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9446, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9449, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9452, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9455, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9458, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9461, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9464, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9467, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9473, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9476, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9491, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9494, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9419, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9422, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9425, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9428, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9431, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9434, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9437, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9443, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9446, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9449, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9452, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9455, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9458, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9461, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9464, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9467, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9473, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9476, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9491, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9494, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9401, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9092, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9479, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9485, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 9125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9500, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9500, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9519, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9523, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9527, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9531, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9531, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9535, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9539, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9543, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9547, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9551, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9555, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9555, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9559, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9563, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9563, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9567, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9567, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9571, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9575, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9579, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9583, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9583, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9587, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9587, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9591, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9595, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9595, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9599, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9599, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9603, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9607, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9611, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9615, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9615, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9619, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9623, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9627, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9631, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9635, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9635, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9639, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9643, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9647, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9651, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9655, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9659, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9659, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9663, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9663, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9667, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9667, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9671, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9675, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9679, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9683, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9687, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9691, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9695, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9699, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9703, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9707, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9711, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9715, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9719, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9719, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9723, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9727, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9731, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9735, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9735, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9739, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9743, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9747, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9751, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9755, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9759, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9763, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9767, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9771, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9775, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9779, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9783, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9787, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9791, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9795, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9799, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9803, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9807, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9811, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9815, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9819, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9823, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9639, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9647, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9827, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9831, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9835, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9839, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9843, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9847, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9843, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9835, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9851, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9855, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9859, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9863, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9867, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9847, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9611, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 9571, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9871, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 9875, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9879, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9883, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9887, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9892, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9897, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9907, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9917, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9945, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 9954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9963, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 1333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9967, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9969, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9971, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9973, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9975, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9977, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9979, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9981, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9983, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 4517, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 4519, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9985, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9987, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9989, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9991, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9993, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9995, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9997, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 9999, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 4892, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 4894, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10001, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10003, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10005, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10007, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10009, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, utf8proc_sequences + 10011, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 10013, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, utf8proc_sequences + 9983, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 4454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 1333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9969, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9967, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9979, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 4517, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 4519, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9985, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9987, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9989, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 9991, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10015, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10017, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10019, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 4511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10021, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10023, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10025, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 4515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10027, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10031, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, utf8proc_sequences + 10033, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10035, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10041, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10047, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10053, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10056, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10059, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10062, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10065, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10068, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10071, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10074, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10077, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10079, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10079, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10081, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10081, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10083, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10083, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10085, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10085, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10087, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10089, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10091, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10093, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10093, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10095, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10097, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10099, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10101, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10103, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10105, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10105, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10107, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10109, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10109, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10111, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10111, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10113, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10115, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10117, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10117, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10117, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10117, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10119, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10121, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10123, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10125, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10127, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10129, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10129, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10129, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10129, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10131, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10133, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10133, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10133, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10133, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10135, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10137, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10139, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10141, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10143, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 8980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 8980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10145, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10145, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, utf8proc_sequences + 10145, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, utf8proc_sequences + 10145, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10147, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10147, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10153, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10153, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, utf8proc_sequences + 10156, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, utf8proc_sequences + 10156, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9967, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10159, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10015, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10029, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10031, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10017, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10161, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4517, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4519, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10019, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4511, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9959, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10021, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10163, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 72, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 60, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 62, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9965, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 1333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10023, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4515, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10025, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9969, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10033, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2376, false, -1, 65345, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10165}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2380, false, -1, 65346, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10167}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4532, false, -1, 65347, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10169}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2382, false, -1, 65348, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10171}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2384, false, -1, 65349, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10173}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4571, false, -1, 65350, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10175}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2388, false, -1, 65351, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10177}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2390, false, -1, 65352, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10179}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2392, false, -1, 65353, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10181}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2394, false, -1, 65354, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10183}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2396, false, -1, 65355, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10185}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2398, false, -1, 65356, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10187}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2400, false, -1, 65357, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10189}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2402, false, -1, 65358, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10191}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2404, false, -1, 65359, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10193}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2408, false, -1, 65360, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10195}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4553, false, -1, 65361, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10197}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2410, false, -1, 65362, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10199}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 5231, false, -1, 65363, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10201}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2412, false, -1, 65364, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10203}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2414, false, -1, 65365, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10205}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4662, false, -1, 65366, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10207}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2416, false, -1, 65367, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10209}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4689, false, -1, 65368, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10211}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 5245, false, -1, 65369, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10213}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4565, false, -1, 65370, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10215}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10009, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10027, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10011, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10217, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9983, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4387, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 0, false, 65313, -1, 65313, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 2, false, 65314, -1, 65314, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 4, false, 65315, -1, 65315, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 6, false, 65316, -1, 65316, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 8, false, 65317, -1, 65317, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10, false, 65318, -1, 65318, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 12, false, 65319, -1, 65319, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 14, false, 65320, -1, 65320, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 16, false, 65321, -1, 65321, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 18, false, 65322, -1, 65322, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 20, false, 65323, -1, 65323, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 22, false, 65324, -1, 65324, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 24, false, 65325, -1, 65325, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 26, false, 65326, -1, 65326, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 28, false, 65327, -1, 65327, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 30, false, 65328, -1, 65328, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 32, false, 65329, -1, 65329, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 34, false, 65330, -1, 65330, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 36, false, 65331, -1, 65331, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 38, false, 65332, -1, 65332, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 40, false, 65333, -1, 65333, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 42, false, 65334, -1, 65334, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 44, false, 65335, -1, 65335, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 46, false, 65336, -1, 65336, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 48, false, 65337, -1, 65337, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 50, false, 65338, -1, 65338, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9985, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10219, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 9987, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10221, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10223, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10225, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 9963, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10001, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10003, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 9961, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10227, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6963, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10229, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10231, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10233, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10235, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10237, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10239, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10241, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10243, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10245, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10247, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6871, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6873, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6875, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6877, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6879, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6881, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6883, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6885, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6887, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6889, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6891, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6893, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6895, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6897, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6899, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6901, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6903, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6905, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6907, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6909, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6911, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6913, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6915, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6917, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6919, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6921, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6923, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6925, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6927, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6929, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6931, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6933, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6935, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6937, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6939, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6941, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6943, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6945, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6947, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6949, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6951, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6953, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6955, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 6957, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10249, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10251, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10253, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10255, false, -1, -1, -1, -1, -1, false, true, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10257, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10259, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10261, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10263, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10265, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10267, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10269, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10271, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10273, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10275, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10277, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10279, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10281, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10283, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10285, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10287, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10289, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10291, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10293, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10295, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10297, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10299, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10301, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10303, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10305, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10307, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10309, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10311, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10313, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10315, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10317, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10319, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10321, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10323, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10325, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10327, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10329, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10331, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10333, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10335, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10337, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10339, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10341, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10343, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10345, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10347, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10349, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10351, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10353, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10355, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10357, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10359, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10361, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10363, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10365, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10367, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10369, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, utf8proc_sequences + 10371, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10373, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10375, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10377, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10379, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10381, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10383, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, utf8proc_sequences + 10385, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, true, false, NULL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_ON, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66600, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10387}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66601, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10389}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66602, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10391}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66603, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10393}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66604, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10395}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66605, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10397}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66606, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10399}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66607, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10401}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66608, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10403}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66609, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10405}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66610, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10407}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66611, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10409}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66612, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10411}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66613, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10413}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66614, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10415}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66615, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10417}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66616, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10419}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66617, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10421}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66618, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10423}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66619, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10425}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66620, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10427}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66621, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10429}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66622, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10431}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66623, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10433}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66624, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10435}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66625, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10437}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66626, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10439}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66627, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10441}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66628, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10443}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66629, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10445}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66630, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10447}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66631, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10449}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66632, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10451}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66633, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10453}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66634, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10455}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66635, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10457}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66636, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10459}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66637, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10461}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66638, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10463}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, 66639, -1, -1, -1, false, false, false, false, utf8proc_sequences + 10465}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66560, -1, 66560, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66561, -1, 66561, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66562, -1, 66562, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66563, -1, 66563, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66564, -1, 66564, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66565, -1, 66565, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66566, -1, 66566, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66567, -1, 66567, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66568, -1, 66568, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66569, -1, 66569, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66570, -1, 66570, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66571, -1, 66571, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66572, -1, 66572, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66573, -1, 66573, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66574, -1, 66574, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66575, -1, 66575, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66576, -1, 66576, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66577, -1, 66577, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66578, -1, 66578, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66579, -1, 66579, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66580, -1, 66580, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66581, -1, 66581, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66582, -1, 66582, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66583, -1, 66583, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66584, -1, 66584, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66585, -1, 66585, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66586, -1, 66586, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66587, -1, 66587, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66588, -1, 66588, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66589, -1, 66589, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66590, -1, 66590, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66591, -1, 66591, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66592, -1, 66592, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66593, -1, 66593, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66594, -1, 66594, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66595, -1, 66595, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66596, -1, 66596, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66597, -1, 66597, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66598, -1, 66598, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, 66599, -1, 66599, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_R, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 22080, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 22140, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10467, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10470, false, -1, -1, -1, 22200, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10473, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10476, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10479, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10482, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10485, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 54, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 226, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 55, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 56, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 57, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 58, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, -1, 59, false, false, false, true, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 22260, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, NULL, false, -1, -1, -1, 22320, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10488, false, -1, -1, -1, 22380, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10491, false, -1, -1, -1, 22440, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10494, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10497, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10500, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10503, false, -1, -1, -1, -1, -1, true, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2376, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2388, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2394, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2396, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 5231, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2412, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2414, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4662, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2416, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4689, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 5245, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 0, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 2, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 20, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 24, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 26, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 30, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 32, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 34, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 36, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 38, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 40, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 42, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 44, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 46, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 48, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 50, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10506, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10510, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10512, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10514, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10516, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10518, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10520, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1504, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10522, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10524, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10526, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10528, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10530, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10532, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10534, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10536, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10538, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1508, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10540, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10542, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10544, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10546, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4567, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10548, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1388, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1390, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1394, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1396, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1326, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1400, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 67, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1402, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1406, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1502, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1412, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1414, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1416, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1418, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1422, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1424, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10550, true, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10552, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10554, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10556, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10558, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10560, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10562, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 10564, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 1482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4497, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 72, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 60, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 62, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4499, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4501, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4503, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4505, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4507, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, utf8proc_sequences + 4509, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10566, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10568, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10570, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10572, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10574, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10576, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10578, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10580, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10582, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10584, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10586, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10588, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10590, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10592, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10594, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10596, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10598, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10600, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10602, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10604, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10606, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10608, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10610, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10612, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10614, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5518, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10616, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10618, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10620, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10622, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10624, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10626, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10628, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10630, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10632, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10634, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10636, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10638, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10640, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10642, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10644, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10646, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10648, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10650, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10652, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10654, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10656, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10658, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10660, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10662, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10664, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10666, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10668, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10670, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10672, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10674, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10676, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10678, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10680, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10682, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10684, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10686, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10688, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10690, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10692, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10694, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10696, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10698, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10700, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10702, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10704, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10706, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10708, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10710, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10712, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10714, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10716, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10718, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10720, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10722, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10724, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10726, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10728, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10730, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10732, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10734, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10736, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10738, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10740, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10742, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10744, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10746, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10748, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10750, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10752, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10754, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10756, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10758, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10760, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5570, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10762, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10764, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10766, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10768, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10770, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10772, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10774, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10776, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10778, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10780, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10782, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10784, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10786, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10788, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10790, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10792, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10794, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10796, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10798, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10800, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10802, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10804, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10806, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10808, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5594, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10810, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10812, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10814, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10816, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10818, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10820, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10822, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10824, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10826, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10828, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10830, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10832, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10834, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10836, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10838, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10840, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10842, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10844, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10846, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10848, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10850, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10852, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10854, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10856, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10858, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10860, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10862, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10864, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10866, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10868, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10870, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10872, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10874, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10876, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10878, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10880, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10882, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10884, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10886, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10888, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10890, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10892, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10894, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10896, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10898, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10900, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10904, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10906, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10908, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10910, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10912, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10914, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10916, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10918, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10920, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10922, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10924, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10926, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10928, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10930, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10932, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10934, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10936, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10938, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10940, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10942, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10944, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10946, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10948, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10950, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10952, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10954, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10956, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10958, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10960, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10962, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10964, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10966, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10968, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10970, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10972, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10974, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10976, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10978, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10980, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10982, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10984, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10986, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10988, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10990, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10992, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10994, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10996, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 10998, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11000, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11002, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11004, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11006, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11008, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11010, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11012, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11014, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11016, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11018, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11020, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11022, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11024, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11026, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11028, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11030, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11032, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11034, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11036, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11038, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11040, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11042, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11044, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11046, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11048, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11050, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11052, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11054, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11056, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11058, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11060, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11062, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11064, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11066, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11068, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11070, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11072, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11074, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11076, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11078, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11080, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11082, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11084, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11086, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11088, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11090, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11092, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11094, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11096, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11098, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11100, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11102, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11104, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11106, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11108, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11110, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11112, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11114, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11116, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11118, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11120, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11122, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11124, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11126, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11128, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11130, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11132, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11134, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11136, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11138, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11140, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11142, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11144, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11146, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11148, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11150, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11152, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11154, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11156, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11158, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11160, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11162, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11164, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11166, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11168, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11170, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11172, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11174, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11176, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11178, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11180, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11182, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11184, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11186, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11188, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11190, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11192, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11194, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11196, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11198, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11200, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11202, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11204, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11206, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11208, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11210, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11212, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11214, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11216, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11218, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11220, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11222, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11224, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11226, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11228, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11230, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11232, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11234, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11236, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11238, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11240, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11242, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11244, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11246, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11248, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11250, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11252, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11254, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11256, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11258, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11260, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11262, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11264, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11266, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11268, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11270, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11272, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11274, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11276, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11278, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11280, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11282, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11284, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11286, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11288, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11290, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11292, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11294, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11296, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11298, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11300, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11302, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11304, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11306, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11308, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11310, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11312, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11314, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11316, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11318, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11320, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11322, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11324, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11326, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11328, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11330, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11332, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11334, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11336, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11338, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11340, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11342, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11344, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5774, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11346, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11348, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11350, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11352, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11354, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11356, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11358, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11360, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11362, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11364, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11366, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5788, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11368, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11370, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11372, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11374, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11376, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11378, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11380, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11382, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11384, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11386, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11388, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11390, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11392, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11394, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11396, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11398, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11400, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11402, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11404, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11406, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11408, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11410, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11412, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11414, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11416, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11418, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11420, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11422, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11424, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11426, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11428, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11430, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11432, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11434, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11436, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11438, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11440, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11442, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11444, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11446, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11448, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11450, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11452, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11454, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11456, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11458, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11460, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11462, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11464, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11466, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11468, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11470, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11472, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11474, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11476, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11478, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11480, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11482, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11484, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11486, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11488, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11490, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5884, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11492, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5892, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11494, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11496, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11498, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11500, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 5902, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, utf8proc_sequences + 11502, false, -1, -1, -1, -1, -1, false, false, false, false, NULL}, +}; + +UTF8PROC_DATA +const int32_t utf8proc_combinations[] = { + 192, 193, 194, 195, 196, 197, -1, + 256, 258, 260, 550, 461, -1, -1, 512, + 514, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7680, 7840, -1, -1, -1, -1, -1, 7842, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 262, 264, + -1, -1, -1, 199, -1, -1, -1, 266, + 268, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 200, 201, 202, 7868, 203, -1, 552, + 274, 276, 280, 278, 282, -1, -1, 516, + 518, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7864, -1, 7704, 7706, -1, -1, 7866, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 204, 205, 206, + 296, 207, -1, -1, 298, 300, 302, 304, + 463, -1, -1, 520, 522, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7882, -1, -1, + 7724, -1, -1, 7880, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 504, 323, -1, 209, -1, -1, 325, + -1, -1, -1, 7748, 327, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7750, 7752, 7754, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 210, 211, 212, + 213, 214, -1, -1, 332, 334, 490, 558, + 465, 336, 416, 524, 526, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7884, -1, -1, + -1, -1, -1, 7886, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 217, 218, 219, 360, 220, 366, -1, + 362, 364, 370, -1, 467, 368, 431, 532, + 534, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7908, -1, 7798, 7796, -1, 7794, 7910, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7922, 221, 374, + 7928, 376, -1, -1, 562, -1, -1, 7822, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7924, -1, -1, + -1, -1, -1, 7926, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 224, 225, 226, 227, 228, 229, -1, + 257, 259, 261, 551, 462, -1, -1, 513, + 515, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7681, 7841, -1, -1, -1, -1, -1, 7843, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 263, 265, + -1, -1, -1, 231, -1, -1, -1, 267, + 269, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 232, 233, 234, 7869, 235, -1, 553, + 275, 277, 281, 279, 283, -1, -1, 517, + 519, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7865, -1, 7705, 7707, -1, -1, 7867, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 236, 237, 238, + 297, 239, -1, -1, 299, 301, 303, -1, + 464, -1, -1, 521, 523, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7883, -1, -1, + 7725, -1, -1, 7881, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 505, 324, -1, 241, -1, -1, 326, + -1, -1, -1, 7749, 328, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7751, 7753, 7755, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 242, 243, 244, + 245, 246, -1, -1, 333, 335, 491, 559, + 466, 337, 417, 525, 527, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7885, -1, -1, + -1, -1, -1, 7887, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 249, 250, 251, 361, 252, 367, -1, + 363, 365, 371, -1, 468, 369, 432, 533, + 535, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7909, -1, 7799, 7797, -1, 7795, 7911, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7923, 253, 375, + 7929, 255, 7833, -1, 563, -1, -1, 7823, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7925, -1, -1, + -1, -1, -1, 7927, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7696, + -1, -1, -1, 7690, 270, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7692, 7694, 7698, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7697, -1, -1, -1, 7691, + 271, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7693, 7695, 7699, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 500, 284, -1, -1, -1, 290, + 7712, 286, -1, 288, 486, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 501, 285, + -1, -1, -1, 291, 7713, 287, -1, 289, + 487, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 292, -1, 7718, -1, 7720, + -1, -1, -1, 7714, 542, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7716, -1, -1, -1, 7722, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 293, + -1, 7719, -1, 7721, -1, -1, -1, 7715, + 543, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7717, 7830, -1, + -1, 7723, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 308, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 309, + -1, -1, -1, -1, -1, -1, -1, -1, + 496, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7728, -1, -1, -1, -1, 310, + -1, -1, -1, -1, 488, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7730, 7732, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7729, -1, + -1, -1, -1, 311, -1, -1, -1, -1, + 489, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7731, 7733, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 313, -1, -1, -1, -1, 315, + -1, -1, -1, -1, 317, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7734, 7738, 7740, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 314, -1, + -1, -1, -1, 316, -1, -1, -1, -1, + 318, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7735, 7739, 7741, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 340, -1, -1, -1, -1, 342, + -1, -1, -1, 7768, 344, -1, -1, 528, + 530, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7770, 7774, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 341, -1, + -1, -1, -1, 343, -1, -1, -1, 7769, + 345, -1, -1, 529, 531, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7771, 7775, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 346, 348, -1, -1, -1, 350, + -1, -1, -1, 7776, 352, -1, -1, -1, + -1, 536, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7778, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 347, 349, + -1, -1, -1, 351, -1, -1, -1, 7777, + 353, -1, -1, -1, -1, 537, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7779, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 354, + -1, -1, -1, 7786, 356, -1, -1, -1, + -1, 538, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7788, 7790, 7792, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7831, -1, 355, -1, -1, -1, 7787, + 357, -1, -1, -1, -1, 539, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7789, 7791, 7793, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7808, 7810, 372, -1, 7812, -1, -1, + -1, -1, -1, 7814, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7816, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7809, 7811, 373, + -1, 7813, 7832, -1, -1, -1, -1, 7815, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7817, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 377, 7824, -1, -1, -1, -1, + -1, -1, -1, 379, 381, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7826, 7828, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 378, 7825, + -1, -1, -1, -1, -1, -1, -1, 380, + 382, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7827, 7829, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 475, 471, -1, -1, -1, -1, -1, + 469, -1, -1, -1, 473, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 476, 472, -1, + -1, -1, -1, -1, 470, -1, -1, -1, + 474, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 478, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 479, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 480, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 481, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 508, -1, -1, -1, -1, -1, + 482, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 509, -1, + -1, -1, -1, -1, 483, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 492, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 493, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 494, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 495, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 506, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 507, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 510, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 511, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 554, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 555, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7756, -1, -1, 7758, -1, -1, + 556, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7757, -1, + -1, 7759, -1, -1, 557, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 560, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 561, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8173, 901, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8129, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8122, 902, -1, + -1, -1, -1, -1, 8121, 8120, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7944, 7945, -1, 8124, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8136, 904, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7960, 7961, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8138, 905, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7976, 7977, -1, 8140, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8154, 906, -1, -1, 938, -1, -1, + 8153, 8152, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7992, 7993, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8184, 908, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8008, 8009, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8170, 910, -1, -1, 939, -1, -1, + 8169, 8168, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8025, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8186, 911, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8040, 8041, -1, 8188, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8146, 912, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8151, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8048, 940, -1, + -1, -1, -1, -1, 8113, 8112, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7936, 7937, 8118, 8115, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8050, 941, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7952, 7953, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8052, 942, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7968, 7969, 8134, 8131, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8054, 943, -1, -1, 970, -1, -1, + 8145, 8144, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7984, 7985, 8150, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8162, 944, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8167, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8058, 973, -1, -1, 971, -1, -1, + 8161, 8160, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8016, 8017, 8166, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8056, 972, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8000, 8001, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8060, 974, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8032, 8033, 8182, 8179, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 979, -1, + -1, 980, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1024, -1, -1, -1, 1025, -1, -1, + -1, 1238, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 1027, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1031, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 1036, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1037, -1, -1, -1, 1252, -1, -1, + 1250, 1049, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1264, -1, -1, 1262, 1038, -1, -1, + -1, 1266, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1117, -1, -1, -1, 1253, -1, -1, + 1251, 1081, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1104, -1, -1, + -1, 1105, -1, -1, -1, 1239, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1107, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1111, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1116, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1265, -1, -1, 1263, 1118, -1, -1, + -1, 1267, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1142, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1143, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1244, -1, -1, + -1, 1217, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1245, -1, -1, -1, 1218, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1234, -1, -1, + -1, 1232, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1235, -1, -1, -1, 1233, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1242, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1243, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1246, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1247, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1254, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1255, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1258, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1259, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1260, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1261, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1268, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1269, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1272, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1273, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1570, 1571, 1573, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1572, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1574, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1728, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1730, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1747, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 2345, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 2353, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 2356, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 2507, 2508, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 2888, 2891, 2892, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2964, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 3020, 3018, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 3019, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 3144, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 3264, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 3271, 3272, + 3274, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 3275, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 3402, 3404, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 3403, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 3546, 3548, 3550, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 3549, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 4134, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6918, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 6920, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6922, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 6924, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6926, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 6930, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6971, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 6973, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6976, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 6977, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 6979, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7682, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7684, 7686, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7683, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7685, 7687, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7688, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7689, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7700, 7702, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7701, 7703, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7708, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7709, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7710, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7711, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7726, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7727, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7736, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7737, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7742, -1, -1, -1, -1, -1, + -1, -1, -1, 7744, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7746, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7743, -1, + -1, -1, -1, -1, -1, -1, -1, 7745, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7747, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7760, 7762, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7761, 7763, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7764, -1, -1, -1, -1, -1, + -1, -1, -1, 7766, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7765, -1, + -1, -1, -1, -1, -1, -1, -1, 7767, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7772, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7773, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7780, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7781, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7782, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7783, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7784, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7785, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7800, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7801, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7802, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7803, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 7804, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7806, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 7805, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7807, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7820, -1, -1, + -1, -1, -1, 7818, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7821, -1, -1, -1, -1, -1, 7819, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7835, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7846, 7844, -1, + 7850, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7848, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7847, 7845, -1, 7851, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7849, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7852, + -1, -1, -1, -1, -1, 7862, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7853, -1, -1, -1, -1, + -1, 7863, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7856, 7854, -1, + 7860, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7858, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7857, 7855, -1, 7861, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7859, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7872, 7870, -1, + 7876, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7874, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7873, 7871, -1, 7877, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7875, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7878, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7879, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7890, 7888, -1, + 7894, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7892, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7891, 7889, -1, 7895, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7893, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7896, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7897, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7900, 7898, -1, + 7904, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7906, -1, -1, + -1, -1, -1, 7902, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7901, 7899, -1, 7905, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7907, -1, -1, -1, -1, -1, 7903, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7914, 7912, -1, + 7918, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7920, -1, -1, + -1, -1, -1, 7916, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7915, 7913, -1, 7919, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7921, -1, -1, -1, -1, -1, 7917, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7938, 7940, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7942, 8064, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7939, 7941, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7943, 8065, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7946, 7948, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7950, 8072, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7947, 7949, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7951, 8073, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7954, 7956, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7955, 7957, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7962, 7964, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7963, 7965, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7970, 7972, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7974, 8080, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7971, 7973, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7975, 8081, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7978, 7980, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7982, 8088, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7979, 7981, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7983, 8089, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7986, 7988, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7990, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7987, 7989, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7991, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7994, 7996, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7998, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 7995, 7997, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 7999, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8002, 8004, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8003, 8005, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8010, 8012, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8011, 8013, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8018, 8020, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8022, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8019, 8021, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8023, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8027, 8029, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8031, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8034, 8036, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8038, 8096, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8035, 8037, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8039, 8097, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8042, 8044, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8046, 8104, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8043, 8045, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8047, 8105, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8066, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8067, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8068, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8069, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8070, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8071, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8074, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8075, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8076, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8077, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8078, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8079, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8082, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8083, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8084, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8085, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8086, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8087, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8090, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8091, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8092, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8093, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8094, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8095, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8098, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8099, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8100, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8101, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8102, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8103, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8106, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8107, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8108, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8109, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8110, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8111, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8114, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8116, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8119, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8130, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8132, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8135, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8141, 8142, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8143, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8157, 8158, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8159, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8164, 8165, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8172, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8178, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8180, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8183, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8602, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8603, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8622, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8653, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8654, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8655, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8708, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8713, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8716, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8740, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8742, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8769, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8772, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8775, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8777, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8800, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8802, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8813, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8814, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8815, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8816, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8817, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8820, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8821, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8824, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8825, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8832, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8833, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8836, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8837, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8840, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8841, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8876, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8877, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8878, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8879, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8928, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8929, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8930, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8931, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8938, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8939, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 8940, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8941, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 10972, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12364, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12366, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12368, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12370, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12372, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12374, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12376, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12378, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12380, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12382, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12384, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12386, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12389, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12391, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12393, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12400, 12401, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12403, 12404, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12406, 12407, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12409, 12410, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12412, 12413, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12436, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12446, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12460, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12462, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12464, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12466, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12468, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12470, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12472, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12474, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12476, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12478, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12480, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12482, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12485, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12487, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12489, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12496, 12497, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12499, 12500, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12502, 12503, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12505, 12506, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12508, 12509, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12532, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12535, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12536, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12537, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 12538, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12542, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 119134, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 119135, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 119136, 119137, 119138, 119139, 119140, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 119227, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 119228, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 119229, 119231, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 119230, 119232, -1, -1, -1, }; + diff --git a/contrib/subversion/subversion/libsvn_subr/utf_validate.c b/contrib/subversion/subversion/libsvn_subr/utf_validate.c index 8311fd71a..0aab81c91 100644 --- a/contrib/subversion/subversion/libsvn_subr/utf_validate.c +++ b/contrib/subversion/subversion/libsvn_subr/utf_validate.c @@ -258,24 +258,7 @@ static const char machine [9][14] = { static const char * first_non_fsm_start_char(const char *data, apr_size_t max_len) { -#if !SVN_UNALIGNED_ACCESS_IS_OK - - /* On some systems, we need to make sure that buf is properly aligned - * for chunky data access. - */ - if ((apr_uintptr_t)data & (sizeof(apr_uintptr_t)-1)) - { - apr_size_t len = (~(apr_uintptr_t)data) & (sizeof(apr_uintptr_t)-1); - if (len > max_len) - len = max_len; - max_len -= len; - - for (; len > 0; ++data, --len) - if (*data < 0 || *data >= 0x80) - return data; - } - -#endif +#if SVN_UNALIGNED_ACCESS_IS_OK /* Scan the input one machine word at a time. */ for (; max_len > sizeof(apr_uintptr_t) @@ -283,55 +266,11 @@ first_non_fsm_start_char(const char *data, apr_size_t max_len) if (*(const apr_uintptr_t *)data & SVN__BIT_7_SET) break; - /* The remaining odd bytes will be examined the naive way: */ - for (; max_len > 0; ++data, --max_len) - if (*data < 0 || *data >= 0x80) - break; - - return data; -} - -/* Scan the C string in *DATA for chars that are not in the octet - * category 0 (FSM_START). Return the position of either the such - * char or of the terminating NUL. - */ -static const char * -first_non_fsm_start_char_cstring(const char *data) -{ - /* We need to make sure that BUF is properly aligned for chunky data - * access because we don't know the string's length. Unaligned chunk - * read access beyond the NUL terminator could therefore result in a - * segfault. - */ - for (; (apr_uintptr_t)data & (sizeof(apr_uintptr_t)-1); ++data) - if (*data <= 0 || *data >= 0x80) - return data; - - /* Scan the input one machine word at a time. */ -#ifndef SVN_UTF_NO_UNINITIALISED_ACCESS - /* This may read allocated but initialised bytes beyond the - terminating null. Any such bytes are always readable and this - code operates correctly whatever the uninitialised values happen - to be. However memory checking tools such as valgrind and GCC - 4.8's address santitizer will object so this bit of code can be - disabled at compile time. */ - for (; ; data += sizeof(apr_uintptr_t)) - { - /* Check for non-ASCII chars: */ - apr_uintptr_t chunk = *(const apr_uintptr_t *)data; - if (chunk & SVN__BIT_7_SET) - break; - - /* This is the well-known strlen test: */ - chunk |= (chunk & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET; - if ((chunk & SVN__BIT_7_SET) != SVN__BIT_7_SET) - break; - } #endif /* The remaining odd bytes will be examined the naive way: */ - for (; ; ++data) - if (*data <= 0 || *data >= 0x80) + for (; max_len > 0; ++data, --max_len) + if ((unsigned char)*data >= 0x80) break; return data; @@ -359,20 +298,10 @@ svn_utf__last_valid(const char *data, apr_size_t len) svn_boolean_t svn_utf__cstring_is_valid(const char *data) { - int state = FSM_START; - if (!data) return FALSE; - data = first_non_fsm_start_char_cstring(data); - - while (*data) - { - unsigned char octet = *data++; - int category = octet_category[octet]; - state = machine[state][category]; - } - return state == FSM_START; + return svn_utf__is_valid(data, strlen(data)); } svn_boolean_t diff --git a/contrib/subversion/subversion/libsvn_subr/utf_width.c b/contrib/subversion/subversion/libsvn_subr/utf_width.c index 3adff4cce..30905a06d 100644 --- a/contrib/subversion/subversion/libsvn_subr/utf_width.c +++ b/contrib/subversion/subversion/libsvn_subr/utf_width.c @@ -190,7 +190,7 @@ mk_wcwidth(apr_uint32_t ucs) /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) + sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ diff --git a/contrib/subversion/subversion/libsvn_subr/version.c b/contrib/subversion/subversion/libsvn_subr/version.c index a0b77ba4d..070808a10 100644 --- a/contrib/subversion/subversion/libsvn_subr/version.c +++ b/contrib/subversion/subversion/libsvn_subr/version.c @@ -75,10 +75,10 @@ svn_boolean_t svn_ver_equal(const svn_version_t *my_version, 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_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; @@ -136,7 +136,7 @@ svn_version_extended(svn_boolean_t verbose, info->build_time = NULL; info->build_host = SVN_BUILD_HOST; info->copyright = apr_pstrdup - (pool, _("Copyright (C) 2015 The Apache Software Foundation.\n" + (pool, _("Copyright (C) 2016 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 " diff --git a/contrib/subversion/subversion/libsvn_subr/win32_crashrpt.c b/contrib/subversion/subversion/libsvn_subr/win32_crashrpt.c index 4b665c169..680b944b9 100644 --- a/contrib/subversion/subversion/libsvn_subr/win32_crashrpt.c +++ b/contrib/subversion/subversion/libsvn_subr/win32_crashrpt.c @@ -37,6 +37,8 @@ typedef int win32_crashrpt__dummy; #include "svn_version.h" +#include "sysinfo.h" + #include "win32_crashrpt.h" #include "win32_crashrpt_dll.h" @@ -58,12 +60,13 @@ HANDLE dbghelp_dll = INVALID_HANDLE_VALUE; /*** Code. ***/ -/* Convert a wide-character string to utf-8. This function will create a buffer - * large enough to hold the result string, the caller should free this buffer. +/* Convert a wide-character string to the current windows locale, suitable + * for directly using stdio. This function will create a buffer large + * enough to hold the result string, the caller should free this buffer. * If the string can't be converted, NULL is returned. */ static char * -convert_wbcs_to_utf8(const wchar_t *str) +convert_wbcs_to_ansi(const wchar_t *str) { size_t len = wcslen(str); char *utf8_str = malloc(sizeof(wchar_t) * len + 1); @@ -167,7 +170,7 @@ write_module_info_callback(void *data, FILE *log_file = (FILE *)data; MINIDUMP_MODULE_CALLBACK module = callback_input->Module; - char *buf = convert_wbcs_to_utf8(module.FullPath); + char *buf = convert_wbcs_to_ansi(module.FullPath); fprintf(log_file, FORMAT_PTR, module.BaseOfImage); fprintf(log_file, " %s", buf); free(buf); @@ -188,7 +191,7 @@ static void write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context, FILE *log_file) { - OSVERSIONINFO oi; + OSVERSIONINFOEXW oi; const char *cmd_line; char workingdir[8192]; @@ -207,13 +210,11 @@ write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context, SVN_VERSION, __DATE__, __TIME__); /* write information about the OS */ - oi.dwOSVersionInfoSize = sizeof(oi); - GetVersionEx(&oi); - - fprintf(log_file, - "Platform: Windows OS version %d.%d build %d %s\n\n", - oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, - oi.szCSDVersion); + if (svn_sysinfo___fill_windows_version(&oi)) + fprintf(log_file, + "Platform: Windows OS version %d.%d build %d %S\n\n", + oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, + oi.szCSDVersion); /* write the exception code */ fprintf(log_file, @@ -244,16 +245,16 @@ write_process_info(EXCEPTION_RECORD *exception, CONTEXT *context, "Rsp=%016I64x Rbp=%016I64x Rsi=%016I64x Rdi=%016I64x\n", context->Rsp, context->Rbp, context->Rsi, context->Rdi); fprintf(log_file, - "R8= %016I64x R9= %016I64x R10= %016I64x R11=%016I64x\n", + "R8= %016I64x R9= %016I64x R10=%016I64x R11=%016I64x\n", context->R8, context->R9, context->R10, context->R11); fprintf(log_file, "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", context->R12, context->R13, context->R14, context->R15); fprintf(log_file, - "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\n", - context->SegCs, context->SegDs, context->SegEs, - context->SegFs, context->SegGs, context->SegSs); + "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x\n", + context->SegCs, context->SegSs, context->SegDs, + context->SegEs, context->SegFs, context->SegGs); #else #error Unknown processortype, please disable SVN_USE_WIN32_CRASHHANDLER #endif @@ -335,7 +336,7 @@ format_value(char *value_str, DWORD64 mod_base, DWORD type, void *value_addr) if (SymGetTypeInfo_(proc, mod_base, type, TI_GET_SYMNAME, &type_name_wbcs)) { - char *type_name = convert_wbcs_to_utf8(type_name_wbcs); + char *type_name = convert_wbcs_to_ansi(type_name_wbcs); LocalFree(type_name_wbcs); if (ptr == 0) diff --git a/contrib/subversion/subversion/libsvn_subr/win32_crypto.c b/contrib/subversion/subversion/libsvn_subr/win32_crypto.c index e16866a68..74bda1c68 100644 --- a/contrib/subversion/subversion/libsvn_subr/win32_crypto.c +++ b/contrib/subversion/subversion/libsvn_subr/win32_crypto.c @@ -40,6 +40,7 @@ typedef int win32_crypto__dummy; #include "svn_user.h" #include "svn_base64.h" +#include "auth.h" #include "private/svn_auth_private.h" #include "svn_private_config.h" @@ -213,7 +214,7 @@ static const svn_auth_provider_t windows_simple_provider = { /* Public API */ void -svn_auth_get_windows_simple_provider(svn_auth_provider_object_t **provider, +svn_auth__get_windows_simple_provider(svn_auth_provider_object_t **provider, apr_pool_t *pool) { svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); @@ -331,7 +332,7 @@ static const svn_auth_provider_t windows_ssl_client_cert_pw_provider = { /* Public API */ void -svn_auth_get_windows_ssl_client_cert_pw_provider +svn_auth__get_windows_ssl_client_cert_pw_provider (svn_auth_provider_object_t **provider, apr_pool_t *pool) { @@ -482,7 +483,7 @@ static const svn_auth_provider_t windows_server_trust_provider = { /* Public API */ void -svn_auth_get_windows_ssl_server_trust_provider +svn_auth__get_windows_ssl_server_trust_provider (svn_auth_provider_object_t **provider, apr_pool_t *pool) { svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po)); diff --git a/contrib/subversion/subversion/libsvn_subr/win32_xlate.c b/contrib/subversion/subversion/libsvn_subr/win32_xlate.c index efe9c056d..d95d62ea0 100644 --- a/contrib/subversion/subversion/libsvn_subr/win32_xlate.c +++ b/contrib/subversion/subversion/libsvn_subr/win32_xlate.c @@ -47,9 +47,12 @@ typedef int win32_xlate__dummy; #include "svn_string.h" #include "svn_utf.h" #include "private/svn_atomic.h" +#include "private/svn_subr_private.h" #include "win32_xlate.h" +#include "svn_private_config.h" + static svn_atomic_t com_initialized = 0; /* Initializes COM and keeps COM available until process exit. @@ -74,11 +77,11 @@ initialize_com(void *baton, apr_pool_t* pool) return SVN_NO_ERROR; } -typedef struct win32_xlate_t +struct svn_subr__win32_xlate_t { UINT from_page_id; UINT to_page_id; -} win32_xlate_t; +}; static apr_status_t get_page_id_from_name(UINT *page_id_p, const char *page_name, apr_pool_t *pool) @@ -113,16 +116,26 @@ get_page_id_from_name(UINT *page_id_p, const char *page_name, apr_pool_t *pool) if ((page_name[0] == 'c' || page_name[0] == 'C') && (page_name[1] == 'p' || page_name[1] == 'P')) { - *page_id_p = atoi(page_name + 2); + int page_id; + + err = svn_cstring_atoi(&page_id, page_name + 2); + if (err) + { + apr_status_t saved = err->apr_err; + svn_error_clear(err); + return saved; + } + + *page_id_p = page_id; return APR_SUCCESS; } err = svn_atomic__init_once(&com_initialized, initialize_com, NULL, pool); - if (err) { + apr_status_t saved = err->apr_err; svn_error_clear(err); - return APR_EGENERAL; + return saved; /* probably SVN_ERR_ATOMIC_INIT_FAILURE */ } hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, @@ -153,12 +166,12 @@ get_page_id_from_name(UINT *page_id_p, const char *page_name, apr_pool_t *pool) } apr_status_t -svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, const char *topage, +svn_subr__win32_xlate_open(svn_subr__win32_xlate_t **xlate_p, const char *topage, const char *frompage, apr_pool_t *pool) { UINT from_page_id, to_page_id; apr_status_t apr_err = APR_SUCCESS; - win32_xlate_t *xlate; + svn_subr__win32_xlate_t *xlate; apr_err = get_page_id_from_name(&to_page_id, topage, pool); if (apr_err == APR_SUCCESS) @@ -177,7 +190,7 @@ svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, const char *topage, } apr_status_t -svn_subr__win32_xlate_to_stringbuf(win32_xlate_t *handle, +svn_subr__win32_xlate_to_stringbuf(svn_subr__win32_xlate_t *handle, const char *src_data, apr_size_t src_length, svn_stringbuf_t **dest, diff --git a/contrib/subversion/subversion/libsvn_subr/win32_xlate.h b/contrib/subversion/subversion/libsvn_subr/win32_xlate.h index 82fc83223..ee23add47 100644 --- a/contrib/subversion/subversion/libsvn_subr/win32_xlate.h +++ b/contrib/subversion/subversion/libsvn_subr/win32_xlate.h @@ -27,25 +27,27 @@ #ifdef WIN32 /* Opaque translation buffer. */ -typedef struct win32_xlate_t win32_xlate_t; +typedef struct svn_subr__win32_xlate_t svn_subr__win32_xlate_t; /* Set *XLATE_P to a handle node for converting from FROMPAGE to TOPAGE. Returns APR_EINVAL or APR_ENOTIMPL, if a conversion isn't supported. If fail for any other reason, return the error. Allocate *RET in POOL. */ -apr_status_t svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, - const char *topage, - const char *frompage, - apr_pool_t *pool); +apr_status_t +svn_subr__win32_xlate_open(svn_subr__win32_xlate_t **xlate_p, + const char *topage, + const char *frompage, + apr_pool_t *pool); /* Convert SRC_LENGTH bytes of SRC_DATA in NODE->handle, store the result in *DEST, which is allocated in POOL. */ -apr_status_t svn_subr__win32_xlate_to_stringbuf(win32_xlate_t *handle, - const char *src_data, - apr_size_t src_length, - svn_stringbuf_t **dest, - apr_pool_t *pool); +apr_status_t +svn_subr__win32_xlate_to_stringbuf(svn_subr__win32_xlate_t *handle, + const char *src_data, + apr_size_t src_length, + svn_stringbuf_t **dest, + apr_pool_t *pool); #endif /* WIN32 */ diff --git a/contrib/subversion/subversion/libsvn_subr/x509.h b/contrib/subversion/subversion/libsvn_subr/x509.h new file mode 100644 index 000000000..9497f381c --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/x509.h @@ -0,0 +1,134 @@ +/** + * \file x509.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SVN_LIBSVN_SUBR_X509_H +#define SVN_LIBSVN_SUBR_X509_H + +#include +#include + +#include "svn_x509.h" + +/* + * DER constants + */ +#define ASN1_BOOLEAN 0x01 +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_UTF8_STRING 0x0C +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_PRINTABLE_STRING 0x13 +#define ASN1_T61_STRING 0x14 +#define ASN1_IA5_STRING 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_GENERALIZED_TIME 0x18 +#define ASN1_UNIVERSAL_STRING 0x1C +#define ASN1_BMP_STRING 0x1E +#define ASN1_PRIMITIVE 0x00 +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_CONTEXT_SPECIFIC 0x80 + +/* + * various object identifiers + */ +#define OID_SUBJECT_ALT_NAME "\x55\x1D\x11" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Structures for parsing X.509 certificates + */ +typedef struct _x509_buf { + int tag; + ptrdiff_t len; + const unsigned char *p; +} x509_buf; + +typedef struct _x509_name { + x509_buf oid; + x509_buf val; + struct _x509_name *next; +} x509_name; + +typedef struct _x509_cert { + int version; + x509_buf serial; + x509_buf sig_oid1; + + x509_name issuer; + x509_name subject; + + apr_time_t valid_from; + apr_time_t valid_to; + + x509_buf issuer_id; + x509_buf subject_id; + apr_array_header_t *dnsnames; + + x509_buf sig_oid2; + x509_buf sig; + +} x509_cert; + + +struct svn_x509_name_attr_t { + unsigned char *oid; + apr_size_t oid_len; + const char *utf8_value; +}; + +/* + * Certificate info, returned from the parser + */ +struct svn_x509_certinfo_t +{ + apr_array_header_t *issuer; + apr_array_header_t *subject; + apr_time_t valid_from; + apr_time_t valid_to; + svn_checksum_t *digest; + apr_array_header_t *hostnames; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_LIBSVN_SUBR_X509_H */ diff --git a/contrib/subversion/subversion/libsvn_subr/x509info.c b/contrib/subversion/subversion/libsvn_subr/x509info.c new file mode 100644 index 000000000..351459629 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/x509info.c @@ -0,0 +1,332 @@ +/* + * x509info.c: Accessors for svn_x509_certinfo_t + * + * ==================================================================== + * 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 + +#include +#include + +#include "svn_string.h" +#include "svn_hash.h" +#include "x509.h" + + + +svn_x509_name_attr_t * +svn_x509_name_attr_dup(const svn_x509_name_attr_t *attr, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_x509_name_attr_t *result = apr_palloc(result_pool, sizeof(*result)); + result->oid_len = attr->oid_len; + result->oid = apr_palloc(result_pool, result->oid_len); + memcpy(result->oid, attr->oid, result->oid_len); + result->utf8_value = apr_pstrdup(result_pool, attr->utf8_value); + + return result; +} + +const unsigned char * +svn_x509_name_attr_get_oid(const svn_x509_name_attr_t *attr, apr_size_t *len) +{ + *len = attr->oid_len; + return attr->oid; +} + +const char * +svn_x509_name_attr_get_value(const svn_x509_name_attr_t *attr) +{ + return attr->utf8_value; +} + +/* Array elements are assumed to be nul-terminated C strings. */ +static apr_array_header_t * +deep_copy_array(apr_array_header_t *s, apr_pool_t *result_pool) +{ + int i; + apr_array_header_t *d; + + if (!s) + return NULL; + + d = apr_array_copy(result_pool, s); + + /* Make a deep copy of the strings in the array. */ + for (i = 0; i < s->nelts; ++i) + { + APR_ARRAY_IDX(d, i, const char *) = + apr_pstrdup(result_pool, APR_ARRAY_IDX(s, i, const char *)); + } + + return d; +} + +/* Copy an array with elements that are svn_x509_name_attr_t's */ +static apr_array_header_t * +deep_copy_name_attrs(apr_array_header_t *s, apr_pool_t *result_pool) +{ + int i; + apr_array_header_t *d; + + if (!s) + return NULL; + + d = apr_array_copy(result_pool, s); + + /* Make a deep copy of the svn_x509_name_attr_t's in the array. */ + for (i = 0; i < s->nelts; ++i) + { + APR_ARRAY_IDX(d, i, const svn_x509_name_attr_t *) = + svn_x509_name_attr_dup(APR_ARRAY_IDX(s, i, svn_x509_name_attr_t *), + result_pool, result_pool); + } + + return d; +} + +svn_x509_certinfo_t * +svn_x509_certinfo_dup(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_x509_certinfo_t *result = apr_palloc(result_pool, sizeof(*result)); + result->subject = deep_copy_name_attrs(certinfo->subject, result_pool); + result->issuer = deep_copy_name_attrs(certinfo->issuer, result_pool); + result->valid_from = certinfo->valid_from; + result->valid_to = certinfo->valid_to; + result->digest = svn_checksum_dup(certinfo->digest, result_pool); + result->hostnames = deep_copy_array(certinfo->hostnames, result_pool); + + return result; +} + +typedef struct asn1_oid { + const unsigned char *oid; + const ptrdiff_t oid_len; + const char *short_label; + const char *long_label; +} asn1_oid; + +#define CONSTANT_PAIR(c) (unsigned char *)(c), sizeof((c)) - 1 + +static const asn1_oid asn1_oids[] = { + { CONSTANT_PAIR(SVN_X509_OID_COMMON_NAME), "CN", "commonName" }, + { CONSTANT_PAIR(SVN_X509_OID_COUNTRY), "C", "countryName" }, + { CONSTANT_PAIR(SVN_X509_OID_LOCALITY), "L", "localityName" }, + { CONSTANT_PAIR(SVN_X509_OID_STATE), "ST", "stateOrProvinceName" }, + { CONSTANT_PAIR(SVN_X509_OID_ORGANIZATION), "O", "organizationName" }, + { CONSTANT_PAIR(SVN_X509_OID_ORG_UNIT), "OU", "organizationalUnitName"}, + { CONSTANT_PAIR(SVN_X509_OID_EMAIL), NULL, "emailAddress" }, + { NULL }, +}; + +/* Given an OID return a null-terminated C string representation. + * For example an OID with the bytes "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" + * would be converted to the string "1.2.840.113549.1.9.1". */ +const char * +svn_x509_oid_to_string(const unsigned char *oid, apr_size_t oid_len, + apr_pool_t *scratch_pool, apr_pool_t *result_pool) +{ + svn_stringbuf_t *out = svn_stringbuf_create_empty(result_pool); + const unsigned char *p = oid; + const unsigned char *end = p + oid_len; + const char *temp; + + while (p != end) { + if (p == oid) + { + /* Handle decoding the first two values of the OID. These values + * are encoded by taking the first value and adding 40 to it and + * adding the result to the second value, then placing this single + * value in the first byte of the output. This is unambiguous since + * the first value is apparently limited to 0, 1 or 2 and the second + * is limited to 0 to 39. */ + temp = apr_psprintf(scratch_pool, "%d.%d", *p / 40, *p % 40); + p++; + } + else if (*p < 128) + { + /* The remaining values if they're less than 128 are just + * the number one to one encoded */ + temp = apr_psprintf(scratch_pool, ".%d", *p); + p++; + } + else + { + /* Values greater than 128 are encoded as a series of 7 bit values + * with the left most bit set to indicate this encoding with the + * last octet missing the left most bit to finish out the series.. */ + unsigned int collector = 0; + svn_boolean_t dot = FALSE; + + do { + if (collector == 0 && *p == 0x80) + { + /* include leading zeros in the string representation + technically not legal, but this seems nicer than just + returning NULL */ + if (!dot) + { + svn_stringbuf_appendbyte(out, '.'); + dot = TRUE; + } + svn_stringbuf_appendbyte(out, '0'); + } + else if (collector > UINT_MAX >> 7) + { + /* overflow */ + return NULL; + } + collector = collector << 7 | (*(p++) & 0x7f); + } while (p != end && *p > 127); + if (collector > UINT_MAX >> 7) + return NULL; /* overflow */ + collector = collector << 7 | *(p++); + temp = apr_psprintf(scratch_pool, "%s%d", dot ? "" : ".", collector); + } + svn_stringbuf_appendcstr(out, temp); + } + + if (svn_stringbuf_isempty(out)) + return NULL; + + return out->data; +} + +static const asn1_oid *oid_to_asn1_oid(unsigned char *oid, apr_size_t oid_len) +{ + const asn1_oid *entry; + + for (entry = asn1_oids; entry->oid; entry++) + { + if (oid_len == entry->oid_len && + memcmp(oid, entry->oid, oid_len) == 0) + return entry; + } + + return NULL; +} + +static const char *oid_to_best_label(unsigned char *oid, apr_size_t oid_len, + apr_pool_t *result_pool) +{ + const asn1_oid *entry = oid_to_asn1_oid(oid, oid_len); + + if (entry) + { + if (entry->short_label) + return entry->short_label; + + if (entry->long_label) + return entry->long_label; + } + else + { + const char *oid_string = svn_x509_oid_to_string(oid, oid_len, + result_pool, result_pool); + if (oid_string) + return oid_string; + } + + return "??"; +} + +/* + * Store the name from dn in printable form into buf, + * using scratch_pool for any temporary allocations. + * If CN is not NULL, return any common name in CN + */ +static const char * +get_dn(apr_array_header_t *name, + apr_pool_t *result_pool) +{ + svn_stringbuf_t *buf = svn_stringbuf_create_empty(result_pool); + int n; + + for (n = 0; n < name->nelts; n++) + { + const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(name, n, svn_x509_name_attr_t *); + + if (n > 0) + svn_stringbuf_appendcstr(buf, ", "); + + svn_stringbuf_appendcstr(buf, oid_to_best_label(attr->oid, attr->oid_len, result_pool)); + svn_stringbuf_appendbyte(buf, '='); + svn_stringbuf_appendcstr(buf, attr->utf8_value); + } + + return buf->data; +} + +const char * +svn_x509_certinfo_get_subject(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool) +{ + return get_dn(certinfo->subject, result_pool); +} + +const apr_array_header_t * +svn_x509_certinfo_get_subject_attrs(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->subject; +} + +const char * +svn_x509_certinfo_get_issuer(const svn_x509_certinfo_t *certinfo, + apr_pool_t *result_pool) +{ + return get_dn(certinfo->issuer, result_pool); +} + +const apr_array_header_t * +svn_x509_certinfo_get_issuer_attrs(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->issuer; +} + +apr_time_t +svn_x509_certinfo_get_valid_from(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->valid_from; +} + +const apr_time_t +svn_x509_certinfo_get_valid_to(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->valid_to; +} + +const svn_checksum_t * +svn_x509_certinfo_get_digest(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->digest; +} + +const apr_array_header_t * +svn_x509_certinfo_get_hostnames(const svn_x509_certinfo_t *certinfo) +{ + return certinfo->hostnames; +} + diff --git a/contrib/subversion/subversion/libsvn_subr/x509parse.c b/contrib/subversion/subversion/libsvn_subr/x509parse.c new file mode 100644 index 000000000..32af4a7bf --- /dev/null +++ b/contrib/subversion/subversion/libsvn_subr/x509parse.c @@ -0,0 +1,1200 @@ +/* + * X.509 certificate and private key decoding + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt + * http://www.ietf.org/rfc/rfc3279.txt + * http://www.ietf.org/rfc/rfc6818.txt + * + * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#include +#include +#include "svn_hash.h" +#include "svn_string.h" +#include "svn_time.h" +#include "svn_checksum.h" +#include "svn_utf.h" +#include "svn_ctype.h" +#include "private/svn_utf_private.h" +#include "private/svn_string_private.h" + +#include "x509.h" + +#include +#include + +/* + * ASN.1 DER decoding routines + */ +static svn_error_t * +asn1_get_len(const unsigned char **p, const unsigned char *end, + ptrdiff_t *len) +{ + if ((end - *p) < 1) + return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + + if ((**p & 0x80) == 0) + *len = *(*p)++; + else + switch (**p & 0x7F) + { + case 1: + if ((end - *p) < 2) + return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if ((end - *p) < 3) + return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + + *len = ((*p)[1] << 8) | (*p)[2]; + (*p) += 3; + break; + + default: + return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL); + break; + } + + if (*len > (end - *p)) + return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +asn1_get_tag(const unsigned char **p, + const unsigned char *end, ptrdiff_t *len, int tag) +{ + if ((end - *p) < 1) + return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + + if (**p != tag) + return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); + + (*p)++; + + return svn_error_trace(asn1_get_len(p, end, len)); +} + +static svn_error_t * +asn1_get_int(const unsigned char **p, const unsigned char *end, int *val) +{ + ptrdiff_t len; + + SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER)); + + /* Reject bit patterns that would overflow the output and those that + represent negative values. */ + if (len > (int)sizeof(int) || (**p & 0x80) != 0) + return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL); + + *val = 0; + + while (len-- > 0) { + /* This would be undefined for bit-patterns of negative values. */ + *val = (*val << 8) | **p; + (*p)++; + } + + return SVN_NO_ERROR; +} + +static svn_boolean_t +equal(const void *left, apr_size_t left_len, + const void *right, apr_size_t right_len) +{ + if (left_len != right_len) + return FALSE; + + return memcmp(left, right, right_len) == 0; +} + +static svn_boolean_t +oids_equal(x509_buf *left, x509_buf *right) +{ + return equal(left->p, left->len, + right->p, right->len); +} + +/* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static svn_error_t * +x509_get_version(const unsigned char **p, const unsigned char *end, int *ver) +{ + svn_error_t *err; + ptrdiff_t len; + + /* + * As defined in the Basic Certificate fields: + * version [0] EXPLICIT Version DEFAULT v1, + * the version is the context specific tag 0. + */ + err = asn1_get_tag(p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0); + if (err) + { + if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) + { + svn_error_clear(err); + *ver = 0; + return SVN_NO_ERROR; + } + + return svn_error_trace(err); + } + + end = *p + len; + + err = asn1_get_int(p, end, ver); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL); + + if (*p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL); + } + + return SVN_NO_ERROR; +} + +/* + * CertificateSerialNumber ::= INTEGER + */ +static svn_error_t * +x509_get_serial(const unsigned char **p, + const unsigned char *end, x509_buf * serial) +{ + svn_error_t *err; + + if ((end - *p) < 1) + { + err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); + } + + if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) && + **p != ASN1_INTEGER) + { + err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); + } + + serial->tag = *(*p)++; + + err = asn1_get_len(p, end, &serial->len); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); + + serial->p = *p; + *p += serial->len; + + return SVN_NO_ERROR; +} + +/* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +static svn_error_t * +x509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg) +{ + svn_error_t *err; + ptrdiff_t len; + + err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); + + end = *p + len; + alg->tag = **p; + + err = asn1_get_tag(p, end, &alg->len, ASN1_OID); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); + + alg->p = *p; + *p += alg->len; + + if (*p == end) + return SVN_NO_ERROR; + + /* + * assume the algorithm parameters must be NULL + */ + err = asn1_get_tag(p, end, &len, ASN1_NULL); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); + + if (*p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); + } + + return SVN_NO_ERROR; +} + +/* + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static svn_error_t * +x509_get_attribute(const unsigned char **p, const unsigned char *end, + x509_name *cur, apr_pool_t *result_pool) +{ + svn_error_t *err; + ptrdiff_t len; + x509_buf *oid; + x509_buf *val; + + err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + + end = *p + len; + + oid = &cur->oid; + + err = asn1_get_tag(p, end, &oid->len, ASN1_OID); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + + oid->tag = ASN1_OID; + oid->p = *p; + *p += oid->len; + + if ((end - *p) < 1) + { + err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + } + + if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && + **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && + **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING) + { + err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + } + + val = &cur->val; + val->tag = *(*p)++; + + err = asn1_get_len(p, end, &val->len); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + + val->p = *p; + *p += val->len; + + cur->next = NULL; + + if (*p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + } + + return SVN_NO_ERROR; +} + +/* + * RelativeDistinguishedName ::= + * SET SIZE (1..MAX) OF AttributeTypeAndValue + */ +static svn_error_t * +x509_get_name(const unsigned char **p, const unsigned char *name_end, + x509_name *name, apr_pool_t *result_pool) +{ + svn_error_t *err; + ptrdiff_t len; + const unsigned char *set_end; + x509_name *cur = NULL; + + err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); + + set_end = *p + len; + + /* + * iterate until the end of the SET is reached + */ + while (*p < set_end) + { + if (!cur) + { + cur = name; + } + else + { + cur->next = apr_palloc(result_pool, sizeof(x509_name)); + cur = cur->next; + } + SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool)); + } + + /* + * recurse until end of SEQUENCE (name) is reached + */ + if (*p == name_end) + return SVN_NO_ERROR; + + cur->next = apr_palloc(result_pool, sizeof(x509_name)); + + return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool)); +} + +/* Retrieve the date from the X.509 cert data between *P and END in either + * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and + * 4.1.2.5.2 respectively) and place the result in WHEN using SCRATCH_POOL + * for temporary allocations. */ +static svn_error_t * +x509_get_date(apr_time_t *when, + const unsigned char **p, + const unsigned char *end, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + apr_status_t ret; + int tag; + ptrdiff_t len; + char *date; + apr_time_exp_t xt = { 0 }; + char tz; + + err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME); + if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) + { + svn_error_clear(err); + err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME); + tag = ASN1_GENERALIZED_TIME; + } + else + { + tag = ASN1_UTC_TIME; + } + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); + + date = apr_pstrndup(scratch_pool, (const char *) *p, len); + switch (tag) + { + case ASN1_UTC_TIME: + if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c", + &xt.tm_year, &xt.tm_mon, &xt.tm_mday, + &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); + + /* UTCTime only provides a 2 digit year. X.509 specifies that years + * greater than or equal to 50 must be interpreted as 19YY and years + * less than 50 be interpreted as 20YY. This format is not used for + * years greater than 2049. apr_time_exp_t wants years as the number + * of years since 1900, so don't convert to 4 digits here. */ + xt.tm_year += 100 * (xt.tm_year < 50); + break; + + case ASN1_GENERALIZED_TIME: + if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c", + &xt.tm_year, &xt.tm_mon, &xt.tm_mday, + &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); + + /* GeneralizedTime has the full 4 digit year. But apr_time_exp_t + * wants years as the number of years since 1900. */ + xt.tm_year -= 1900; + break; + + default: + /* shouldn't ever get here because we should error out above in the + * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */ + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); + break; + } + + /* check that the timezone is GMT + * ASN.1 allows for the timezone to be specified but X.509 says it must + * always be GMT. A little bit of extra paranoia here seems like a good + * idea. */ + if (tz != 'Z') + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); + + /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */ + xt.tm_mon -= 1; + + ret = apr_time_exp_gmt_get(when, &xt); + if (ret) + return svn_error_wrap_apr(ret, NULL); + + *p += len; + + return SVN_NO_ERROR; +} + +/* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + */ +static svn_error_t * +x509_get_dates(apr_time_t *from, + apr_time_t *to, + const unsigned char **p, + const unsigned char *end, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + ptrdiff_t len; + + err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); + + end = *p + len; + + SVN_ERR(x509_get_date(from, p, end, scratch_pool)); + + SVN_ERR(x509_get_date(to, p, end, scratch_pool)); + + if (*p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig) +{ + svn_error_t *err; + ptrdiff_t len; + + err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL); + + sig->tag = ASN1_BIT_STRING; + + if (--len < 1 || *(*p)++ != 0) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL); + + sig->len = len; + sig->p = *p; + + *p += len; + + return SVN_NO_ERROR; +} + +/* + * X.509 v2/v3 unique identifier (not parsed) + */ +static svn_error_t * +x509_get_uid(const unsigned char **p, + const unsigned char *end, x509_buf * uid, int n) +{ + svn_error_t *err; + + if (*p == end) + return SVN_NO_ERROR; + + err = asn1_get_tag(p, end, &uid->len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n); + if (err) + { + if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + + return svn_error_trace(err); + } + + uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n; + uid->p = *p; + *p += uid->len; + + return SVN_NO_ERROR; +} + +/* + * X.509 v3 extensions (not parsed) + */ +static svn_error_t * +x509_get_ext(apr_array_header_t *dnsnames, + const unsigned char **p, + const unsigned char *end) +{ + svn_error_t *err; + ptrdiff_t len; + + if (*p == end) + return SVN_NO_ERROR; + + err = asn1_get_tag(p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3); + if (err) + { + /* If there aren't extensions that's ok they aren't required */ + if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + + return svn_error_trace(err); + } + + end = *p + len; + + SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE)); + + if (end != *p + len) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); + } + + while (*p < end) + { + ptrdiff_t ext_len; + const unsigned char *ext_start, *sna_end; + err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, + NULL); + ext_start = *p; + + err = asn1_get_tag(p, end, &len, ASN1_OID); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, + NULL); + + /* skip all extensions except SubjectAltName */ + if (!equal(*p, len, + OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1)) + { + *p += ext_len - (*p - ext_start); + continue; + } + *p += len; + + err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, + NULL); + + /* SubjectAltName ::= GeneralNames + + GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + + GeneralName ::= CHOICE { + other Name [0] OtherName, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, + directoryName [4] Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } */ + sna_end = *p + len; + + err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, + NULL); + + if (sna_end != *p + len) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); + } + + while (*p < sna_end) + { + err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC | + ASN1_PRIMITIVE | 2); + if (err) + { + /* not not a dNSName */ + if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) + { + svn_error_clear(err); + /* need to skip the tag and then find the length to + * skip to ignore this SNA entry. */ + (*p)++; + SVN_ERR(asn1_get_len(p, sna_end, &len)); + *p += len; + continue; + } + + return svn_error_trace(err); + } + else + { + /* We found a dNSName entry */ + x509_buf *dnsname = apr_palloc(dnsnames->pool, + sizeof(x509_buf)); + dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */ + dnsname->len = len; + dnsname->p = *p; + APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname; + } + + *p += len; + } + + } + + return SVN_NO_ERROR; +} + +/* Escape all non-ascii or control characters similar to + * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy(). + * All of the encoding formats somewhat overlap with ascii (BMPString + * and UniversalString are actually always wider so you'll end up + * with a bunch of escaped nul bytes, but ideally we don't get here + * for those). The result is always a nul-terminated C string. */ +static const char * +fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool) +{ + const char *end = src->data + src->len; + const char *p = src->data, *q; + svn_stringbuf_t *outstr; + char escaped_char[6]; /* ? \ u u u \0 */ + + for (q = p; q < end; q++) + { + if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q)) + break; + } + + if (q == end) + return src->data; + + outstr = svn_stringbuf_create_empty(result_pool); + while (1) + { + q = p; + + /* Traverse till either unsafe character or eos. */ + while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q)) + q++; + + /* copy chunk before marker */ + svn_stringbuf_appendbytes(outstr, p, q - p); + + if (q == end) + break; + + apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u", + (unsigned char) *q); + svn_stringbuf_appendcstr(outstr, escaped_char); + + p = q + 1; + } + + return outstr->data; +} + +/* Escape only NUL characters from a string that is presumed to + * be UTF-8 encoded and return a nul-terminated C string. */ +static const char * +nul_escape(const svn_string_t *src, apr_pool_t *result_pool) +{ + const char *end = src->data + src->len; + const char *p = src->data, *q; + svn_stringbuf_t *outstr; + + for (q = p; q < end; q++) + { + if (*q == '\0') + break; + } + + if (q == end) + return src->data; + + outstr = svn_stringbuf_create_empty(result_pool); + while (1) + { + q = p; + + /* Traverse till either unsafe character or eos. */ + while (q < end && *q != '\0') + q++; + + /* copy chunk before marker */ + svn_stringbuf_appendbytes(outstr, p, q - p); + + if (q == end) + break; + + svn_stringbuf_appendcstr(outstr, "?\\000"); + + p = q + 1; + } + + return outstr->data; +} + + +/* Convert an ISO-8859-1 (Latin-1) string to UTF-8. + ISO-8859-1 is a strict subset of Unicode. */ +static svn_error_t * +latin1_to_utf8(const svn_string_t **result, const svn_string_t *src, + apr_pool_t *result_pool) +{ + apr_int32_t *ucs4buf; + svn_membuf_t resultbuf; + apr_size_t length; + apr_size_t i; + svn_string_t *res; + + ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf)); + for (i = 0; i < src->len; ++i) + ucs4buf[i] = (unsigned char)(src->data[i]); + + svn_membuf__create(&resultbuf, 2 * src->len, result_pool); + SVN_ERR(svn_utf__encode_ucs4_string( + &resultbuf, ucs4buf, src->len, &length)); + + res = apr_palloc(result_pool, sizeof(*res)); + res->data = resultbuf.data; + res->len = length; + *result = res; + return SVN_NO_ERROR; +} + +/* Make a best effort to convert a X.509 name to a UTF-8 encoded + * string and return it. If we can't properly convert just do a + * fuzzy conversion so we have something to display. */ +static const char * +x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool) +{ + const svn_string_t *src_string; + const svn_string_t *utf8_string; + svn_error_t *err; + + src_string = svn_string_ncreate((const char *)name->val.p, + name->val.len, + result_pool); + switch (name->val.tag) + { + case ASN1_UTF8_STRING: + if (svn_utf__is_valid(src_string->data, src_string->len)) + return nul_escape(src_string, result_pool); + else + /* not a valid UTF-8 string, who knows what it is, + * so run it through the fuzzy_escape code. */ + return fuzzy_escape(src_string, result_pool); + break; + + /* Both BMP and UNIVERSAL should always be in Big Endian (aka + * network byte order). But rumor has it that there are certs + * out there with other endianess and even Byte Order Marks. + * If we actually run into these, we might need to do something + * about it. */ + + case ASN1_BMP_STRING: + if (0 != src_string->len % sizeof(apr_uint16_t)) + return fuzzy_escape(src_string, result_pool); + err = svn_utf__utf16_to_utf8(&utf8_string, + (const void*)(src_string->data), + src_string->len / sizeof(apr_uint16_t), + TRUE, result_pool, result_pool); + break; + + case ASN1_UNIVERSAL_STRING: + if (0 != src_string->len % sizeof(apr_int32_t)) + return fuzzy_escape(src_string, result_pool); + err = svn_utf__utf32_to_utf8(&utf8_string, + (const void*)(src_string->data), + src_string->len / sizeof(apr_int32_t), + TRUE, result_pool, result_pool); + break; + + /* Despite what all the IETF, ISO, ITU bits say everything out + * on the Internet that I can find treats this as ISO-8859-1. + * Even the name is misleading, it's not actually T.61. All the + * gory details can be found in the Character Sets section of: + * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt + */ + case ASN1_T61_STRING: + err = latin1_to_utf8(&utf8_string, src_string, result_pool); + break; + + /* This leaves two types out there in the wild. PrintableString, + * which is just a subset of ASCII and IA5 which is ASCII (though + * 0x24 '$' and 0x23 '#' may be defined with differnet symbols + * depending on the location, in practice it seems everyone just + * treats it as ASCII). Since these are just ASCII run through + * the fuzzy_escape code to deal with anything that isn't actually + * ASCII. There shouldn't be any other types here but if we find + * a cert with some other encoding, the best we can do is the + * fuzzy_escape(). Note: Technically IA5 isn't valid in this + * context, however in the real world it may pop up. */ + default: + return fuzzy_escape(src_string, result_pool); + } + + if (err) + { + svn_error_clear(err); + return fuzzy_escape(src_string, result_pool); + } + + return nul_escape(utf8_string, result_pool); +} + +static svn_error_t * +x509_name_to_certinfo(apr_array_header_t **result, + const x509_name *dn, + apr_pool_t *scratch_pool, + apr_pool_t *result_pool) +{ + const x509_name *name = dn; + + *result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *)); + + while (name != NULL) { + svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t)); + + attr->oid_len = name->oid.len; + attr->oid = apr_palloc(result_pool, attr->oid_len); + memcpy(attr->oid, name->oid.p, attr->oid_len); + attr->utf8_value = x509name_to_utf8_string(name, result_pool); + if (!attr->utf8_value) + /* this should never happen */ + attr->utf8_value = apr_pstrdup(result_pool, "??"); + APR_ARRAY_PUSH(*result, const svn_x509_name_attr_t *) = attr; + + name = name->next; + } + + return SVN_NO_ERROR; +} + +static svn_boolean_t +is_hostname(const char *str) +{ + apr_size_t i, len = strlen(str); + + for (i = 0; i < len; i++) + { + char c = str[i]; + + /* '-' is only legal when not at the start or end of a label */ + if (c == '-') + { + if (i + 1 != len) + { + if (str[i + 1] == '.') + return FALSE; /* '-' preceeds a '.' */ + } + else + return FALSE; /* '-' is at end of string */ + + /* determine the previous character. */ + if (i == 0) + return FALSE; /* '-' is at start of string */ + else + if (str[i - 1] == '.') + return FALSE; /* '-' follows a '.' */ + } + else if (c != '*' && c != '.' && !svn_ctype_isalnum(c)) + return FALSE; /* some character not allowed */ + } + + return TRUE; +} + +static const char * +x509parse_get_cn(apr_array_header_t *subject) +{ + int i; + + for (i = 0; i < subject->nelts; ++i) + { + const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(subject, i, const svn_x509_name_attr_t *); + if (equal(attr->oid, attr->oid_len, + SVN_X509_OID_COMMON_NAME, sizeof(SVN_X509_OID_COMMON_NAME) - 1)) + return attr->utf8_value; + } + + return NULL; +} + + +static void +x509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + ci->hostnames = NULL; + + if (crt->dnsnames->nelts > 0) + { + int i; + + ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts, + sizeof(const char*)); + + /* Subject Alt Names take priority */ + for (i = 0; i < crt->dnsnames->nelts; i++) + { + x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *); + const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p, + dnsname->len, + scratch_pool); + + APR_ARRAY_PUSH(ci->hostnames, const char*) + = fuzzy_escape(temp, result_pool); + } + } + else + { + /* no SAN then get the hostname from the CommonName on the cert */ + const char *utf8_value; + + utf8_value = x509parse_get_cn(ci->subject); + + if (utf8_value && is_hostname(utf8_value)) + { + ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*)); + APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value; + } + } +} + +/* + * Parse one certificate. + */ +svn_error_t * +svn_x509_parse_cert(svn_x509_certinfo_t **certinfo, + const char *buf, + apr_size_t buflen, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + ptrdiff_t len; + const unsigned char *p; + const unsigned char *end; + x509_cert *crt; + svn_x509_certinfo_t *ci; + + crt = apr_pcalloc(scratch_pool, sizeof(*crt)); + p = (const unsigned char *)buf; + len = buflen; + end = p + len; + + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + + if (len != (end - p)) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + } + + /* + * TBSCertificate ::= SEQUENCE { + */ + err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + + end = p + len; + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * signature AlgorithmIdentifier + */ + SVN_ERR(x509_get_version(&p, end, &crt->version)); + SVN_ERR(x509_get_serial(&p, end, &crt->serial)); + SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1)); + + crt->version++; + + if (crt->version > 3) + return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL); + + /* + * issuer Name + */ + err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + + SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool)); + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + */ + SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end, + scratch_pool)); + + /* + * subject Name + */ + err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + + SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool)); + + /* + * SubjectPublicKeyInfo ::= SEQUENCE + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if (err) + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + + /* Skip pubkey. */ + p += len; + + /* + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + */ + crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *)); + + /* Try to parse issuerUniqueID, subjectUniqueID and extensions for *every* + * version (X.509 v1, v2 and v3), not just v2 or v3. If they aren't present, + * we are fine, but we don't want to throw an error if they are. v1 and v2 + * certificates with the corresponding extra fields are ill-formed per RFC + * 5280 s. 4.1, but we suspect they could exist in the real world. Other + * X.509 parsers (e.g., within OpenSSL or Microsoft CryptoAPI) aren't picky + * about these certificates, and we also allow them. */ + SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1)); + SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2)); + SVN_ERR(x509_get_ext(crt->dnsnames, &p, end)); + + if (p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + } + + end = (const unsigned char*) buf + buflen; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2)); + + if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2)) + return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL); + + SVN_ERR(x509_get_sig(&p, end, &crt->sig)); + + if (p != end) + { + err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); + return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); + } + + ci = apr_pcalloc(result_pool, sizeof(*ci)); + + /* Get the subject name */ + SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject, + scratch_pool, result_pool)); + + /* Get the issuer name */ + SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer, + scratch_pool, result_pool)); + + /* Copy the validity range */ + ci->valid_from = crt->valid_from; + ci->valid_to = crt->valid_to; + + /* Calculate the SHA1 digest of the certificate, otherwise known as + the fingerprint */ + SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen, + result_pool)); + + /* Construct the array of host names */ + x509parse_get_hostnames(ci, crt, result_pool, scratch_pool); + + *certinfo = ci; + return SVN_NO_ERROR; +} + diff --git a/contrib/subversion/subversion/libsvn_subr/xml.c b/contrib/subversion/subversion/libsvn_subr/xml.c index a9d834a82..7f66b4531 100644 --- a/contrib/subversion/subversion/libsvn_subr/xml.c +++ b/contrib/subversion/subversion/libsvn_subr/xml.c @@ -34,6 +34,7 @@ #include "svn_ctype.h" #include "private/svn_utf_private.h" +#include "private/svn_subr_private.h" #ifdef SVN_HAVE_OLD_EXPAT #include @@ -45,6 +46,28 @@ #error Expat is unusable -- it has been compiled for wide characters #endif +const char * +svn_xml__compiled_version(void) +{ + static const char xml_version_str[] = APR_STRINGIFY(XML_MAJOR_VERSION) + "." APR_STRINGIFY(XML_MINOR_VERSION) + "." APR_STRINGIFY(XML_MICRO_VERSION); + + return xml_version_str; +} + +const char * +svn_xml__runtime_version(void) +{ + const char *expat_version = XML_ExpatVersion(); + + if (!strncmp(expat_version, "expat_", 6)) + expat_version += 6; + + return expat_version; +} + + /* The private internals for a parser object. */ struct svn_xml_parser_t { diff --git a/contrib/subversion/subversion/libsvn_wc/adm_crawler.c b/contrib/subversion/subversion/libsvn_wc/adm_crawler.c index e5935a274..ebdc75e10 100644 --- a/contrib/subversion/subversion/libsvn_wc/adm_crawler.c +++ b/contrib/subversion/subversion/libsvn_wc/adm_crawler.c @@ -69,6 +69,8 @@ restore_file(svn_wc__db_t *db, const char *local_abspath, svn_boolean_t use_commit_times, svn_boolean_t mark_resolved_text_conflict, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { svn_skel_t *work_item; @@ -86,12 +88,14 @@ restore_file(svn_wc__db_t *db, /* Run the work item immediately. */ SVN_ERR(svn_wc__wq_run(db, local_abspath, - NULL, NULL, /* ### nice to have cancel_func/baton */ + cancel_func, cancel_baton, scratch_pool)); /* Remove any text conflict */ if (mark_resolved_text_conflict) - SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, scratch_pool)); + SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); return SVN_NO_ERROR; } @@ -102,6 +106,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, svn_boolean_t use_commit_times, apr_pool_t *scratch_pool) { + /* ### If ever revved: Add cancel func. */ svn_wc__db_status_t status; svn_node_kind_t kind; svn_node_kind_t disk_kind; @@ -138,6 +143,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, if (kind == svn_node_file || kind == svn_node_symlink) SVN_ERR(restore_file(wc_ctx->db, local_abspath, use_commit_times, FALSE /*mark_resolved_text_conflict*/, + NULL, NULL /* cancel func, baton */, scratch_pool)); else SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); @@ -145,7 +151,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, return SVN_NO_ERROR; } -/* Try to restore LOCAL_ABSPATH of node type KIND and if successfull, +/* Try to restore LOCAL_ABSPATH of node type KIND and if successful, notify that the node is restored. Use DB for accessing the working copy. If USE_COMMIT_TIMES is set, then set working file's timestamp to last-commit-time. @@ -156,7 +162,10 @@ static svn_error_t * restore_node(svn_wc__db_t *db, const char *local_abspath, svn_node_kind_t kind, + svn_boolean_t mark_resolved_text_conflict, 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) @@ -165,7 +174,8 @@ restore_node(svn_wc__db_t *db, { /* Recreate file from text-base; mark any text conflict as resolved */ SVN_ERR(restore_file(db, local_abspath, use_commit_times, - TRUE /*mark_resolved_text_conflict*/, + mark_resolved_text_conflict, + cancel_func, cancel_baton, scratch_pool)); } else if (kind == svn_node_dir) @@ -293,11 +303,11 @@ report_revisions_and_depths(svn_wc__db_t *db, hi != NULL; hi = apr_hash_next(hi)) { - const char *child = svn__apr_hash_index_key(hi); + const char *child = apr_hash_this_key(hi); const char *this_report_relpath; const char *this_abspath; svn_boolean_t this_switched = FALSE; - struct svn_wc__db_base_info_t *ths = svn__apr_hash_index_val(hi); + struct svn_wc__db_base_info_t *ths = apr_hash_this_val(hi); if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); @@ -375,12 +385,13 @@ report_revisions_and_depths(svn_wc__db_t *db, svn_wc__db_status_t wrk_status; svn_node_kind_t wrk_kind; const svn_checksum_t *checksum; + svn_boolean_t conflicted; SVN_ERR(svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, db, this_abspath, iterpool, iterpool)); if ((wrk_status == svn_wc__db_status_normal @@ -399,8 +410,9 @@ report_revisions_and_depths(svn_wc__db_t *db, if (dirent_kind == svn_node_none) { SVN_ERR(restore_node(db, this_abspath, wrk_kind, - use_commit_times, notify_func, - notify_baton, iterpool)); + conflicted, use_commit_times, + cancel_func, cancel_baton, + notify_func, notify_baton, iterpool)); } } } @@ -708,12 +720,13 @@ svn_wc_crawl_revisions5(svn_wc_context_t *wc_ctx, svn_wc__db_status_t wrk_status; svn_node_kind_t wrk_kind; const svn_checksum_t *checksum; + svn_boolean_t conflicted; err = svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, 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); @@ -733,7 +746,8 @@ svn_wc_crawl_revisions5(svn_wc_context_t *wc_ctx, && (wrk_kind == svn_node_dir || checksum)) { SVN_ERR(restore_node(wc_ctx->db, local_abspath, - wrk_kind, use_commit_times, + wrk_kind, conflicted, use_commit_times, + cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); } @@ -877,7 +891,7 @@ read_handler_copy(void *baton, char *buffer, apr_size_t *len) { struct copying_stream_baton *btn = baton; - SVN_ERR(svn_stream_read(btn->source, buffer, len)); + SVN_ERR(svn_stream_read_full(btn->source, buffer, len)); return svn_stream_write(btn->target, buffer, len); } @@ -910,7 +924,8 @@ copying_stream(svn_stream_t *source, baton->target = target; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_copy); + svn_stream_set_read2(stream, NULL /* only full read support */, + read_handler_copy); svn_stream_set_close(stream, close_handler_copy); return stream; @@ -999,8 +1014,9 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, svn_checksum_t *verify_checksum; /* calc'd MD5 of BASE_STREAM */ svn_checksum_t *local_md5_checksum; /* calc'd MD5 of LOCAL_STREAM */ svn_checksum_t *local_sha1_checksum; /* calc'd SHA1 of LOCAL_STREAM */ - const char *new_pristine_tmp_abspath; + svn_wc__db_install_data_t *install_data = NULL; svn_error_t *err; + svn_error_t *err2; svn_stream_t *base_stream; /* delta source */ svn_stream_t *local_stream; /* delta target: LOCAL_ABSPATH transl. to NF */ @@ -1037,11 +1053,11 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, { svn_stream_t *new_pristine_stream; - SVN_ERR(svn_wc__open_writable_base(&new_pristine_stream, - &new_pristine_tmp_abspath, - NULL, &local_sha1_checksum, - db, local_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_pristine_prepare_install(&new_pristine_stream, + &install_data, + &local_sha1_checksum, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); local_stream = copying_stream(local_stream, new_pristine_stream, scratch_pool); } @@ -1097,7 +1113,15 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, scratch_pool, scratch_pool); /* Close the two streams to force writing the digest */ - err = svn_error_compose_create(err, svn_stream_close(base_stream)); + err2 = svn_stream_close(base_stream); + if (err2) + { + /* Set verify_checksum to NULL if svn_stream_close() returns error + because checksum will be uninitialized in this case. */ + verify_checksum = NULL; + err = svn_error_compose_create(err, err2); + } + err = svn_error_compose_create(err, svn_stream_close(local_stream)); /* If we have an error, it may be caused by a corrupt text base, @@ -1145,7 +1169,7 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, result_pool); if (new_text_base_sha1_checksum) { - SVN_ERR(svn_wc__db_pristine_install(db, new_pristine_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_install(install_data, local_sha1_checksum, local_md5_checksum, scratch_pool)); diff --git a/contrib/subversion/subversion/libsvn_wc/adm_files.c b/contrib/subversion/subversion/libsvn_wc/adm_files.c index 11ad277d9..67a1357d4 100644 --- a/contrib/subversion/subversion/libsvn_wc/adm_files.c +++ b/contrib/subversion/subversion/libsvn_wc/adm_files.c @@ -117,7 +117,7 @@ svn_wc__adm_child(const char *path, path, adm_dir_name, child, - NULL); + SVN_VA_NULL); } @@ -295,38 +295,6 @@ svn_wc__open_adm_stream(svn_stream_t **stream, } -svn_error_t * -svn_wc__open_writable_base(svn_stream_t **stream, - const char **temp_base_abspath, - svn_checksum_t **md5_checksum, - svn_checksum_t **sha1_checksum, - 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_ASSERT(svn_dirent_is_absolute(wri_abspath)); - - SVN_ERR(svn_wc__db_pristine_get_tempdir(&temp_dir_abspath, db, wri_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(svn_stream_open_unique(stream, - temp_base_abspath, - temp_dir_abspath, - svn_io_file_del_none, - result_pool, scratch_pool)); - if (md5_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, - svn_checksum_md5, FALSE, result_pool); - if (sha1_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, - svn_checksum_sha1, FALSE, result_pool); - - return SVN_NO_ERROR; -} - - - /*** Checking for and creating administrative subdirs. ***/ @@ -464,11 +432,14 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, db, local_abspath, scratch_pool, scratch_pool)); else - SVN_ERR(svn_wc__db_scan_base_repos(&db_repos_relpath, - &db_repos_root_url, - &db_repos_uuid, - db, local_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, + &db_repos_relpath, + &db_repos_root_url, + &db_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); } /* The caller gives us a URL which should match the entry. However, diff --git a/contrib/subversion/subversion/libsvn_wc/adm_files.h b/contrib/subversion/subversion/libsvn_wc/adm_files.h index 37121499b..8c94f74f2 100644 --- a/contrib/subversion/subversion/libsvn_wc/adm_files.h +++ b/contrib/subversion/subversion/libsvn_wc/adm_files.h @@ -112,30 +112,6 @@ svn_error_t *svn_wc__open_adm_stream(svn_stream_t **stream, apr_pool_t *scratch_pool); -/* Open a writable stream to a temporary (normal or revert) text base, - associated with the versioned file LOCAL_ABSPATH in DB. Set *STREAM to - the opened stream and *TEMP_BASE_ABSPATH to the path to the temporary - file. The temporary file will have an arbitrary unique name, in contrast - to the deterministic name that svn_wc__text_base_deterministic_tmp_path() - returns. - - Arrange that, on stream closure, *MD5_CHECKSUM and *SHA1_CHECKSUM will be - set to the MD-5 and SHA-1 checksums respectively of that file. - MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted. - - Allocate the new stream, path and checksums in RESULT_POOL. - */ -svn_error_t * -svn_wc__open_writable_base(svn_stream_t **stream, - const char **temp_base_abspath, - svn_checksum_t **md5_checksum, - svn_checksum_t **sha1_checksum, - svn_wc__db_t *db, - const char *wri_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - /* Blow away the admistrative directory associated with DIR_ABSPATH. For single-db this doesn't perform actual work unless the wcroot is passed. */ diff --git a/contrib/subversion/subversion/libsvn_wc/adm_ops.c b/contrib/subversion/subversion/libsvn_wc/adm_ops.c index a0f806140..3ccd2e7af 100644 --- a/contrib/subversion/subversion/libsvn_wc/adm_ops.c +++ b/contrib/subversion/subversion/libsvn_wc/adm_ops.c @@ -36,6 +36,7 @@ #include #include +#include "svn_private_config.h" #include "svn_types.h" #include "svn_pools.h" #include "svn_string.h" @@ -53,32 +54,32 @@ #include "conflicts.h" #include "workqueue.h" -#include "svn_private_config.h" -#include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.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; + /* Mapping (const char *) wcroot_abspath to svn_wc__db_commit_queue_t * */ + apr_hash_t *wc_queues; }; typedef struct committed_queue_item_t { const char *local_abspath; - svn_boolean_t recurse; - svn_boolean_t no_unlock; - svn_boolean_t keep_changelist; + svn_boolean_t recurse; /* Use legacy recursion */ + svn_boolean_t committed; /* Process the node as committed */ + svn_boolean_t remove_lock; /* Remove existing lock on node */ + svn_boolean_t remove_changelist; /* Remove changelist on node */ - /* The pristine text checksum. */ - const svn_checksum_t *sha1_checksum; + /* The pristine text checksum. NULL if the old value should be kept + and for directories */ + const svn_checksum_t *new_sha1_checksum; - apr_hash_t *new_dav_cache; + apr_hash_t *new_dav_cache; /* New DAV cache for the node */ } committed_queue_item_t; @@ -88,245 +89,6 @@ 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) @@ -357,76 +119,56 @@ svn_wc_committed_queue_create(apr_pool_t *pool) q = apr_palloc(pool, sizeof(*q)); q->pool = pool; - q->queue = apr_hash_make(pool); - q->have_recursive = FALSE; + q->wc_queues = apr_hash_make(pool); return q; } svn_error_t * -svn_wc_queue_committed3(svn_wc_committed_queue_t *queue, +svn_wc_queue_committed4(svn_wc_committed_queue_t *queue, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t recurse, + svn_boolean_t is_committed, 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; + const char *wcroot_abspath; + svn_wc__db_commit_queue_t *db_queue; 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; -} - + SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); -/* 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)) + db_queue = svn_hash_gets(queue->wc_queues, wcroot_abspath); + if (! db_queue) { - const committed_queue_item_t *qi = svn__apr_hash_index_val(hi); + wcroot_abspath = apr_pstrdup(queue->pool, wcroot_abspath); - if (qi == item) - continue; + SVN_ERR(svn_wc__db_create_commit_queue(&db_queue, + wc_ctx->db, wcroot_abspath, + queue->pool, scratch_pool)); - if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath, - NULL)) - return TRUE; + svn_hash_sets(queue->wc_queues, wcroot_abspath, db_queue); } - return FALSE; + return svn_error_trace( + svn_wc__db_commit_queue_add(db_queue, local_abspath, recurse, + is_committed, remove_lock, + remove_changelist, sha1_checksum, + svn_wc__prop_array_to_hash(wcprop_changes, + queue->pool), + queue->pool, scratch_pool)); } @@ -440,75 +182,44 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue, void *cancel_baton, apr_pool_t *scratch_pool) { - apr_array_header_t *sorted_queue; + apr_array_header_t *wcs; 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++) + /* Process the wc's in order of their paths. */ + wcs = svn_sort__hash(queue->wc_queues, svn_sort_compare_items_as_paths, + scratch_pool); + for (i = 0; i < wcs->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; + = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); + svn_wc__db_commit_queue_t *db_queue = sort_item->value; 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); - } + SVN_ERR(svn_wc__db_process_commit_queue(wc_ctx->db, db_queue, + new_revnum, new_date, rev_author, + iterpool)); } /* Make sure nothing happens if this function is called again. */ - apr_hash_clear(queue->queue); + apr_hash_clear(queue->wc_queues); /* 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)) + for (i = 0; i < wcs->nelts; i++) { - const char *wcroot_abspath = svn__apr_hash_index_key(hi); + const svn_sort__item_t *sort_item + = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); + const char *wcroot_abspath = sort_item->key; svn_pool_clear(iterpool); @@ -631,10 +342,12 @@ check_can_add_to_parent(const char **repos_root_url, 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)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + repos_root_url, repos_uuid, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + db, parent_abspath, + result_pool, scratch_pool)); } return SVN_NO_ERROR; @@ -887,11 +600,13 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, 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)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath, + &inner_repos_root_url, + &inner_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, local_abspath, + scratch_pool, scratch_pool)); if (strcmp(inner_repos_uuid, repos_uuid) || strcmp(repos_root_url, inner_repos_root_url)) @@ -993,9 +708,10 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, svn_error_t * -svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, +svn_wc_add_from_disk3(svn_wc_context_t *wc_ctx, const char *local_abspath, const apr_hash_t *props, + svn_boolean_t skip_checks, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -1014,7 +730,7 @@ svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__canonicalize_props( &new_props, - local_abspath, kind, props, FALSE /* skip_some_checks */, + local_abspath, kind, props, skip_checks, scratch_pool, scratch_pool)); props = new_props; } @@ -1177,10 +893,9 @@ svn_wc_add_lock2(svn_wc_context_t *wc_ctx, SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - /* ### Enable after fixing callers */ - /*SVN_ERR(svn_wc__write_check(wc_ctx->db, + SVN_ERR(svn_wc__write_check(wc_ctx->db, svn_dirent_dirname(local_abspath, scratch_pool), - scratch_pool));*/ + scratch_pool)); db_lock.token = lock->token; db_lock.owner = lock->owner; @@ -1227,16 +942,20 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { svn_error_t *err; - const svn_string_t *needs_lock; + svn_skel_t *work_item; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - /* ### Enable after fixing callers */ - /*SVN_ERR(svn_wc__write_check(wc_ctx->db, + SVN_ERR(svn_wc__write_check(wc_ctx->db, svn_dirent_dirname(local_abspath, scratch_pool), - scratch_pool));*/ + scratch_pool)); - err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool); + SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, work_item, + scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) @@ -1250,24 +969,9 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, 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; + return svn_error_trace(svn_wc__wq_run(wc_ctx->db, local_abspath, + NULL, NULL /* cancel*/, + scratch_pool)); } @@ -1321,9 +1025,8 @@ get_node_changelist(const char *local_abspath, 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)) + if (!b->clhash + || (changelist && svn_hash_gets(b->clhash, changelist) != NULL)) SVN_ERR(b->callback_func(b->callback_baton, local_abspath, changelist, scratch_pool)); diff --git a/contrib/subversion/subversion/libsvn_wc/cleanup.c b/contrib/subversion/subversion/libsvn_wc/cleanup.c index afe73718d..491ca932a 100644 --- a/contrib/subversion/subversion/libsvn_wc/cleanup.c +++ b/contrib/subversion/subversion/libsvn_wc/cleanup.c @@ -81,6 +81,9 @@ status_dummy_callback(void *baton, static svn_error_t * cleanup_internal(svn_wc__db_t *db, const char *dir_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t vacuum_pristines, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) @@ -98,7 +101,7 @@ cleanup_internal(svn_wc__db_t *db, 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)); + SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, -1, break_locks, scratch_pool)); /* Run our changes before the subdirectories. We may not have to recurse if we blow away a subdir. */ @@ -117,7 +120,7 @@ cleanup_internal(svn_wc__db_t *db, svn_wc__check_wcroot() as that function, will just return true once we start sharing databases with externals. */ - if (is_wcroot) + if (is_wcroot && vacuum_pristines) { /* 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 @@ -128,17 +131,20 @@ cleanup_internal(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_cleanup(db, dir_abspath, 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)); + if (fix_recorded_timestamps) + { + /* 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)); @@ -146,39 +152,59 @@ cleanup_internal(svn_wc__db_t *db, 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, +svn_wc_cleanup4(svn_wc_context_t *wc_ctx, const char *local_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, 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; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR_ASSERT(wc_ctx != NULL); - /* 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)); + if (break_locks) + { + /* We'll handle everything manually. */ - SVN_ERR(cleanup_internal(db, local_abspath, cancel_func, cancel_baton, + /* Close the existing database (if any) to avoid problems with + exclusive database usage */ + SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, local_abspath, + scratch_pool)); + + SVN_ERR(svn_wc__db_open(&db, + NULL /* ### config */, FALSE, FALSE, + scratch_pool, scratch_pool)); + } + else + db = wc_ctx->db; + + SVN_ERR(cleanup_internal(db, local_abspath, + break_locks, + fix_recorded_timestamps, + vacuum_pristines, + 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)); + if (clear_dav_cache) + 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)); + if (vacuum_pristines) + 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)); + if (break_locks) + SVN_ERR(svn_wc__db_close(db)); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_wc/conflicts.c b/contrib/subversion/subversion/libsvn_wc/conflicts.c index 8bd54105a..ae0b348c3 100644 --- a/contrib/subversion/subversion/libsvn_wc/conflicts.c +++ b/contrib/subversion/subversion/libsvn_wc/conflicts.c @@ -477,8 +477,7 @@ svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, hi; hi = apr_hash_next(hi)) { - svn_skel__prepend_str(apr_pstrdup(result_pool, - svn__apr_hash_index_key(hi)), + svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)), conflict_names, result_pool); } @@ -509,7 +508,7 @@ svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, } /* A map for svn_wc_conflict_reason_t values. */ -static const svn_token_map_t local_change_map[] = +static const svn_token_map_t reason_map[] = { { "edited", svn_wc_conflict_reason_edited }, { "obstructed", svn_wc_conflict_reason_obstructed }, @@ -523,7 +522,7 @@ static const svn_token_map_t local_change_map[] = { NULL } }; -static const svn_token_map_t incoming_change_map[] = +static const svn_token_map_t action_map[] = { { "edited", svn_wc_conflict_action_edit }, { "added", svn_wc_conflict_action_add }, @@ -536,8 +535,8 @@ 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, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, const char *move_src_op_root_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -550,12 +549,12 @@ svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */ - SVN_ERR_ASSERT(local_change == svn_wc_conflict_reason_moved_away + SVN_ERR_ASSERT(reason == 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 + if (reason == svn_wc_conflict_reason_moved_away && move_src_op_root_abspath) { const char *move_src_op_root_relpath; @@ -569,13 +568,11 @@ svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, 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(action_map, action), + tree_conflict, result_pool); - svn_skel__prepend_str( - svn_token__to_word(local_change_map, local_change), - tree_conflict, result_pool); + svn_skel__prepend_str(svn_token__to_word(reason_map, reason), + tree_conflict, result_pool); /* Tree conflicts have no marker files */ markers = svn_skel__make_empty_list(result_pool); @@ -931,8 +928,8 @@ svn_wc__conflict_read_prop_conflict(const char **marker_abspath, } svn_error_t * -svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change, - svn_wc_conflict_action_t *incoming_change, +svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason, + svn_wc_conflict_action_t *action, const char **move_src_op_root_abspath, svn_wc__db_t *db, const char *wri_abspath, @@ -957,28 +954,28 @@ svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change, c = c->next; /* Skip markers */ { - int value = svn_token__from_mem(local_change_map, c->data, c->len); + int value = svn_token__from_mem(reason_map, c->data, c->len); - if (local_change) + if (reason) { if (value != SVN_TOKEN_UNKNOWN) - *local_change = value; + *reason = value; else - *local_change = svn_wc_conflict_reason_edited; + *reason = svn_wc_conflict_reason_edited; } is_moved_away = (value == svn_wc_conflict_reason_moved_away); } c = c->next; - if (incoming_change) + if (action) { - int value = svn_token__from_mem(incoming_change_map, c->data, c->len); + int value = svn_token__from_mem(action_map, c->data, c->len); if (value != SVN_TOKEN_UNKNOWN) - *incoming_change = value; + *action = value; else - *incoming_change = svn_wc_conflict_action_edit; + *action = svn_wc_conflict_action_edit; } c = c->next; @@ -1050,69 +1047,8 @@ svn_wc__conflict_read_markers(const apr_array_header_t **markers, /* -------------------------------------------------------------------- */ -/* 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, @@ -1141,10 +1077,8 @@ svn_wc__conflict_create_markers(svn_skel_t **work_items, /* 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 */ + - Store the name in the conflict_skel + - Create a WQ item that fills the marker with the expected data */ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); @@ -1176,65 +1110,9 @@ svn_wc__conflict_create_markers(svn_skel_t **work_items, 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)); - } + SVN_ERR(svn_wc__wq_build_prej_install(work_items, + db, local_abspath, + scratch_pool, scratch_pool)); } return SVN_NO_ERROR; @@ -1264,6 +1142,7 @@ static svn_error_t * generate_propconflict(svn_boolean_t *conflict_remains, svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, svn_wc_operation_t operation, const svn_wc_conflict_version_t *left_version, const svn_wc_conflict_version_t *right_version, @@ -1274,29 +1153,18 @@ generate_propconflict(svn_boolean_t *conflict_remains, const svn_string_t *incoming_new_val, svn_wc_conflict_resolver_func2_t conflict_func, void *conflict_baton, + svn_cancel_func_t cancel_func, + void *cancel_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, + kind, propname, scratch_pool); cdesc->operation = operation; @@ -1313,6 +1181,7 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_io_file_del_on_pool_cleanup, scratch_pool)); cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_working = working_val; } if (incoming_new_val) @@ -1323,7 +1192,11 @@ generate_propconflict(svn_boolean_t *conflict_remains, incoming_new_val->len, svn_io_file_del_on_pool_cleanup, scratch_pool)); - cdesc->their_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_incoming_new = incoming_new_val; } if (!base_val && !incoming_old_val) @@ -1332,7 +1205,6 @@ generate_propconflict(svn_boolean_t *conflict_remains, 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)) { @@ -1354,7 +1226,6 @@ generate_propconflict(svn_boolean_t *conflict_remains, 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; @@ -1391,6 +1262,9 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_io_file_del_on_pool_cleanup, scratch_pool)); cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_base = base_val; + cdesc->prop_value_incoming_old = incoming_old_val; + if (working_val && incoming_new_val) { svn_stream_t *mergestream; @@ -1398,17 +1272,22 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_diff_file_options_t *options = svn_diff_file_options_create(scratch_pool); - SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->merged_file, + SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath, 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, + SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff, + conflict_base_val, working_val, incoming_new_val, NULL, NULL, NULL, NULL, - svn_diff_conflict_display_modified_latest, scratch_pool)); + svn_diff_conflict_display_modified_latest, + cancel_func, cancel_baton, scratch_pool)); SVN_ERR(svn_stream_close(mergestream)); + + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + cdesc->their_abspath = cdesc->prop_reject_abspath; } } @@ -1427,10 +1306,8 @@ generate_propconflict(svn_boolean_t *conflict_remains, cdesc->reason = svn_wc_conflict_reason_edited; /* Invoke the interactive conflict callback. */ - { - SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, - scratch_pool)); - } + SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, + scratch_pool)); if (result == NULL) { *conflict_remains = TRUE; @@ -1476,18 +1353,24 @@ generate_propconflict(svn_boolean_t *conflict_remains, { svn_stringbuf_t *merged_stringbuf; - if (!cdesc->merged_file && !result->merged_file) + if (!cdesc->merged_file + && (!result->merged_file && !result->merged_value)) 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); + if (result->merged_value) + new_value = result->merged_value; + else + { + 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; } @@ -1513,172 +1396,113 @@ generate_propconflict(svn_boolean_t *conflict_remains, return SVN_NO_ERROR; } -/* Resolve the text conflict on DB/LOCAL_ABSPATH in the manner specified - * by CHOICE. +/* Perform a 3-way merge in which conflicts are expected, showing the + * conflicts in the way specified by STYLE, and using MERGE_OPTIONS. * - * 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. + * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET + * and RIGHT_ABSPATH. The output is stored in a new temporary file, + * whose name is put into *CHOSEN_ABSPATH. + * + * The output file will be deleted according to DELETE_WHEN. If + * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL. + * + * DB and WRI_ABSPATH are used to choose a directory for the output file. + * + * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +merge_showing_conflicts(const char **chosen_abspath, + svn_wc__db_t *db, + const char *wri_abspath, + svn_diff_conflict_display_style_t style, + const apr_array_header_t *merge_options, + const char *left_abspath, + const char *detranslated_target, + const char *right_abspath, + svn_io_file_del_t delete_when, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *temp_dir; + svn_stream_t *chosen_stream; + svn_diff_t *diff; + 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)); + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, + wri_abspath, + scratch_pool, scratch_pool)); + /* We need to open the stream in RESULT_POOL because that controls the + * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also + * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care + * about the stream itself.) */ + SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath, + temp_dir, delete_when, + result_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_merge3(chosen_stream, diff, + left_abspath, + detranslated_target, + right_abspath, + NULL, NULL, NULL, NULL, /* markers */ + style, cancel_func, cancel_baton, + scratch_pool)); + SVN_ERR(svn_stream_close(chosen_stream)); + + return SVN_NO_ERROR; +} + +/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the + * working copy at DB/WRI_ABSPATH. * - * 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'. + * 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. * - * 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. + * 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 * -eval_text_conflict_func_result(svn_skel_t **work_items, - svn_boolean_t *is_resolved, +remove_artifact_file_if_exists(svn_skel_t **work_items, + svn_boolean_t *file_found, 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, + const char *wri_abspath, + const char *artifact_file_abspath, 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 (artifact_file_abspath) { - /* 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; - } + svn_node_kind_t node_kind; - /* 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: + SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind, + scratch_pool)); + if (node_kind == svn_node_file) { - /* Assume conflict remains. */ - *is_resolved = FALSE; - return SVN_NO_ERROR; + 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; } } - 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 @@ -1721,154 +1545,294 @@ save_merge_result(svn_skel_t **work_item, } -/* 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. + +/* Resolve the text conflict in CONFLICT, which is currently recorded + * on DB/LOCAL_ABSPATH in the manner specified by CHOICE. * - * 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. + * 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). * - * If the callback returns a resolution other than 'postpone', then - * perform that requested resolution and prepare to mark the conflict - * as resolved. + * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise + * (which is only if CHOICE is 'postpone') to false. * - * 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. + * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by + * the conflict resolver. * - * 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. + * MERGE_OPTIONS allows customizing the diff handling when using + * per hunk conflict resolving. */ 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) +build_text_conflict_resolve_items(svn_skel_t **work_items, + svn_boolean_t *found_artifact, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict, + svn_wc_conflict_choice_t choice, + const char *merged_file, + svn_boolean_t save_merged, + const apr_array_header_t *merge_options, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc_conflict_result_t *result; + const char *mine_abspath; + const char *their_old_abspath; + const char *their_abspath; svn_skel_t *work_item; - svn_wc_conflict_description2_t *cdesc; - apr_hash_t *props; - const char *mime_type; + const char *install_from_abspath = NULL; + svn_boolean_t remove_source = FALSE; *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); - 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 (found_artifact) + *found_artifact = FALSE; - if (result->save_merged) + SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, + &their_old_abspath, + &their_abspath, + db, local_abspath, + conflict, + scratch_pool, scratch_pool)); + + if (save_merged) + SVN_ERR(save_merge_result(work_items, + db, local_abspath, + merged_file + ? merged_file + : local_abspath, + result_pool, scratch_pool)); + + if (choice == svn_wc_conflict_choose_postpone) + return SVN_NO_ERROR; + + switch (choice) { - 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 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 = their_old_abspath; + break; + } + case svn_wc_conflict_choose_theirs_full: + { + install_from_abspath = their_abspath; + break; + } + case svn_wc_conflict_choose_mine_full: + { + install_from_abspath = mine_abspath; + break; + } + case svn_wc_conflict_choose_theirs_conflict: + case svn_wc_conflict_choose_mine_conflict: + { + svn_diff_conflict_display_style_t style + = choice == svn_wc_conflict_choose_theirs_conflict + ? svn_diff_conflict_display_latest + : svn_diff_conflict_display_modified; + + SVN_ERR(merge_showing_conflicts(&install_from_abspath, + db, local_abspath, + style, merge_options, + their_old_abspath, + mine_abspath, + their_abspath, + /* ### why not same as other caller? */ + svn_io_file_del_none, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + remove_source = 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_file + ? merged_file + : local_abspath; + break; + } + case svn_wc_conflict_choose_postpone: + { + /* Assume conflict remains. */ + return SVN_NO_ERROR; + } + default: + SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone); } - if (result->choice != svn_wc_conflict_choose_postpone) + 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)); + + /* ### It would be nice if we could somehow pass RECORD_FILEINFO + as true in some easy cases. */ + 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); + + if (remove_source) { - 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)); + 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); } - else - *was_resolved = FALSE; + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + their_old_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + their_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + mine_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); return SVN_NO_ERROR; } +/* Set *DESC to a new description of the text conflict in + * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return + * an error. + * + * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION, + * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and + * MIME_TYPE for the corresponding fields of *DESC. + * + * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary + * allocations. */ 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) +read_text_conflict_desc(svn_wc_conflict_description2_t **desc, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict_skel, + const char *mime_type, + 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) +{ + *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool); + (*desc)->mime_type = mime_type; + (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE; + (*desc)->operation = operation; + (*desc)->src_left_version = left_version; + (*desc)->src_right_version = right_version; + + 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); + + return SVN_NO_ERROR; +} + +/* Set *CONFLICT_DESC to a new description of the tree conflict in + * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return + * an error. + * + * Use 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_tree_conflict_desc(svn_wc_conflict_description2_t **desc, + svn_wc__db_t *db, + const char *local_abspath, + svn_node_kind_t node_kind, + const svn_skel_t *conflict_skel, + 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) { - svn_node_kind_t tc_kind; + svn_node_kind_t local_kind; + svn_wc_conflict_reason_t reason; + svn_wc_conflict_action_t action; - if (left_version) - tc_kind = left_version->node_kind; - else if (right_version) - tc_kind = right_version->node_kind; + SVN_ERR(svn_wc__conflict_read_tree_conflict( + &reason, &action, NULL, + db, local_abspath, conflict_skel, scratch_pool, scratch_pool)); + + if (reason == svn_wc_conflict_reason_missing) + local_kind = svn_node_none; + else if (reason == svn_wc_conflict_reason_unversioned || + reason == svn_wc_conflict_reason_obstructed) + SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool)); + else if (action == svn_wc_conflict_action_delete + && left_version + && (operation == svn_wc_operation_update + ||operation == svn_wc_operation_switch) + && (reason == svn_wc_conflict_reason_deleted + || reason == svn_wc_conflict_reason_moved_away)) + { + /* We have nothing locally to take the kind from */ + local_kind = left_version->node_kind; + } else - tc_kind = svn_node_file; /* Avoid assertion */ + local_kind = node_kind; - *desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind, + *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind, operation, left_version, right_version, result_pool); - (*desc)->reason = local_change; - (*desc)->action = incoming_change; + (*desc)->reason = reason; + (*desc)->action = action; return SVN_NO_ERROR; } +/* Forward definition */ +static svn_error_t * +resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict, + svn_wc_conflict_choice_t conflict_choice, + apr_hash_t *resolve_later, + 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_error_t * svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, const svn_skel_t *conflict_skel, const apr_array_header_t *merge_options, svn_wc_conflict_resolver_func2_t resolver_func, @@ -1936,7 +1900,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_boolean_t conflict_remains = TRUE; svn_pool_clear(iterpool); @@ -1945,7 +1909,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, SVN_ERR(cancel_func(cancel_baton)); SVN_ERR(generate_propconflict(&conflict_remains, - db, local_abspath, + db, local_abspath, kind, operation, left_version, right_version, @@ -1963,6 +1927,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, ? svn_hash_gets(their_props, propname) : NULL, resolver_func, resolver_baton, + cancel_func, cancel_baton, iterpool)); if (conflict_remains) @@ -1974,75 +1939,96 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath, scratch_pool)); } + svn_pool_destroy(iterpool); } 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_wc_conflict_description2_t *desc; + apr_hash_t *props; + svn_wc_conflict_result_t *result; - 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, + SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, scratch_pool)); - if (was_resolved) + SVN_ERR(read_text_conflict_desc(&desc, + db, local_abspath, conflict_skel, + svn_prop_get_value(props, + SVN_PROP_MIME_TYPE), + operation, left_version, right_version, + scratch_pool, scratch_pool)); + + + 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(resolver_func(&result, desc, resolver_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")); + + SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved, + db, local_abspath, + conflict_skel, result->choice, + result->merged_file, + result->save_merged, + merge_options, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + + if (result->choice != svn_wc_conflict_choose_postpone) { - 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)); + 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)); } } 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_boolean_t resolved; + svn_node_kind_t node_kind; - SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, - &incoming_change, - NULL, - db, local_abspath, - conflict_skel, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE, + TRUE, FALSE, 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)); + SVN_ERR(read_tree_conflict_desc(&desc, + db, local_abspath, node_kind, + conflict_skel, + operation, left_version, right_version, + 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. */ + if (result == NULL) + return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Conflict callback violated API:" + " returned no results")); + + /* Pass retry hash to avoid erroring out on cases where update + can continue safely. ### Need notify handling */ + if (result->choice != svn_wc_conflict_choose_postpone) + SVN_ERR(resolve_tree_conflict_on_node(&resolved, + db, local_abspath, conflict_skel, + result->choice, + apr_hash_make(scratch_pool), + NULL, NULL, /* ### notify */ + cancel_func, cancel_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2050,7 +2036,8 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, /* Read all property conflicts contained in CONFLICT_SKEL into * individual conflict descriptions, and append those descriptions - * to the CONFLICTS array. + * to the CONFLICTS array. If there is no property conflict in + * CONFLICT_SKEL, return an error. * * If NOT create_tempfiles, always create a legacy property conflict * descriptor. @@ -2061,27 +2048,36 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, * 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) +read_prop_conflict_descs(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; + const char *prop_reject_abspath; + apr_hash_t *base_props; 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_boolean_t prop_conflicted; - SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file, + 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) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath, &my_props, &their_old_props, &their_props, @@ -2090,7 +2086,9 @@ read_prop_conflicts(apr_array_header_t *conflicts, conflict_skel, scratch_pool, scratch_pool)); - if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0) + prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath); + + if (apr_hash_count(conflicted_props) == 0) { /* Legacy prop conflict with only a .reject file. */ svn_wc_conflict_description2_t *desc; @@ -2099,26 +2097,31 @@ read_prop_conflicts(apr_array_header_t *conflicts, 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); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ + desc->their_abspath = desc->prop_reject_abspath; 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; + APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; return SVN_NO_ERROR; } + if (operation == svn_wc_operation_merge) + SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath, + result_pool, scratch_pool)); + else + base_props = NULL; 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); + const char *propname = apr_hash_this_key(hi); svn_string_t *old_value; svn_string_t *my_value; svn_string_t *their_value; @@ -2126,10 +2129,10 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_pool_clear(iterpool); - desc = svn_wc_conflict_description_create_prop2(local_abspath, - node_kind, - propname, - result_pool); + desc = svn_wc_conflict_description_create_prop2(local_abspath, + node_kind, + propname, + result_pool); desc->operation = operation; desc->src_left_version = left_version; @@ -2157,26 +2160,30 @@ read_prop_conflicts(apr_array_header_t *conflicts, 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); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ + desc->their_abspath = desc->prop_reject_abspath; + + desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname) + : desc->prop_value_incoming_old; - /* ### 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 (create_tempfiles) + { + 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)); + } + + desc->prop_value_working = svn_string_dup(my_value, result_pool); } if (their_value) @@ -2184,15 +2191,19 @@ read_prop_conflicts(apr_array_header_t *conflicts, 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)); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + if (create_tempfiles) + { + 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)); + } + + desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool); } if (old_value) @@ -2200,15 +2211,20 @@ read_prop_conflicts(apr_array_header_t *conflicts, 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)); + if (create_tempfiles) + { + 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)); + } + + desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool); } - APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc; + APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; } svn_pool_destroy(iterpool); @@ -2217,13 +2233,15 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_error_t * svn_wc__read_conflicts(const apr_array_header_t **conflicts, + svn_skel_t **conflict_skel, svn_wc__db_t *db, const char *local_abspath, svn_boolean_t create_tempfiles, + svn_boolean_t only_tree_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_skel_t *conflict_skel; + svn_skel_t *the_conflict_skel; apr_array_header_t *cflcts; svn_boolean_t prop_conflicted; svn_boolean_t text_conflicted; @@ -2232,301 +2250,82 @@ svn_wc__read_conflicts(const apr_array_header_t **conflicts, 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)); + svn_node_kind_t node_kind; + apr_hash_t *props; if (!conflict_skel) + conflict_skel = &the_conflict_skel; + + SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props, + db, local_abspath, + (conflict_skel == &the_conflict_skel) + ? scratch_pool + : result_pool, + scratch_pool)); + + if (!*conflict_skel) { /* Some callers expect not NULL */ *conflicts = apr_array_make(result_pool, 0, - sizeof(svn_wc_conflict_description2_t*));; + 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; + db, local_abspath, *conflict_skel, + result_pool, scratch_pool)); - 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; + cflcts = apr_array_make(result_pool, 4, + sizeof(svn_wc_conflict_description2_t *)); - SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working, - &conflict_old, - &conflict_new, - db, local_abspath, conflicts, - 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 *); - /* Handle automatic conflict resolution before the temporary files are - * deleted, if necessary. */ - switch (conflict_choice) + if (prop_conflicted && !only_tree_conflict) { - 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")); + SVN_ERR(read_prop_conflict_descs(cflcts, + db, local_abspath, *conflict_skel, + create_tempfiles, node_kind, + operation, left_version, right_version, + result_pool, scratch_pool)); } - if (auto_resolve_src) + if (text_conflicted && !only_tree_conflict) { - 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_wc_conflict_description2_t *desc; - 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); + SVN_ERR(read_text_conflict_desc(&desc, + db, local_abspath, *conflict_skel, + svn_prop_get_value(props, + SVN_PROP_MIME_TYPE), + operation, left_version, right_version, + result_pool, scratch_pool)); + APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc; } - /* 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); + if (tree_conflicted) + { + svn_wc_conflict_description2_t *desc; - 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(read_tree_conflict_desc(&desc, + db, local_abspath, node_kind, + *conflict_skel, + operation, left_version, right_version, + result_pool, 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)); + APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc; + } + *conflicts = cflcts; return SVN_NO_ERROR; } + +/*** Resolving a conflict automatically ***/ + /* * Resolve the property conflicts found in DB/LOCAL_ABSPATH according * to CONFLICT_CHOICE. @@ -2566,9 +2365,11 @@ static svn_error_t * resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *conflicts, const char *conflicted_propname, svn_wc_conflict_choice_t conflict_choice, const char *merged_file, + const svn_string_t *merged_value, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) @@ -2581,18 +2382,13 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, 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; + apr_hash_t *actual_props; + svn_boolean_t resolved_all, resolved_all_prop; *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)); @@ -2605,12 +2401,35 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, db, local_abspath, conflicts, scratch_pool, scratch_pool)); + if (!conflicted_props) + { + /* We have a pre 1.8 property conflict. Just mark it resolved */ + + SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve, + db, local_abspath, prop_reject_file, + scratch_pool, 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; + } + + if (conflicted_propname[0] != '\0' + && !svn_hash_gets(conflicted_props, conflicted_propname)) + { + return SVN_NO_ERROR; /* This property is not conflicted! */ + } + 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; + SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, + scratch_pool, scratch_pool)); + /* 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 @@ -2636,21 +2455,23 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, resolve_from = their_props; break; case svn_wc_conflict_choose_merged: - if (merged_file && conflicted_propname[0] != '\0') + if ((merged_file || merged_value) && conflicted_propname[0] != '\0') { - apr_hash_t *actual_props; - svn_stream_t *stream; - svn_string_t *merged_propval; + resolve_from = apr_hash_copy(scratch_pool, actual_props); - SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); - resolve_from = actual_props; + if (!merged_value) + { + svn_stream_t *stream; + svn_string_t *merged_propval; - 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); + 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)); + + merged_value = merged_propval; + } + svn_hash_sets(resolve_from, conflicted_propname, merged_value); } else resolve_from = NULL; @@ -2660,47 +2481,97 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, _("Invalid 'conflict_result' argument")); } - if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from) + + if (resolve_from) { apr_hash_index_t *hi; - apr_hash_t *actual_props; + apr_hash_t *apply_on_props; - SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); + if (conflicted_propname[0] == '\0') + { + /* Apply to all conflicted properties */ + apply_on_props = conflicted_props; + } + else + { + /* Apply to a single property */ + apply_on_props = apr_hash_make(scratch_pool); + svn_hash_sets(apply_on_props, conflicted_propname, ""); + } - for (hi = apr_hash_first(scratch_pool, conflicted_props); + /* Apply the selected changes */ + for (hi = apr_hash_first(scratch_pool, apply_on_props); hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_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)); } + /*else the user accepted the properties as-is */ + + /* This function handles conflicted_propname "" as resolving + all property conflicts... Just what we need here */ + SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts, + db, local_abspath, + FALSE, conflicted_propname, + FALSE, + scratch_pool, scratch_pool)); - /* Legacy behavior: Only report property conflicts as resolved when the - property reject file exists + if (!resolved_all) + { + /* Are there still property conflicts left? (or only...) */ + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted, + NULL, db, local_abspath, conflicts, + scratch_pool, scratch_pool)); - If not the UI shows the conflict as already resolved - (and in this case we just remove the in-db conflict) */ + resolved_all_prop = (! prop_conflicted); + } + else + { + resolved_all_prop = TRUE; + conflicts = NULL; + } - { - svn_skel_t *work_item; + if (resolved_all_prop) + { + /* Legacy behavior: Only report property conflicts as resolved when the + property reject file exists - 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); - } + 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_items, did_resolve, + db, local_abspath, + prop_reject_file, + scratch_pool, scratch_pool)); + } + else + { + /* Create a new prej file, based on the remaining conflicts */ + SVN_ERR(svn_wc__wq_build_prej_install(&work_items, + db, local_abspath, + scratch_pool, scratch_pool)); + *did_resolve = TRUE; /* We resolved a property conflict */ + } + + /* This installs the updated conflict skel */ + SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props, + FALSE, conflicts, work_items, + scratch_pool)); + + if (resolved_all) + { + /* Remove the whole conflict. Should probably be integrated + into the op_set_props() call */ + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + FALSE, TRUE, FALSE, + NULL, 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)); @@ -2715,12 +2586,18 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, * 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. + * + * If the conflict can't be resolved yet because another tree conflict is + * blocking a storage location, store the tree conflict in the RESOLVE_LATER + * hash. */ static svn_error_t * resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_wc__db_t *db, const char *local_abspath, + const svn_skel_t *conflicts, svn_wc_conflict_choice_t conflict_choice, + apr_hash_t *resolve_later, svn_wc_notify_func2_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, @@ -2729,24 +2606,20 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, { 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; + const char *src_op_root_abspath; *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, + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, + &src_op_root_abspath, db, local_abspath, conflicts, scratch_pool, scratch_pool)); @@ -2754,6 +2627,7 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, if (operation == svn_wc_operation_update || operation == svn_wc_operation_switch) { + svn_error_t *err; if (reason == svn_wc_conflict_reason_deleted || reason == svn_wc_conflict_reason_replaced) { @@ -2761,21 +2635,88 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, { /* 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)); + + if (action != svn_wc_conflict_action_delete) + { + SVN_ERR(svn_wc__db_op_break_moved_away( + db, local_abspath, src_op_root_abspath, TRUE, + notify_func, notify_baton, + scratch_pool)); + *did_resolve = TRUE; + return SVN_NO_ERROR; /* Marked resolved by function*/ + } + /* else # The move is/moves are already broken */ + + *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. + svn_skel_t *new_conflicts; + + /* Raise moved-away conflicts on any children moved out of + * this directory, and leave this directory as-is. + * * 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( + err = svn_wc__db_op_raise_moved_away( db, local_abspath, notify_func, notify_baton, - scratch_pool)); - *did_resolve = TRUE; + scratch_pool); + + if (err) + { + const char *dup_abspath; + + if (!resolve_later + || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE) + return svn_error_trace(err); + + svn_error_clear(err); + dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later), + local_abspath); + + svn_hash_sets(resolve_later, dup_abspath, dup_abspath); + + return SVN_NO_ERROR; /* Retry after other conflicts */ + } + + /* We might now have a moved-away on *this* path, let's + try to resolve that directly if that is the case */ + SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (new_conflicts) + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, + &tree_conflicted, + db, local_abspath, + new_conflicts, + scratch_pool, + scratch_pool)); + + if (!new_conflicts || !tree_conflicted) + { + /* TC is marked resolved by calling + svn_wc__db_resolve_delete_raise_moved_away */ + *did_resolve = TRUE; + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, + &src_op_root_abspath, + db, local_abspath, + new_conflicts, + scratch_pool, + scratch_pool)); + + if (reason != svn_wc_conflict_reason_moved_away) + { + *did_resolve = TRUE; + return SVN_NO_ERROR; /* We fixed one, but... */ + } + + conflicts = new_conflicts; + /* Fall through in moved_away handling */ } else return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, @@ -2786,8 +2727,9 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_dirent_local_style(local_abspath, scratch_pool)); } - else if (reason == svn_wc_conflict_reason_moved_away - && action == svn_wc_conflict_action_edit) + + 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 @@ -2795,12 +2737,31 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, * 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, + err = svn_wc__db_update_moved_away_conflict_victim( + db, local_abspath, src_op_root_abspath, + operation, action, reason, cancel_func, cancel_baton, - scratch_pool)); - *did_resolve = TRUE; + notify_func, notify_baton, + scratch_pool); + + if (err) + { + const char *dup_abspath; + + if (!resolve_later + || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE) + return svn_error_trace(err); + + svn_error_clear(err); + dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later), + local_abspath); + + svn_hash_sets(resolve_later, dup_abspath, dup_abspath); + + return SVN_NO_ERROR; /* Retry after other conflicts */ + } + else + *did_resolve = TRUE; } else if (conflict_choice == svn_wc_conflict_choose_merged) { @@ -2808,14 +2769,12 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, * 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)); + SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath, + src_op_root_abspath, TRUE, + notify_func, notify_baton, + scratch_pool)); *did_resolve = TRUE; + return SVN_NO_ERROR; /* Conflict is marked resolved */ } else return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, @@ -2826,22 +2785,57 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_dirent_local_style(local_abspath, scratch_pool)); } + else if (reason == svn_wc_conflict_reason_moved_away + && action != svn_wc_conflict_action_edit) + { + /* action added is impossible, because that would imply that + something was added, but before that already moved... + (which would imply a replace) */ + SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete + || action == svn_wc_conflict_action_replace); + + if (conflict_choice == svn_wc_conflict_choose_merged) + { + /* Whatever was moved is removed at its original location by the + update. That must also remove the recording of the move, so + we don't have to do anything here. */ + + *did_resolve = TRUE; + } + else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) + { + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, + NULL, + _("Tree conflict can only be " + "resolved to 'working' state; " + "'%s' is no longer moved"), + svn_dirent_local_style(local_abspath, + scratch_pool)); + } + } } - if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged) + if (! *did_resolve) { - /* 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)); + if (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)); + } + else + *did_resolve = TRUE; } + SVN_ERR_ASSERT(*did_resolve); + 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, @@ -2852,16 +2846,33 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_error_t * svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { - svn_boolean_t ignored_result; + svn_skel_t *work_items; + svn_skel_t *conflict; - 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_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (!conflict) + return SVN_NO_ERROR; + + SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL, + db, local_abspath, conflict, + svn_wc_conflict_choose_merged, + NULL, FALSE, NULL, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE, + work_items, scratch_pool)); + + return svn_error_trace(svn_wc__wq_run(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); } svn_error_t * @@ -2870,11 +2881,20 @@ svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db, apr_pool_t *scratch_pool) { svn_boolean_t ignored_result; + svn_skel_t *conflicts; + + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (!conflicts) + return SVN_NO_ERROR; return svn_error_trace(resolve_prop_conflict_on_node( &ignored_result, - db, local_abspath, "", - svn_wc_conflict_choose_merged, NULL, + db, local_abspath, conflicts, "", + svn_wc_conflict_choose_merged, + NULL, NULL, NULL, NULL, scratch_pool)); } @@ -2894,8 +2914,39 @@ struct conflict_status_walker_baton void *cancel_baton; svn_wc_notify_func2_t notify_func; void *notify_baton; + svn_boolean_t resolved_one; + apr_hash_t *resolve_later; }; +/* Implements svn_wc_notify_func2_t to collect new conflicts caused by + resolving a tree conflict. */ +static void +tree_conflict_collector(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *pool) +{ + struct conflict_status_walker_baton *cswb = baton; + + if (cswb->notify_func) + cswb->notify_func(cswb->notify_baton, notify, pool); + + if (cswb->resolve_later + && (notify->action == svn_wc_notify_tree_conflict + || notify->prop_state == svn_wc_notify_state_conflicted + || notify->content_state == svn_wc_notify_state_conflicted)) + { + if (!svn_hash_gets(cswb->resolve_later, notify->path)) + { + const char *dup_path; + + dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later), + notify->path); + + svn_hash_sets(cswb->resolve_later, dup_path, dup_path); + } + } +} + /* Implements svn_wc_status4_t to walk all conflicts to resolve. */ static svn_error_t * @@ -2911,13 +2962,17 @@ conflict_status_walker(void *baton, apr_pool_t *iterpool; int i; svn_boolean_t resolved = FALSE; + svn_skel_t *conflict; if (!status->conflicted) return SVN_NO_ERROR; iterpool = svn_pool_create(scratch_pool); - SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE, + SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict, + db, local_abspath, + (cswb->conflict_func != NULL) /* tmp files */, + FALSE /* only tree conflicts */, scratch_pool, iterpool)); for (i = 0; i < conflicts->nelts; i++) @@ -2925,11 +2980,15 @@ conflict_status_walker(void *baton, 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; + svn_wc_conflict_result_t *result = NULL; + svn_skel_t *work_items; cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *); - if ((cd->kind == svn_wc_conflict_kind_property && !cswb->resolve_prop) + if ((cd->kind == svn_wc_conflict_kind_property + && (!cswb->resolve_prop + || (*cswb->resolve_prop != '\0' + && strcmp(cswb->resolve_prop, cd->property_name) != 0))) || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text) || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree)) { @@ -2940,8 +2999,6 @@ conflict_status_walker(void *baton, 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 " @@ -2951,8 +3008,6 @@ conflict_status_walker(void *baton, iterpool, iterpool)); my_choice = result->choice; - merged_file = result->merged_file; - /* ### Bug: ignores result->save_merged */ } @@ -2962,54 +3017,57 @@ conflict_status_walker(void *baton, 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, + local_abspath, conflict, my_choice, - cswb->notify_func, - cswb->notify_baton, + cswb->resolve_later, + tree_conflict_collector, + cswb, cswb->cancel_func, cswb->cancel_baton, iterpool)); - resolved = TRUE; + if (did_resolve) + 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; + SVN_ERR(build_text_conflict_resolve_items( + &work_items, + &resolved, + db, local_abspath, conflict, + my_choice, + result ? result->merged_file + : NULL, + result ? result->save_merged + : FALSE, + NULL /* merge_options */, + cswb->cancel_func, + cswb->cancel_baton, + iterpool, iterpool)); + + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + TRUE, FALSE, FALSE, + work_items, iterpool)); + SVN_ERR(svn_wc__wq_run(db, local_abspath, + cswb->cancel_func, cswb->cancel_baton, + iterpool)); 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, + conflict, cd->property_name, my_choice, - merged_file, + result + ? result->merged_file + : NULL, + result + ? result->merged_value + : NULL, cswb->cancel_func, cswb->cancel_baton, iterpool)); @@ -3032,6 +3090,9 @@ conflict_status_walker(void *baton, iterpool), iterpool); + if (resolved) + cswb->resolved_one = TRUE; + svn_pool_destroy(iterpool); return SVN_NO_ERROR; @@ -3056,13 +3117,8 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, 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.")); + apr_pool_t *iterpool = NULL; + svn_error_t *err; /* ### Just a versioned check? */ /* Conflicted is set to allow invoking on actual only nodes */ @@ -3095,6 +3151,11 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, cswb.notify_func = notify_func; cswb.notify_baton = notify_baton; + cswb.resolved_one = FALSE; + cswb.resolve_later = (depth != svn_depth_empty) + ? apr_hash_make(scratch_pool) + : NULL; + if (notify_func) notify_func(notify_baton, svn_wc_create_notify(local_abspath, @@ -3102,16 +3163,103 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, 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)); + 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 we got new tree conflicts (or delayed conflicts) during the initial + walk, we now walk them one by one as closure. */ + while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later)) + { + apr_hash_index_t *hi; + svn_wc_status3_t *status = NULL; + const char *tc_abspath = NULL; + + if (iterpool) + svn_pool_clear(iterpool); + else + iterpool = svn_pool_create(scratch_pool); + + hi = apr_hash_first(scratch_pool, cswb.resolve_later); + cswb.resolve_later = apr_hash_make(scratch_pool); + cswb.resolved_one = FALSE; + + for (; hi && !err; hi = apr_hash_next(hi)) + { + const char *relpath; + svn_pool_clear(iterpool); + + tc_abspath = apr_hash_this_key(hi); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + relpath = svn_dirent_skip_ancestor(local_abspath, + tc_abspath); + + if (!relpath + || (depth >= svn_depth_empty + && depth < svn_depth_infinity + && strchr(relpath, '/'))) + { + continue; + } + + SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath, + iterpool, iterpool)); + + if (depth == svn_depth_files + && status->kind == svn_node_dir) + continue; + + err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, + status, scratch_pool)); + } + + /* None of the remaining conflicts got resolved, and non did provide + an error... + + We can fix that if we disable the 'resolve_later' option... + */ + if (!cswb.resolved_one && !err && tc_abspath + && apr_hash_count(cswb.resolve_later)) + { + /* Run the last resolve operation again. We still have status + and tc_abspath for that one. */ + + cswb.resolve_later = NULL; /* Produce proper error! */ + + /* Recreate the error */ + err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, + status, scratch_pool)); + + SVN_ERR_ASSERT(err != NULL); + + err = svn_error_createf( + SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, + _("Unable to resolve pending conflict on '%s'"), + svn_dirent_local_style(tc_abspath, scratch_pool)); + break; + } + } + + if (iterpool) + svn_pool_destroy(iterpool); + + if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE) + err = svn_error_createf( + SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, + _("Unable to resolve conflicts on '%s'"), + svn_dirent_local_style(local_abspath, scratch_pool)); + + SVN_ERR(err); if (notify_func) notify_func(notify_baton, @@ -3155,7 +3303,7 @@ svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice, { svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result)); result->choice = choice; - result->merged_file = merged_file; + result->merged_file = apr_pstrdup(pool, merged_file); result->save_merged = FALSE; /* If we add more fields to svn_wc_conflict_result_t, add them here. */ diff --git a/contrib/subversion/subversion/libsvn_wc/conflicts.h b/contrib/subversion/subversion/libsvn_wc/conflicts.h index 839e8a0d4..0a9324b89 100644 --- a/contrib/subversion/subversion/libsvn_wc/conflicts.h +++ b/contrib/subversion/subversion/libsvn_wc/conflicts.h @@ -419,6 +419,7 @@ svn_wc__conflict_create_markers(svn_skel_t **work_item, svn_error_t * svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, const svn_skel_t *conflict_skel, const apr_array_header_t *merge_options, svn_wc_conflict_resolver_func2_t resolver_func, @@ -432,6 +433,8 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, svn_error_t * svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool); /* Mark as resolved any prop conflicts on the node at DB/LOCAL_ABSPATH. */ diff --git a/contrib/subversion/subversion/libsvn_wc/copy.c b/contrib/subversion/subversion/libsvn_wc/copy.c index 1e7d7cf08..30a0db58c 100644 --- a/contrib/subversion/subversion/libsvn_wc/copy.c +++ b/contrib/subversion/subversion/libsvn_wc/copy.c @@ -42,6 +42,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" +/* #define RECORD_MIXED_MOVE */ /*** Code. ***/ @@ -50,7 +51,14 @@ 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. */ + that no copy was made. + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. + RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of + SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + + These values will be used to avoid unneeded work. + */ static svn_error_t * copy_to_tmpdir(svn_skel_t **work_item, svn_node_kind_t *kind, @@ -60,6 +68,9 @@ copy_to_tmpdir(svn_skel_t **work_item, const char *tmpdir_abspath, svn_boolean_t file_copy, svn_boolean_t unversioned, + const svn_io_dirent2_t *dirent, + svn_filesize_t recorded_size, + apr_time_t recorded_time, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, @@ -74,8 +85,14 @@ copy_to_tmpdir(svn_skel_t **work_item, *work_item = NULL; - SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, - scratch_pool)); + if (dirent) + { + *kind = dirent->kind; + is_special = dirent->special; + } + else + SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, + scratch_pool)); if (*kind == svn_node_none) { return SVN_NO_ERROR; @@ -104,9 +121,21 @@ copy_to_tmpdir(svn_skel_t **work_item, 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 (dirent + && dirent->kind == svn_node_file + && recorded_size != SVN_INVALID_FILESIZE + && recorded_size == dirent->filesize + && recorded_time == dirent->mtime) + { + modified = FALSE; /* Recorded matches on-disk. Easy out */ + } + else + { + 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? */ @@ -117,6 +146,15 @@ copy_to_tmpdir(svn_skel_t **work_item, return SVN_NO_ERROR; } } + else if (*kind == svn_node_dir && !file_copy) + { + /* Just build a new direcory from the workqueue */ + SVN_ERR(svn_wc__wq_build_dir_install(work_item, + db, dst_abspath, + 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 @@ -172,7 +210,14 @@ copy_to_tmpdir(svn_skel_t **work_item, versioned file itself. This also works for versioned symlinks that are stored in the db as - svn_node_file with svn:special set. */ + svn_node_file with svn:special set. + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. + RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of + SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + + These values will be used to avoid unneeded work. +*/ static svn_error_t * copy_versioned_file(svn_wc__db_t *db, const char *src_abspath, @@ -182,6 +227,9 @@ copy_versioned_file(svn_wc__db_t *db, svn_boolean_t metadata_only, svn_boolean_t conflicted, svn_boolean_t is_move, + const svn_io_dirent2_t *dirent, + svn_filesize_t recorded_size, + apr_time_t recorded_time, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -210,8 +258,9 @@ copy_versioned_file(svn_wc__db_t *db, 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)); + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, src_abspath, + scratch_pool, scratch_pool)); err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL, db, src_abspath, conflict, @@ -248,6 +297,7 @@ copy_versioned_file(svn_wc__db_t *db, dst_abspath, tmpdir_abspath, TRUE /* file_copy */, handle_as_unversioned /* unversioned */, + dirent, recorded_size, recorded_time, cancel_func, cancel_baton, scratch_pool, scratch_pool)); } @@ -265,10 +315,6 @@ copy_versioned_file(svn_wc__db_t *db, 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; @@ -282,6 +328,8 @@ copy_versioned_file(svn_wc__db_t *db, data in addition to copying the directory. WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root) + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. */ static svn_error_t * copy_versioned_dir(svn_wc__db_t *db, @@ -291,6 +339,7 @@ copy_versioned_dir(svn_wc__db_t *db, const char *tmpdir_abspath, svn_boolean_t metadata_only, svn_boolean_t is_move, + const svn_io_dirent2_t *dirent, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -314,6 +363,7 @@ copy_versioned_dir(svn_wc__db_t *db, tmpdir_abspath, FALSE /* file_copy */, FALSE /* unversioned */, + dirent, SVN_INVALID_FILESIZE, 0, cancel_func, cancel_baton, scratch_pool, scratch_pool)); } @@ -353,6 +403,7 @@ copy_versioned_dir(svn_wc__db_t *db, SVN_ERR(svn_wc__db_read_children_info(&versioned_children, &conflicted_children, db, src_abspath, + FALSE /* base_tree_only */, scratch_pool, iterpool)); for (hi = apr_hash_first(scratch_pool, versioned_children); hi; @@ -366,8 +417,8 @@ copy_versioned_dir(svn_wc__db_t *db, 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_name = apr_hash_this_key(hi); + info = apr_hash_this_val(hi); child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool); child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool); @@ -394,6 +445,12 @@ copy_versioned_dir(svn_wc__db_t *db, tmpdir_abspath, metadata_only, info->conflicted, is_move, + disk_children + ? svn_hash_gets(disk_children, + child_name) + : NULL, + info->recorded_size, + info->recorded_time, cancel_func, cancel_baton, NULL, NULL, iterpool)); @@ -403,6 +460,10 @@ copy_versioned_dir(svn_wc__db_t *db, child_src_abspath, child_dst_abspath, dst_op_root_abspath, tmpdir_abspath, metadata_only, is_move, + disk_children + ? svn_hash_gets(disk_children, + child_name) + : NULL, cancel_func, cancel_baton, NULL, NULL, iterpool)); else @@ -421,7 +482,7 @@ copy_versioned_dir(svn_wc__db_t *db, child_dst_abspath, dst_op_root_abspath, is_move, NULL, iterpool)); - /* Don't recurse on children while all we do is creating not-present + /* Don't recurse on children when all we do is creating not-present children */ } else if (info->status == svn_wc__db_status_incomplete) @@ -467,7 +528,7 @@ copy_versioned_dir(svn_wc__db_t *db, 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 *name = apr_hash_this_key(hi); const char *unver_src_abspath, *unver_dst_abspath; svn_skel_t *work_item; @@ -488,6 +549,7 @@ copy_versioned_dir(svn_wc__db_t *db, SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath, unver_dst_abspath, tmpdir_abspath, TRUE /* recursive */, TRUE /* unversioned */, + NULL, SVN_INVALID_FILESIZE, 0, cancel_func, cancel_baton, scratch_pool, iterpool)); @@ -507,10 +569,10 @@ copy_versioned_dir(svn_wc__db_t *db, * 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. */ + * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded + * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */ static svn_error_t * -copy_or_move(svn_boolean_t *move_degraded_to_copy, +copy_or_move(svn_boolean_t *record_move_on_delete, svn_wc_context_t *wc_ctx, const char *src_abspath, const char *dst_abspath, @@ -533,6 +595,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, svn_boolean_t within_one_wc; svn_wc__db_status_t src_status; svn_error_t *err; + svn_filesize_t recorded_size; + apr_time_t recorded_time; SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); @@ -550,7 +614,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, 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, NULL, NULL, NULL, NULL, NULL, + &recorded_size, &recorded_time, NULL, &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, db, src_abspath, scratch_pool, scratch_pool); @@ -643,10 +708,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, 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)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + &src_repos_root_url, + &src_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, src_abspath, + scratch_pool, scratch_pool)); } if (!dst_repos_root_url) @@ -660,10 +728,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, 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)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + &dst_repos_root_url, + &dst_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, dstdir_abspath, + scratch_pool, scratch_pool)); } if (strcmp(src_repos_root_url, dst_repos_root_url) != 0 @@ -751,8 +822,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, if (is_move && !within_one_wc) { - if (move_degraded_to_copy) - *move_degraded_to_copy = TRUE; + if (record_move_on_delete) + *record_move_on_delete = FALSE; is_move = FALSE; } @@ -768,6 +839,7 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath, tmpdir_abspath, metadata_only, conflicted, is_move, + NULL, recorded_size, recorded_time, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool); @@ -795,14 +867,17 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, scratch_pool), min_rev, max_rev); +#ifndef RECORD_MIXED_MOVE is_move = FALSE; - if (move_degraded_to_copy) - *move_degraded_to_copy = TRUE; + if (record_move_on_delete) + *record_move_on_delete = FALSE; +#endif } } err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath, tmpdir_abspath, metadata_only, is_move, + NULL /* dirent */, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool); @@ -871,7 +946,8 @@ remove_node_conflict_markers(svn_wc__db_t *db, { svn_skel_t *conflict; - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, src_abspath, scratch_pool, scratch_pool)); /* Do we have conflict markers that should be removed? */ @@ -923,6 +999,8 @@ static svn_error_t * remove_all_conflict_markers(svn_wc__db_t *db, const char *src_dir_abspath, const char *dst_dir_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -936,14 +1014,18 @@ remove_all_conflict_markers(svn_wc__db_t *db, artillery. */ SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, src_dir_abspath, + FALSE /* base_tree_only */, 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); + const char *name = apr_hash_this_key(hi); + struct svn_wc__db_info_t *info = apr_hash_this_val(hi); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); if (info->conflicted) { @@ -961,6 +1043,7 @@ remove_all_conflict_markers(svn_wc__db_t *db, db, svn_dirent_join(src_dir_abspath, name, iterpool), svn_dirent_join(dst_dir_abspath, name, iterpool), + cancel_func, cancel_baton, iterpool)); } } @@ -982,7 +1065,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { svn_wc__db_t *db = wc_ctx->db; - svn_boolean_t move_degraded_to_copy = FALSE; + svn_boolean_t record_on_delete = TRUE; svn_node_kind_t kind; svn_boolean_t conflicted; @@ -994,7 +1077,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, svn_dirent_dirname(dst_abspath, scratch_pool), scratch_pool)); - SVN_ERR(copy_or_move(&move_degraded_to_copy, + SVN_ERR(copy_or_move(&record_on_delete, wc_ctx, src_abspath, dst_abspath, TRUE /* metadata_only */, TRUE /* is_move */, @@ -1018,7 +1101,25 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, 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_error_t *err; + + err = svn_error_trace(svn_io_file_rename(src_abspath, dst_abspath, + scratch_pool)); + + /* Let's try if we can keep wc.db consistent even when the move + fails. Deleting the target is a wc.db only operation, while + going forward (delaying the error) would try to change + conflict markers, which might also fail. */ + if (err) + return svn_error_trace( + svn_error_compose_create( + err, + svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE, + NULL, NULL, cancel_func, cancel_baton, + NULL, NULL, + scratch_pool))); + } SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1030,6 +1131,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, if (kind == svn_node_dir) SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath, + cancel_func, cancel_baton, scratch_pool)); if (conflicted) @@ -1045,7 +1147,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, } SVN_ERR(svn_wc__db_op_delete(db, src_abspath, - move_degraded_to_copy ? NULL : dst_abspath, + record_on_delete ? dst_abspath : NULL, TRUE /* delete_dir_externals */, NULL /* conflict */, NULL /* work_items */, cancel_func, cancel_baton, diff --git a/contrib/subversion/subversion/libsvn_wc/crop.c b/contrib/subversion/subversion/libsvn_wc/crop.c index a8d5ce257..3a46b42d4 100644 --- a/contrib/subversion/subversion/libsvn_wc/crop.c +++ b/contrib/subversion/subversion/libsvn_wc/crop.c @@ -53,7 +53,7 @@ crop_children(svn_wc__db_t *db, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { const apr_array_header_t *children; apr_pool_t *iterpool; @@ -65,7 +65,7 @@ crop_children(svn_wc__db_t *db, if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - iterpool = svn_pool_create(pool); + iterpool = svn_pool_create(scratch_pool); if (dir_depth == svn_depth_unknown) dir_depth = svn_depth_infinity; @@ -76,8 +76,8 @@ crop_children(svn_wc__db_t *db, iterpool)); /* Looping over current directory's SVN entries: */ - SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, pool, - iterpool)); + SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -86,6 +86,8 @@ crop_children(svn_wc__db_t *db, svn_wc__db_status_t child_status; svn_node_kind_t kind; svn_depth_t child_depth; + svn_boolean_t have_work; + svn_depth_t remove_below; svn_pool_clear(iterpool); @@ -96,86 +98,80 @@ crop_children(svn_wc__db_t *db, NULL,NULL, NULL, NULL, &child_depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, &have_work, db, child_abspath, iterpool, iterpool)); - if (child_status == svn_wc__db_status_server_excluded || - child_status == svn_wc__db_status_excluded || - child_status == svn_wc__db_status_not_present) + if (have_work) + { + svn_boolean_t modified, all_deletes; + + if (child_status != svn_wc__db_status_deleted) + continue; /* Leave local additions alone */ + + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + db, child_abspath, FALSE, + cancel_func, cancel_baton, + iterpool)); + + if (modified && !all_deletes) + continue; /* Something interesting is still there */ + } + + remove_below = (kind == svn_node_dir) + ? svn_depth_immediates + : svn_depth_files; + + if ((child_status == svn_wc__db_status_server_excluded || + child_status == svn_wc__db_status_excluded || + child_status == svn_wc__db_status_not_present)) { - svn_depth_t remove_below = (kind == svn_node_dir) - ? svn_depth_immediates - : svn_depth_files; if (new_depth < remove_below) SVN_ERR(svn_wc__db_base_remove(db, child_abspath, FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, + FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, iterpool)); - continue; + continue; /* No recurse */ } - else if (kind == svn_node_file) + + if (new_depth < remove_below) { - if (new_depth == svn_depth_empty) - SVN_ERR(svn_wc__db_op_remove_node(NULL, - db, child_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, - NULL, NULL, + svn_boolean_t modified, all_deletes; + + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + db, child_abspath, FALSE, cancel_func, cancel_baton, iterpool)); - else - continue; - } - else if (kind == svn_node_dir) - { - if (new_depth < svn_depth_immediates) - { - SVN_ERR(svn_wc__db_op_remove_node(NULL, - db, child_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, - NULL, NULL, - cancel_func, cancel_baton, - iterpool)); - } - else + if (!modified || all_deletes) { - SVN_ERR(crop_children(db, - child_abspath, - child_depth, - svn_depth_empty, - notify_func, - notify_baton, - cancel_func, - cancel_baton, - iterpool)); - continue; + SVN_ERR(svn_wc__db_base_remove(db, child_abspath, + FALSE, FALSE, FALSE, + SVN_INVALID_REVNUM, + NULL, NULL, iterpool)); + if (notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify(child_abspath, + svn_wc_notify_delete, + iterpool); + (*notify_func)(notify_baton, notify, iterpool); + } + + continue; /* No recurse */ } - } - else - { - return svn_error_createf - (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown node kind for '%s'"), - svn_dirent_local_style(child_abspath, iterpool)); + + /* Fall through: recurse:*/ } - if (notify_func) + if (kind == svn_node_dir) { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify(child_abspath, - svn_wc_notify_delete, - iterpool); - (*notify_func)(notify_baton, notify, iterpool); + SVN_ERR(crop_children(db, child_abspath, + child_depth, svn_depth_empty, + notify_func, notify_baton, + cancel_func, cancel_baton, + iterpool)); } } @@ -197,6 +193,8 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, svn_wc__db_status_t status; svn_node_kind_t kind; svn_revnum_t revision; + svn_depth_t depth; + svn_boolean_t modified, all_deletes; const char *repos_relpath, *repos_root, *repos_uuid; SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL, @@ -221,7 +219,7 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath, &repos_root, &repos_uuid, NULL, NULL, NULL, - NULL, 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, wc_ctx->db, local_abspath, @@ -258,29 +256,41 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, break; /* Ok to exclude */ } - /* Remove all working copy data below local_abspath */ - SVN_ERR(svn_wc__db_op_remove_node(NULL, - wc_ctx->db, local_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - revision, - svn_wc__db_status_excluded, - kind, - NULL, NULL, - cancel_func, cancel_baton, - scratch_pool)); - - SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, - cancel_func, cancel_baton, - scratch_pool)); - - if (notify_func) + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + wc_ctx->db, local_abspath, FALSE, + cancel_func, cancel_baton, + scratch_pool)); + + if (!modified || all_deletes) + { + /* Remove all working copy data below local_abspath */ + SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, + FALSE /* keep_working */, + FALSE, TRUE, + revision, + NULL, NULL, + scratch_pool)); + + SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); + + if (notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify(local_abspath, + svn_wc_notify_exclude, + scratch_pool); + notify_func(notify_baton, notify, scratch_pool); + } + } + else { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify(local_abspath, - svn_wc_notify_exclude, - scratch_pool); - notify_func(notify_baton, notify, scratch_pool); + /* Do the next best thing: retry below this path */ + SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty, + notify_func, notify_baton, + cancel_func, cancel_baton, + scratch_pool)); } return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/libsvn_wc/delete.c b/contrib/subversion/subversion/libsvn_wc/delete.c index 37c8af081..82ae9382d 100644 --- a/contrib/subversion/subversion/libsvn_wc/delete.c +++ b/contrib/subversion/subversion/libsvn_wc/delete.c @@ -135,7 +135,8 @@ create_delete_wq_items(svn_skel_t **work_items, const apr_array_header_t *markers; int i; - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath, @@ -431,9 +432,6 @@ svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, db, local_abspath, destroy_wf /* destroy_wc */, destroy_wf /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, NULL, NULL, cancel_func, cancel_baton, scratch_pool)); diff --git a/contrib/subversion/subversion/libsvn_wc/deprecated.c b/contrib/subversion/subversion/libsvn_wc/deprecated.c index 79cdb3030..dcb5e42f7 100644 --- a/contrib/subversion/subversion/libsvn_wc/deprecated.c +++ b/contrib/subversion/subversion/libsvn_wc/deprecated.c @@ -127,7 +127,7 @@ gather_traversal_info(svn_wc_context_t *wc_ctx, hi; hi = apr_hash_next(hi)) { - const char *node_abspath = svn__apr_hash_index_key(hi); + const char *node_abspath = apr_hash_this_key(hi); const char *relpath; relpath = svn_dirent_join(path, @@ -137,11 +137,11 @@ gather_traversal_info(svn_wc_context_t *wc_ctx, if (gather_as_old) svn_hash_sets(traversal_info->externals_old, relpath, - svn__apr_hash_index_val(hi)); + apr_hash_this_val(hi)); if (gather_as_new) svn_hash_sets(traversal_info->externals_new, relpath, - svn__apr_hash_index_val(hi)); + apr_hash_this_val(hi)); svn_hash_sets(traversal_info->depths, relpath, svn_hash_gets(ambient_depths, node_abspath)); @@ -652,6 +652,24 @@ svn_wc_get_pristine_contents(svn_stream_t **contents, return svn_error_trace(svn_wc_context_destroy(wc_ctx)); } +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) +{ + return svn_error_trace( + svn_wc_queue_committed4(queue, wc_ctx, local_abspath, + recurse, TRUE /* is_committed */, + wcprop_changes, remove_lock, + remove_changelist, sha1_checksum, + scratch_pool)); +} svn_error_t * svn_wc_queue_committed2(svn_wc_committed_queue_t *queue, @@ -668,7 +686,9 @@ svn_wc_queue_committed2(svn_wc_committed_queue_t *queue, const char *local_abspath; const svn_checksum_t *sha1_checksum = NULL; - SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL, + svn_wc__adm_get_db(adm_access), + scratch_pool)); SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); if (md5_checksum != NULL) @@ -759,15 +779,11 @@ svn_wc_process_committed4(const char *path, const char *local_abspath; const svn_checksum_t *md5_checksum; const svn_checksum_t *sha1_checksum = NULL; - apr_time_t new_date; - apr_hash_t *wcprop_changes_hash; + svn_wc_context_t *wc_ctx; + svn_wc_committed_queue_t *queue; SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - - if (rev_date) - SVN_ERR(svn_time_from_cstring(&new_date, rev_date, pool)); - else - new_date = 0; + SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL, db, pool)); if (digest) md5_checksum = svn_checksum__from_digest_md5(digest, pool); @@ -790,15 +806,20 @@ svn_wc_process_committed4(const char *path, SVN_ERR(err); } - wcprop_changes_hash = svn_wc__prop_array_to_hash(wcprop_changes, pool); - SVN_ERR(svn_wc__process_committed_internal(db, local_abspath, recurse, TRUE, - new_revnum, new_date, rev_author, - wcprop_changes_hash, - !remove_lock, !remove_changelist, - sha1_checksum, NULL, pool)); + queue = svn_wc_committed_queue_create(pool); + SVN_ERR(svn_wc_queue_committed3(queue, wc_ctx, local_abspath, recurse, + wcprop_changes, remove_lock, + remove_changelist, + sha1_checksum /* or NULL if not modified + or directory */, + pool)); + + SVN_ERR(svn_wc_process_committed_queue2(queue, wc_ctx, + new_revnum, rev_date, rev_author, + NULL, NULL /* cancel */, + pool)); - /* Run the log file(s) we just created. */ - return svn_error_trace(svn_wc__wq_run(db, local_abspath, NULL, NULL, pool)); + return svn_error_trace(svn_wc_context_destroy(wc_ctx)); } @@ -924,6 +945,19 @@ svn_wc_delete(const char *path, compat_call_notify_func, &nb, pool); } +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_ERR(svn_wc_add_from_disk3(wc_ctx, local_abspath, props, FALSE, + notify_func, notify_baton, scratch_pool)); + return SVN_NO_ERROR; +} + svn_error_t * svn_wc_add_from_disk(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -1025,6 +1059,30 @@ svn_wc_add(const char *path, compat_call_notify_func, &nb, pool); } +/*** From revert.c ***/ +svn_error_t * +svn_wc_revert4(svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t use_commit_times, + 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) +{ + return svn_error_trace(svn_wc_revert5(wc_ctx, local_abspath, + depth, + use_commit_times, + changelist_filter, + FALSE /* clear_changelists */, + FALSE /* metadata_only */, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); +} + svn_error_t * svn_wc_revert3(const char *path, svn_wc_adm_access_t *parent_access, @@ -1966,16 +2024,37 @@ svn_wc_get_diff_editor6(const svn_delta_editor_t **editor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + const svn_diff_tree_processor_t *diff_processor; + + /* --git implies --show-copies-as-adds */ + if (use_git_diff_format) + show_copies_as_adds = TRUE; + + /* --show-copies-as-adds implies --notice-ancestry */ + if (show_copies_as_adds) + ignore_ancestry = FALSE; + + SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, + callbacks, callback_baton, TRUE, + result_pool, scratch_pool)); + + if (reverse_order) + diff_processor = svn_diff__tree_processor_reverse_create( + diff_processor, NULL, result_pool); + + if (! show_copies_as_adds) + diff_processor = svn_diff__tree_processor_copy_as_changed_create( + diff_processor, result_pool); + return svn_error_trace( svn_wc__get_diff_editor(editor, edit_baton, wc_ctx, anchor_abspath, target, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, use_text_base, + ignore_ancestry, use_text_base, reverse_order, server_performs_filtering, changelist_filter, - callbacks, callback_baton, + diff_processor, cancel_func, cancel_baton, result_pool, scratch_pool)); } @@ -2633,6 +2712,146 @@ svn_wc_props_modified_p(svn_boolean_t *modified_p, } +svn_error_t * +svn_wc__status2_from_3(svn_wc_status2_t **status, + const svn_wc_status3_t *old_status, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_wc_entry_t *entry = NULL; + + if (old_status == NULL) + { + *status = NULL; + return SVN_NO_ERROR; + } + + *status = apr_pcalloc(result_pool, sizeof(**status)); + + if (old_status->versioned) + { + svn_error_t *err; + err= svn_wc__get_entry(&entry, wc_ctx->db, local_abspath, FALSE, + svn_node_unknown, result_pool, scratch_pool); + + if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND) + svn_error_clear(err); + else + SVN_ERR(err); + } + + (*status)->entry = entry; + (*status)->copied = old_status->copied; + (*status)->repos_lock = svn_lock_dup(old_status->repos_lock, result_pool); + + if (old_status->repos_relpath) + (*status)->url = svn_path_url_add_component2(old_status->repos_root_url, + old_status->repos_relpath, + result_pool); + (*status)->ood_last_cmt_rev = old_status->ood_changed_rev; + (*status)->ood_last_cmt_date = old_status->ood_changed_date; + (*status)->ood_kind = old_status->ood_kind; + (*status)->ood_last_cmt_author = old_status->ood_changed_author; + + if (old_status->conflicted) + { + const svn_wc_conflict_description2_t *tree_conflict2; + SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict2, wc_ctx, local_abspath, + scratch_pool, scratch_pool)); + (*status)->tree_conflict = svn_wc__cd2_to_cd(tree_conflict2, result_pool); + } + + (*status)->switched = old_status->switched; + + (*status)->text_status = old_status->node_status; + (*status)->prop_status = old_status->prop_status; + + (*status)->repos_text_status = old_status->repos_node_status; + (*status)->repos_prop_status = old_status->repos_prop_status; + + /* Some values might be inherited from properties */ + if (old_status->node_status == svn_wc_status_modified + || old_status->node_status == svn_wc_status_conflicted) + (*status)->text_status = old_status->text_status; + + /* (Currently a no-op, but just make sure it is ok) */ + if (old_status->repos_node_status == svn_wc_status_modified + || old_status->repos_node_status == svn_wc_status_conflicted) + (*status)->repos_text_status = old_status->repos_text_status; + + if (old_status->node_status == svn_wc_status_added) + (*status)->prop_status = svn_wc_status_none; /* No separate info */ + + /* Find pristine_text_status value */ + switch (old_status->text_status) + { + case svn_wc_status_none: + case svn_wc_status_normal: + case svn_wc_status_modified: + (*status)->pristine_text_status = old_status->text_status; + break; + case svn_wc_status_conflicted: + default: + /* ### Fetch compare data, or fall back to the documented + not retrieved behavior? */ + (*status)->pristine_text_status = svn_wc_status_none; + break; + } + + /* Find pristine_prop_status value */ + switch (old_status->prop_status) + { + case svn_wc_status_none: + case svn_wc_status_normal: + case svn_wc_status_modified: + if (old_status->node_status != svn_wc_status_added + && old_status->node_status != svn_wc_status_deleted + && old_status->node_status != svn_wc_status_replaced) + { + (*status)->pristine_prop_status = old_status->prop_status; + } + else + (*status)->pristine_prop_status = svn_wc_status_none; + break; + case svn_wc_status_conflicted: + default: + /* ### Fetch compare data, or fall back to the documented + not retrieved behavior? */ + (*status)->pristine_prop_status = svn_wc_status_none; + break; + } + + if (old_status->versioned + && old_status->conflicted + && old_status->node_status != svn_wc_status_obstructed + && (old_status->kind == svn_node_file + || old_status->node_status != svn_wc_status_missing)) + { + svn_boolean_t text_conflict_p, prop_conflict_p; + + /* The entry says there was a conflict, but the user might have + marked it as resolved by deleting the artifact files, so check + for that. */ + SVN_ERR(svn_wc__internal_conflicted_p(&text_conflict_p, + &prop_conflict_p, + NULL, + wc_ctx->db, local_abspath, + scratch_pool)); + + if (text_conflict_p) + (*status)->text_status = svn_wc_status_conflicted; + + if (prop_conflict_p) + (*status)->prop_status = svn_wc_status_conflicted; + } + + return SVN_NO_ERROR; +} + + + /*** From status.c ***/ struct status4_wrapper_baton @@ -2698,9 +2917,9 @@ svn_wc_get_status_editor5(const svn_delta_editor_t **editor, wc_ctx, anchor_abspath, target_basename, - depth, - get_all, no_ignore, - depth_as_sticky, + depth, get_all, + TRUE, /* check_working_copy */ + no_ignore, depth_as_sticky, server_performs_filtering, ignore_patterns, status_func, status_baton, @@ -4044,7 +4263,26 @@ svn_wc_relocate(const char *path, } -/*** From log.c ***/ +/*** From log.c / cleanup.c ***/ + +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) +{ + return svn_error_trace( + svn_wc_cleanup4(wc_ctx, + local_abspath, + TRUE /* break_locks */, + TRUE /* fix_recorded_timestamps */, + TRUE /* clear_dav_cache */, + TRUE /* clean_pristines */, + cancel_func, cancel_baton, + NULL, NULL /* notify */, + scratch_pool)); +} svn_error_t * svn_wc_cleanup2(const char *path, @@ -4570,13 +4808,11 @@ svn_wc_read_kind(svn_node_kind_t *kind, TRUE /* show_deleted */, show_hidden, scratch_pool)); +} - /*if (db_kind == svn_node_dir) - *kind = svn_node_dir; - else if (db_kind == svn_node_file || db_kind == svn_node_symlink) - *kind = svn_node_file; - else - *kind = svn_node_none;*/ - - return SVN_NO_ERROR; +svn_wc_conflict_description2_t * +svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, + apr_pool_t *pool) +{ + return svn_wc_conflict_description2_dup(conflict, pool); } diff --git a/contrib/subversion/subversion/libsvn_wc/diff.h b/contrib/subversion/subversion/libsvn_wc/diff.h index 3ddada67d..2df88e16a 100644 --- a/contrib/subversion/subversion/libsvn_wc/diff.h +++ b/contrib/subversion/subversion/libsvn_wc/diff.h @@ -39,7 +39,7 @@ 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. @@ -60,7 +60,7 @@ svn_wc__diff_local_only_file(svn_wc__db_t *db, 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. diff --git a/contrib/subversion/subversion/libsvn_wc/diff_editor.c b/contrib/subversion/subversion/libsvn_wc/diff_editor.c index c9078ed7d..fc059a5d4 100644 --- a/contrib/subversion/subversion/libsvn_wc/diff_editor.c +++ b/contrib/subversion/subversion/libsvn_wc/diff_editor.c @@ -64,10 +64,11 @@ #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 "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_wc_private.h" #include "wc.h" #include "props.h" @@ -241,10 +242,9 @@ make_edit_baton(struct edit_baton_t **edit_baton, svn_wc__db_t *db, const char *anchor_abspath, const char *target, - const svn_diff_tree_processor_t *processor, + const svn_diff_tree_processor_t *diff_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, svn_cancel_func_t cancel_func, @@ -255,22 +255,11 @@ make_edit_baton(struct edit_baton_t **edit_baton, SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); - 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->processor = diff_processor; eb->depth = depth; eb->ignore_ancestry = ignore_ancestry; eb->local_before_remote = reverse_order; @@ -566,6 +555,7 @@ ensure_local_info(struct dir_baton_t *db, SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts, db->eb->db, db->local_abspath, + FALSE /* base_tree_only */, db->pool, scratch_pool)); return SVN_NO_ERROR; @@ -655,6 +645,7 @@ walk_local_nodes_diff(struct edit_baton_t *eb, SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, + FALSE /* base_tree_only */, scratch_pool, iterpool)); children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, @@ -703,6 +694,9 @@ walk_local_nodes_diff(struct edit_baton_t *eb, if (!info->have_base) { local_only = TRUE; /* Only report additions */ + + if (info->status == svn_wc__db_status_deleted) + continue; /* Nothing added (deleted copy) */ } else if (info->status == svn_wc__db_status_normal) { @@ -1042,9 +1036,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t *db, 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, @@ -1098,70 +1089,82 @@ svn_wc__diff_local_only_dir(svn_wc__db_t *db, 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++) + if ((depth > svn_depth_empty || depth == svn_depth_unknown) + && ! skip_children) { - 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_depth_t depth_below_here = depth; + apr_hash_t *nodes; + apr_hash_t *conflicts; - svn_pool_clear(iterpool); + if (depth_below_here == svn_depth_immediates) + depth_below_here = svn_depth_empty; + + SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, + db, local_abspath, + FALSE /* base_tree_only */, + scratch_pool, iterpool)); - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - child_abspath = svn_dirent_join(local_abspath, name, iterpool); + children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, + scratch_pool); - if (NOT_PRESENT(info->status)) + for (i = 0; i < children->nelts; i++) { - continue; - } + 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; - /* 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; + svn_pool_clear(iterpool); - child_relpath = svn_relpath_join(relpath, name, iterpool); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); - 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, - diff_pristine, - cancel_func, cancel_baton, - scratch_pool)); - break; + child_abspath = svn_dirent_join(local_abspath, name, iterpool); - case svn_node_dir: - if (depth > svn_depth_files || depth == svn_depth_unknown) + if (NOT_PRESENT(info->status)) { - SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, - child_relpath, depth_below_here, - processor, pdb, - diff_pristine, - cancel_func, cancel_baton, - iterpool)); + continue; } - break; - default: - break; + /* 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, + 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, + diff_pristine, + cancel_func, + cancel_baton, + iterpool)); + } + break; + + default: + break; + } } } @@ -2193,7 +2196,7 @@ change_file_prop(void *file_baton, propchange = apr_array_push(fb->propchanges); propchange->name = apr_pstrdup(fb->pool, name); - propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; + propchange->value = svn_string_dup(value, fb->pool); return SVN_NO_ERROR; } @@ -2218,7 +2221,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); return SVN_NO_ERROR; } @@ -2257,14 +2260,11 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, 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, + const svn_diff_tree_processor_t *diff_processor, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, @@ -2277,18 +2277,9 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **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) { @@ -2305,7 +2296,7 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, wc_ctx->db, anchor_abspath, target, diff_processor, - depth, ignore_ancestry, show_copies_as_adds, + depth, ignore_ancestry, use_text_base, reverse_order, cancel_func, cancel_baton, result_pool)); @@ -2458,8 +2449,8 @@ wrap_dir_opened(void **new_dir_baton, /* 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 svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, diff --git a/contrib/subversion/subversion/libsvn_wc/diff_local.c b/contrib/subversion/subversion/libsvn_wc/diff_local.c index 22b498ffd..e1cb3294b 100644 --- a/contrib/subversion/subversion/libsvn_wc/diff_local.c +++ b/contrib/subversion/subversion/libsvn_wc/diff_local.c @@ -1,6 +1,6 @@ /* - * diff_pristine.c -- A simple diff walker which compares local files against - * their pristine versions. + * diff_local.c -- A simple diff walker which compares local files against + * their pristine versions. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -41,8 +41,8 @@ #include "private/svn_diff_tree.h" #include "wc.h" +#include "wc_db.h" #include "props.h" -#include "translate.h" #include "diff.h" #include "svn_private_config.h" @@ -89,9 +89,6 @@ struct diff_baton /* 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; - /* Cancel function/baton */ svn_cancel_func_t cancel_func; void *cancel_baton; @@ -119,17 +116,17 @@ ensure_state(struct diff_baton *eb, if (! relpath) return SVN_NO_ERROR; - /* Don't recurse on the anchor, as that might loop infinately because + /* Don't recurse on the anchor, as that might loop infinitely because svn_dirent_dirname("/",...) -> "/" svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */ if (*relpath) SVN_ERR(ensure_state(eb, - svn_dirent_dirname(local_abspath,scratch_pool), + 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), + SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), FALSE, scratch_pool)); else @@ -421,23 +418,22 @@ diff_status_callback(void *baton, /* 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) +svn_wc__diff7(const char **root_relpath, + svn_boolean_t *root_is_dir, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + const apr_array_header_t *changelist_filter, + const svn_diff_tree_processor_t *diff_processor, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + 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, @@ -446,30 +442,27 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, 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; + eb.anchor_abspath = local_abspath; + if (root_relpath) + { + svn_boolean_t is_wcroot; + SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, + wc_ctx->db, local_abspath, scratch_pool)); - /* - if (reverse_order) - processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); - */ + if (!is_wcroot) + eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); + } + else if (kind != svn_node_dir) + eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); - if (! show_copies_as_adds && !use_git_diff_format) - processor = svn_diff__tree_processor_copy_as_changed_create(processor, - scratch_pool); + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, + svn_dirent_skip_ancestor(eb.anchor_abspath, + local_abspath)); + if (root_is_dir) + *root_is_dir = (kind == svn_node_dir); /* Apply changelist filtering to the output */ if (changelist_filter && changelist_filter->nelts) @@ -477,19 +470,18 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, 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); + result_pool)); + diff_processor = svn_wc__changelist_filter_tree_processor_create( + diff_processor, wc_ctx, local_abspath, + changelist_hash, result_pool); } eb.db = wc_ctx->db; - eb.processor = processor; + eb.processor = diff_processor; eb.ignore_ancestry = ignore_ancestry; - eb.show_copies_as_adds = show_copies_as_adds; eb.pool = scratch_pool; - if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry) + if (ignore_ancestry) get_all = TRUE; /* We need unmodified descendants of copies */ else get_all = FALSE; @@ -512,22 +504,22 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, 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)); + SVN_ERR(diff_processor->dir_changed(ns->relpath, + ns->left_src, + ns->right_src, + ns->left_props, + ns->right_props, + ns->propchanges, + ns->baton, + diff_processor, + ns->pool)); else - SVN_ERR(processor->dir_closed(ns->relpath, - ns->left_src, - ns->right_src, - ns->baton, - processor, - ns->pool)); + SVN_ERR(diff_processor->dir_closed(ns->relpath, + ns->left_src, + ns->right_src, + ns->baton, + diff_processor, + ns->pool)); } eb.cur = ns->parent; svn_pool_clear(ns->pool); @@ -535,3 +527,43 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, return SVN_NO_ERROR; } + +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) +{ + const svn_diff_tree_processor_t *processor; + + 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 (! show_copies_as_adds && !use_git_diff_format) + processor = svn_diff__tree_processor_copy_as_changed_create(processor, + scratch_pool); + + return svn_error_trace(svn_wc__diff7(NULL, NULL, + wc_ctx, local_abspath, + depth, + ignore_ancestry, + changelist_filter, + processor, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); +} + diff --git a/contrib/subversion/subversion/libsvn_wc/entries.c b/contrib/subversion/subversion/libsvn_wc/entries.c index 24dae50c0..1be6a8bec 100644 --- a/contrib/subversion/subversion/libsvn_wc/entries.c +++ b/contrib/subversion/subversion/libsvn_wc/entries.c @@ -45,9 +45,14 @@ #include "wc_db.h" #include "wc-queries.h" /* for STMT_* */ +#define SVN_WC__I_AM_WC_DB + #include "svn_private_config.h" #include "private/svn_wc_private.h" #include "private/svn_sqlite.h" +#include "token-map.h" + +#include "wc_db_private.h" #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x)))) @@ -213,6 +218,8 @@ get_info_for_deleted(svn_wc_entry_t *entry, svn_wc__db_lock_t **lock, svn_wc__db_t *db, const char *entry_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *entry_relpath, const svn_wc_entry_t *parent_entry, svn_boolean_t have_base, svn_boolean_t have_more_work, @@ -221,12 +228,13 @@ get_info_for_deleted(svn_wc_entry_t *entry, { if (have_base && !have_more_work) { + apr_int64_t repos_id; /* This is the delete of a BASE node */ - SVN_ERR(svn_wc__db_base_get_info(NULL, kind, + SVN_ERR(svn_wc__db_base_get_info_internal( + NULL, kind, &entry->revision, repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &entry->cmt_rev, &entry->cmt_date, &entry->cmt_author, @@ -236,16 +244,18 @@ get_info_for_deleted(svn_wc_entry_t *entry, lock, &entry->has_props, NULL, NULL, - db, - entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); } else { - const char *work_del_abspath; + const char *work_del_relpath; const char *parent_repos_relpath; - const char *parent_abspath; + const char *parent_relpath; + apr_int64_t repos_id; /* This is a deleted child of a copy/move-here, so we need to scan up the WORKING tree to find the root of @@ -265,30 +275,33 @@ get_info_for_deleted(svn_wc_entry_t *entry, scratch_pool)); /* working_size and text_time unavailable */ - SVN_ERR(svn_wc__db_scan_deletion(NULL, + SVN_ERR(svn_wc__db_scan_deletion_internal( NULL, - &work_del_abspath, NULL, - db, entry_abspath, + NULL, + &work_del_relpath, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); - SVN_ERR_ASSERT(work_del_abspath != NULL); - parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); + SVN_ERR_ASSERT(work_del_relpath != NULL); + parent_relpath = svn_relpath_dirname(work_del_relpath, 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, + SVN_ERR(svn_wc__db_scan_addition_internal( + NULL, NULL, &parent_repos_relpath, - &entry->repos, - &entry->uuid, - NULL, NULL, NULL, NULL, - db, parent_abspath, + &repos_id, + NULL, NULL, NULL, + wcroot, parent_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); /* Now glue it all together */ *repos_relpath = svn_relpath_join(parent_repos_relpath, - svn_dirent_is_child(parent_abspath, - entry_abspath, - NULL), + svn_relpath_skip_ancestor( + parent_relpath, + entry_relpath), result_pool); @@ -297,11 +310,12 @@ get_info_for_deleted(svn_wc_entry_t *entry, if (have_base) { svn_wc__db_status_t status; - SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision, + SVN_ERR(svn_wc__db_base_get_info_internal( + &status, NULL, &entry->revision, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, lock, NULL, NULL, + NULL, NULL, lock, NULL, NULL, NULL, - db, entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); if (status == svn_wc__db_status_not_present) @@ -346,7 +360,7 @@ write_tree_conflicts(const char **conflict_data, { svn_skel_t *c_skel; - SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi), + SVN_ERR(svn_wc__serialize_conflict(&c_skel, apr_hash_this_val(hi), pool, pool)); svn_skel__prepend(c_skel, skel); } @@ -369,12 +383,17 @@ write_tree_conflicts(const char **conflict_data, 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. + ### All database read operations should really use wcroot, dir_relpath, + as that restores obstruction compatibility with <= 1.6.0 + but that has been the case since the introduction of WC-NG in 1.7.0 + 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, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, const char *name, const svn_wc_entry_t *parent_entry, apr_pool_t *result_pool, @@ -387,24 +406,28 @@ read_one_entry(const svn_wc_entry_t **new_entry, const svn_checksum_t *checksum; svn_filesize_t translated_size; svn_wc_entry_t *entry = alloc_entry(result_pool); + const char *entry_relpath; const char *entry_abspath; + apr_int64_t repos_id; + apr_int64_t original_repos_id; 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; + svn_boolean_t op_root; - entry->name = name; + entry->name = apr_pstrdup(result_pool, name); + entry_relpath = svn_relpath_join(dir_relpath, entry->name, scratch_pool); entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool); - SVN_ERR(svn_wc__db_read_info( + SVN_ERR(svn_wc__db_read_info_internal( &status, &kind, &entry->revision, &repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &entry->cmt_rev, &entry->cmt_date, &entry->cmt_author, @@ -412,24 +435,27 @@ read_one_entry(const svn_wc_entry_t **new_entry, &checksum, NULL, &original_repos_relpath, - &original_root_url, - NULL, + &original_repos_id, &entry->copyfrom_rev, &lock, &translated_size, &entry->text_time, &entry->changelist, &conflicted, - NULL /* op_root */, + &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)); + wcroot, entry_relpath, + result_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&original_root_url, NULL, + wcroot, original_repos_id, + result_pool)); if (entry->has_prop_mods) entry->has_props = TRUE; @@ -457,9 +483,10 @@ read_one_entry(const svn_wc_entry_t **new_entry, child_abspath = svn_dirent_join(dir_abspath, child_name, scratch_pool); - SVN_ERR(svn_wc__read_conflicts(&child_conflicts, + SVN_ERR(svn_wc__read_conflicts(&child_conflicts, NULL, db, child_abspath, FALSE /* create tempfiles */, + TRUE /* tree_conflicts_only */, scratch_pool, scratch_pool)); for (j = 0; j < child_conflicts->nelts; j++) @@ -493,13 +520,15 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* 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)); + 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, entry_relpath, + result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); } entry->incomplete = (status == svn_wc__db_status_incomplete); @@ -519,13 +548,14 @@ read_one_entry(const svn_wc_entry_t **new_entry, 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, + const char *work_del_relpath; + SVN_ERR(svn_wc__db_scan_deletion_internal( + NULL, NULL, + &work_del_relpath, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); - if (work_del_abspath) + if (work_del_relpath) entry->copied = TRUE; } @@ -563,13 +593,14 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* 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, + SVN_ERR(svn_wc__db_base_get_info_internal( + &base_status, NULL, &entry->revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - db, entry_abspath, + NULL, NULL, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); @@ -603,18 +634,27 @@ read_one_entry(const svn_wc_entry_t **new_entry, 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, + const char *op_root_relpath; + SVN_ERR(svn_wc__db_scan_addition_internal( + &work_status, + &op_root_relpath, &repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &scanned_original_relpath, - NULL, NULL, /* original_root|uuid */ + NULL /* original_repos_id */, &original_revision, - db, - entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); + + if (!op_root_relpath) + op_root_abspath = NULL; + else + op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath, + 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. */ @@ -633,7 +673,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* ### scan_addition may need to be updated to avoid returning ### status_copied in this case. */ } - /* For backwards-compatiblity purposes we treat moves just like + /* For backwards-compatibility 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) @@ -675,10 +715,12 @@ read_one_entry(const svn_wc_entry_t **new_entry, mixed-revision situation. */ if (!is_copied_child) { - const char *parent_abspath; + const char *parent_relpath; svn_error_t *err; const char *parent_repos_relpath; const char *parent_root_url; + apr_int64_t parent_repos_id; + const char *op_root_relpath; /* When we insert entries into the database, we will construct additional copyfrom records for mixed-revision @@ -705,15 +747,16 @@ read_one_entry(const svn_wc_entry_t **new_entry, 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, + parent_relpath = svn_relpath_dirname(entry_relpath, + scratch_pool); + err = svn_wc__db_scan_addition_internal( + NULL, + &op_root_relpath, NULL, NULL, - db, parent_abspath, + &parent_repos_relpath, + &parent_repos_id, + NULL, + wcroot, parent_relpath, scratch_pool, scratch_pool); if (err) @@ -721,10 +764,24 @@ read_one_entry(const svn_wc_entry_t **new_entry, if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); + op_root_abspath = NULL; + parent_repos_relpath = NULL; + parent_root_url = NULL; + } + else + { + SVN_ERR(svn_wc__db_fetch_repos_info(&parent_root_url, NULL, + wcroot, parent_repos_id, + scratch_pool)); + op_root_abspath = svn_dirent_join(wcroot->abspath, + op_root_relpath, + scratch_pool); } - else if (parent_root_url != NULL + + 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( @@ -827,6 +884,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, &checksum, &lock, db, entry_abspath, + wcroot, entry_relpath, parent_entry, have_base, have_more_work, result_pool, scratch_pool)); @@ -869,7 +927,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* 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, + dir_abspath, checksum, scratch_pool, scratch_pool)); SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5); @@ -881,8 +939,9 @@ read_one_entry(const svn_wc_entry_t **new_entry, 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__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, entry_relpath, + scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted, &prop_conflicted, NULL, @@ -955,7 +1014,9 @@ read_one_entry(const svn_wc_entry_t **new_entry, static svn_error_t * read_entries_new(apr_hash_t **result_entries, svn_wc__db_t *db, - const char *local_abspath, + const char *dir_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -964,11 +1025,12 @@ read_entries_new(apr_hash_t **result_entries, 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, + SVN_ERR(read_one_entry(&parent_entry, + db, dir_abspath, + wcroot, dir_relpath, "" /* name */, NULL /* parent_entry */, result_pool, iterpool)); @@ -977,8 +1039,8 @@ read_entries_new(apr_hash_t **result_entries, /* 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)); + dir_abspath, + scratch_pool, iterpool)); for (i = children->nelts; i--; ) { const char *name = APR_ARRAY_IDX(children, i, const char *); @@ -987,7 +1049,9 @@ read_entries_new(apr_hash_t **result_entries, svn_pool_clear(iterpool); SVN_ERR(read_one_entry(&entry, - db, wc_id, local_abspath, name, parent_entry, + db, dir_abspath, + wcroot, dir_relpath, + name, parent_entry, result_pool, iterpool)); svn_hash_sets(entries, entry->name, entry); } @@ -1000,28 +1064,20 @@ read_entries_new(apr_hash_t **result_entries, } -/* 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) +read_entry_pair_txn(const svn_wc_entry_t **parent_entry, + const svn_wc_entry_t **entry, + svn_wc__db_t *db, + const char *dir_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, + 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, + SVN_ERR(read_one_entry(parent_entry, + db, dir_abspath, + wcroot, dir_relpath, "" /* name */, NULL /* parent_entry */, result_pool, scratch_pool)); @@ -1073,7 +1129,9 @@ read_entry_pair(const svn_wc_entry_t **parent_entry, svn_error_t *err; err = read_one_entry(entry, - db, wc_id, dir_abspath, name, *parent_entry, + db, dir_abspath, + wcroot, dir_relpath, + name, *parent_entry, result_pool, scratch_pool); if (err) { @@ -1096,28 +1154,76 @@ read_entry_pair(const svn_wc_entry_t **parent_entry, 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) +{ + svn_wc__db_wcroot_t *wcroot; + const char *dir_relpath; + + 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_entry_pair_txn(parent_entry, entry, + db, dir_abspath, + wcroot, dir_relpath, + name, + result_pool, scratch_pool), + wcroot); + + return SVN_NO_ERROR; +} /* */ static svn_error_t * read_entries(apr_hash_t **entries, svn_wc__db_t *db, - const char *wcroot_abspath, + const char *dir_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *dir_relpath; int wc_format; - SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath, + SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, dir_abspath, scratch_pool)); if (wc_format < SVN_WC__WC_NG_VERSION) return svn_error_trace(svn_wc__read_entries_old(entries, - wcroot_abspath, + dir_abspath, result_pool, scratch_pool)); - return svn_error_trace(read_entries_new(entries, db, wcroot_abspath, - result_pool, scratch_pool)); + 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_entries_new(entries, + db, dir_abspath, + wcroot, dir_relpath, + result_pool, scratch_pool), + wcroot); + + return SVN_NO_ERROR; } @@ -1342,7 +1448,7 @@ prune_deleted(apr_hash_t **entries_pruned, svn_boolean_t hidden; SVN_ERR(svn_wc__entry_is_hidden(&hidden, - svn__apr_hash_index_val(hi))); + apr_hash_this_val(hi))); if (hidden) break; } @@ -1360,8 +1466,8 @@ prune_deleted(apr_hash_t **entries_pruned, 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); + const void *key = apr_hash_this_key(hi); + const svn_wc_entry_t *entry = apr_hash_this_val(hi); svn_boolean_t hidden; SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); @@ -1372,25 +1478,6 @@ prune_deleted(apr_hash_t **entries_pruned, 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, @@ -1405,21 +1492,9 @@ svn_wc__entries_read_internal(apr_hash_t **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_ERR(read_entries(&new_entries, db, local_abspath, + result_pool, pool)); svn_wc__adm_access_set_entries(adm_access, new_entries); } @@ -1452,23 +1527,31 @@ insert_node(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + svn_boolean_t present = (node->presence == svn_wc__db_status_normal + || node->presence == svn_wc__db_status_incomplete); 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", + SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn", 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)); + (node->kind == svn_node_dir && present) + ? svn_depth_to_word(node->depth) + : NULL)); - if (node->repos_relpath) + if (present && node->repos_relpath) + { + SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, node->changed_rev)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 12, node->changed_date)); + SVN_ERR(svn_sqlite__bind_text(stmt, 13, node->changed_author)); + } + + if (node->repos_relpath + && node->presence != svn_wc__db_status_base_deleted) { SVN_ERR(svn_sqlite__bind_int64(stmt, 5, node->repos_id)); @@ -1477,26 +1560,14 @@ insert_node(svn_sqlite__db_t *sdb, 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")); + SVN_ERR(svn_sqlite__bind_token(stmt, 8, presence_map, node->presence)); 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))); + SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, node->kind)); - if (node->kind == svn_node_file) + if (node->kind == svn_node_file && present) { if (!node->checksum && node->op_depth == 0 @@ -1510,19 +1581,25 @@ insert_node(svn_sqlite__db_t *sdb, SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum, scratch_pool)); + + if (node->repos_relpath) + { + if (node->recorded_size != SVN_INVALID_FILESIZE) + SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); + + SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time)); + } } - if (node->properties) /* ### Never set, props done later */ + /* ### Never set, props done later */ + if (node->properties && present && node->repos_relpath) 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) + if (node->inherited_props && present) SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props, scratch_pool)); @@ -1801,6 +1878,10 @@ write_entry(struct write_baton **entry_node, if (entry->copied) { + db_node_t *work = parent_node->work + ? parent_node->work + : parent_node->below_work; + if (entry->copyfrom_url) { working_node->repos_id = repos_id; @@ -1810,20 +1891,37 @@ write_entry(struct write_baton **entry_node, working_node->revision = entry->copyfrom_rev; working_node->op_depth = svn_wc__db_op_depth_for_upgrade(local_relpath); + + if (work && work->repos_relpath + && work->repos_id == repos_id + && work->revision == entry->copyfrom_rev) + { + const char *name; + + name = svn_relpath_skip_ancestor(work->repos_relpath, + working_node->repos_relpath); + + if (name + && !strcmp(name, svn_relpath_basename(local_relpath, NULL))) + { + working_node->op_depth = work->op_depth; + } + } } - else if (parent_node->work && parent_node->work->repos_relpath) + else if (work && work->repos_relpath) { working_node->repos_id = repos_id; working_node->repos_relpath - = svn_relpath_join(parent_node->work->repos_relpath, + = svn_relpath_join(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; + working_node->revision = work->revision; + working_node->op_depth = work->op_depth; } else if (parent_node->below_work && parent_node->below_work->repos_relpath) { + /* Parent deleted, this not-present or similar */ working_node->repos_id = repos_id; working_node->repos_relpath = svn_relpath_join(parent_node->below_work->repos_relpath, @@ -1837,6 +1935,29 @@ write_entry(struct write_baton **entry_node, _("No copyfrom URL for '%s'"), svn_dirent_local_style(local_relpath, scratch_pool)); + + if (work && work->op_depth != working_node->op_depth + && work->repos_relpath + && work->repos_id == working_node->repos_id + && work->presence == svn_wc__db_status_normal + && !below_working_node) + { + /* Introduce a not-present node! */ + below_working_node = MAYBE_ALLOC(below_working_node, scratch_pool); + + below_working_node->wc_id = wc_id; + below_working_node->op_depth = work->op_depth; + below_working_node->local_relpath = local_relpath; + below_working_node->parent_relpath = parent_relpath; + + below_working_node->presence = svn_wc__db_status_not_present; + below_working_node->repos_id = repos_id; + below_working_node->repos_relpath = working_node->local_relpath; + + SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); + + below_working_node = NULL; /* Don't write a present intermediate! */ + } } if (entry->conflict_old) @@ -1892,7 +2013,7 @@ write_entry(struct write_baton **entry_node, scratch_pool); tree_conflicts = apr_hash_make(result_pool); skel = skel->children; - while(skel) + while (skel) { svn_wc_conflict_description2_t *conflict; svn_skel_t *new_skel; @@ -1909,11 +2030,6 @@ write_entry(struct write_baton **entry_node, 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)); @@ -2295,6 +2411,11 @@ write_entry(struct write_baton **entry_node, { working_node->op_depth = parent_node->work->op_depth; } + else if (working_node->presence == svn_wc__db_status_excluded + && parent_node->work) + { + working_node->op_depth = parent_node->work->op_depth; + } else if (!entry->copied) { working_node->op_depth @@ -2358,9 +2479,9 @@ write_actual_only_entries(apr_hash_t *tree_conflicts, 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->local_relpath = apr_hash_this_key(hi); actual_node->parent_relpath = parent_relpath; - actual_node->tree_conflict_data = svn__apr_hash_index_val(hi); + actual_node->tree_conflict_data = apr_hash_this_val(hi); SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node, scratch_pool)); @@ -2418,8 +2539,8 @@ svn_wc__write_upgraded_entries(void **dir_baton, 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 *name = apr_hash_this_key(hi); + const svn_wc_entry_t *this_entry = apr_hash_this_val(hi); const char *child_abspath, *child_relpath; svn_wc__text_base_info_t *text_base_info = svn_hash_gets(text_bases_info, name); @@ -2573,8 +2694,8 @@ walker_helper(const char *dirpath, /* 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 *name = apr_hash_this_key(hi); + const svn_wc_entry_t *current_entry = apr_hash_this_val(hi); const char *entrypath; const char *entry_abspath; svn_boolean_t hidden; @@ -2661,11 +2782,11 @@ svn_wc_walk_entries3(const char *path, 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_wc__db_status_t status; 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, + 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, @@ -2684,7 +2805,9 @@ svn_wc_walk_entries3(const char *path, walk_baton, pool); } - if (kind == svn_node_file || depth == svn_depth_exclude) + if (kind == svn_node_file + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_server_excluded) { const svn_wc_entry_t *entry; @@ -2693,27 +2816,24 @@ svn_wc_walk_entries3(const char *path, ### we should not call handle_error for an error the *callback* ### gave us. let it deal with the problem before returning. */ - if (!show_hidden) + if (!show_hidden + && (status == svn_wc__db_status_not_present + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_server_excluded)) { - 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); - } + /* 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, diff --git a/contrib/subversion/subversion/libsvn_wc/externals.c b/contrib/subversion/subversion/libsvn_wc/externals.c index 98395b87c..4cbd4f0e4 100644 --- a/contrib/subversion/subversion/libsvn_wc/externals.c +++ b/contrib/subversion/subversion/libsvn_wc/externals.c @@ -68,6 +68,7 @@ * 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. + * Set REV_STR to the element that specifies the revision. * PARENT_DIRECTORY_DISPLAY and LINE are given to return a nice error * string. * @@ -76,6 +77,7 @@ */ static svn_error_t * find_and_remove_externals_revision(int *rev_idx, + const char **rev_str, const char **line_parts, int num_line_parts, svn_wc_external_item2_t *item, @@ -137,6 +139,8 @@ find_and_remove_externals_revision(int *rev_idx, line_parts[j] = line_parts[j+shift_count]; line_parts[num_line_parts-shift_count] = NULL; + *rev_str = apr_psprintf(pool, "-r%s", digits_ptr); + /* Found the revision, so leave the function immediately, do * not continue looking for additional revisions. */ return SVN_NO_ERROR; @@ -158,23 +162,29 @@ find_and_remove_externals_revision(int *rev_idx, } svn_error_t * -svn_wc_parse_externals_description3(apr_array_header_t **externals_p, - const char *parent_directory, +svn_wc__parse_externals_description(apr_array_header_t **externals_p, + apr_array_header_t **parser_infos_p, + const char *defining_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 *parser_infos = 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); + const char *defining_directory_display = svn_path_is_url(defining_directory) ? + defining_directory : svn_dirent_local_style(defining_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 *)); + if (parser_infos_p) + parser_infos = + apr_array_make(pool, 1, sizeof(svn_wc__externals_parser_info_t *)); + for (i = 0; i < lines->nelts; i++) { const char *line = APR_ARRAY_IDX(lines, i, const char *); @@ -186,10 +196,12 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, const char *token1; svn_boolean_t token0_is_url; svn_boolean_t token1_is_url; + svn_wc__externals_parser_info_t *info = NULL; /* Index into line_parts where the revision specification started. */ int rev_idx = -1; + const char *rev_str = NULL; if ((! line) || (line[0] == '#')) continue; @@ -209,6 +221,9 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, item->revision.kind = svn_opt_revision_unspecified; item->peg_revision.kind = svn_opt_revision_unspecified; + if (parser_infos) + info = apr_pcalloc(pool, sizeof(*info)); + /* * There are six different formats of externals: * @@ -231,7 +246,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, _("Error parsing %s property on '%s': '%s'"), SVN_PROP_EXTERNALS, - parent_directory_display, + defining_directory_display, line); /* To make it easy to check for the forms, find and remove -r N @@ -240,9 +255,10 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, set item->revision to the parsed revision. */ /* ### ugh. stupid cast. */ SVN_ERR(find_and_remove_externals_revision(&rev_idx, + &rev_str, (const char **)line_parts, num_line_parts, item, - parent_directory_display, + defining_directory_display, line, pool)); token0 = line_parts[0]; @@ -258,7 +274,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, "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); + SVN_PROP_EXTERNALS, defining_directory_display, token0, token1); if (0 == rev_idx && token1_is_url) return svn_error_createf @@ -266,7 +282,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("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); + SVN_PROP_EXTERNALS, defining_directory_display, token1); if (1 == rev_idx && token0_is_url) return svn_error_createf @@ -274,9 +290,9 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("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); + SVN_PROP_EXTERNALS, defining_directory_display, token0); - /* The appearence of -r N or -rN forces the type of external. + /* The appearance 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. */ @@ -290,12 +306,34 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, SVN_ERR(svn_opt_parse_path(&item->peg_revision, &item->url, token0, pool)); item->target_dir = token1; + + if (info) + { + info->format = svn_wc__external_description_format_2; + + if (rev_str) + info->rev_str = apr_pstrdup(pool, rev_str); + + if (item->peg_revision.kind != svn_opt_revision_unspecified) + info->peg_rev_str = strrchr(token0, '@'); + } } else { item->target_dir = token0; item->url = token1; item->peg_revision = item->revision; + + if (info) + { + info->format = svn_wc__external_description_format_1; + + if (rev_str) + { + info->rev_str = apr_pstrdup(pool, rev_str); + info->peg_rev_str = info->rev_str; + } + } } SVN_ERR(svn_opt_resolve_revisions(&item->peg_revision, @@ -316,7 +354,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("Invalid %s property on '%s': " "target '%s' is an absolute path or involves '..'"), SVN_PROP_EXTERNALS, - parent_directory_display, + defining_directory_display, item->target_dir); if (canonicalize_url) @@ -333,14 +371,33 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, if (externals) APR_ARRAY_PUSH(externals, svn_wc_external_item2_t *) = item; + if (parser_infos) + APR_ARRAY_PUSH(parser_infos, svn_wc__externals_parser_info_t *) = info; } if (externals_p) *externals_p = externals; + if (parser_infos_p) + *parser_infos_p = parser_infos; return SVN_NO_ERROR; } +svn_error_t * +svn_wc_parse_externals_description3(apr_array_header_t **externals_p, + const char *defining_directory, + const char *desc, + svn_boolean_t canonicalize_url, + apr_pool_t *pool) +{ + return svn_error_trace(svn_wc__parse_externals_description(externals_p, + NULL, + defining_directory, + desc, + canonicalize_url, + pool)); +} + svn_error_t * svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets, apr_array_header_t *externals, @@ -418,8 +475,6 @@ struct edit_baton /* 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; @@ -432,7 +487,7 @@ struct edit_baton const svn_checksum_t *original_checksum; /* What we are installing now */ - const char *new_pristine_abspath; + svn_wc__db_install_data_t *install_data; svn_checksum_t *new_sha1_checksum; svn_checksum_t *new_md5_checksum; @@ -581,11 +636,12 @@ apply_textdelta(void *file_baton, 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_ERR(svn_wc__db_pristine_prepare_install(&dest_stream, + &eb->install_data, + &eb->new_sha1_checksum, + &eb->new_md5_checksum, + eb->db, eb->wri_abspath, + eb->pool, pool)); svn_txdelta_apply(src_stream, dest_stream, NULL, eb->local_abspath, pool, handler, handler_baton); @@ -605,7 +661,7 @@ change_file_prop(void *file_baton, propchange = apr_array_push(eb->propchanges); propchange->name = apr_pstrdup(eb->pool, name); - propchange->value = value ? svn_string_dup(value, eb->pool) : NULL; + propchange->value = svn_string_dup(value, eb->pool); return SVN_NO_ERROR; } @@ -658,11 +714,11 @@ close_file(void *file_baton, behavior to the pristine store. */ if (eb->new_sha1_checksum) { - SVN_ERR(svn_wc__db_pristine_install(eb->db, eb->new_pristine_abspath, + SVN_ERR(svn_wc__db_pristine_install(eb->install_data, eb->new_sha1_checksum, eb->new_md5_checksum, pool)); - eb->new_pristine_abspath = NULL; + eb->install_data = NULL; } /* Merge the changes */ @@ -767,7 +823,6 @@ close_file(void *file_baton, { 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)); @@ -832,7 +887,7 @@ close_file(void *file_baton, { SVN_ERR(svn_wc__wq_build_file_install(&work_item, eb->db, eb->local_abspath, - install_from, + NULL, eb->use_commit_times, TRUE, pool, pool)); @@ -971,6 +1026,7 @@ close_edit(void *edit_baton, apr_hash_make(pool) /* exclude_relpaths */, wcroot_iprops, + TRUE /* empty update */, eb->notify_func, eb->notify_baton, pool)); @@ -997,8 +1053,6 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, 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, @@ -1050,8 +1104,6 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, 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; @@ -1421,10 +1473,8 @@ svn_wc__external_remove(svn_wc_context_t *wc_ctx, 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, + FALSE, TRUE, FALSE, + 0, NULL, NULL, scratch_pool)); SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, @@ -1567,7 +1617,7 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, apr_pstrndup(scratch_pool, url, num_leading_slashes), svn_relpath_canonicalize(url + num_leading_slashes, scratch_pool), - (char*)NULL); + SVN_VA_NULL); } else { @@ -1674,7 +1724,7 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, SVN_ERR(uri_scheme(&scheme, repos_root_url, scratch_pool)); *resolved_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool, scheme, - ":", url, (char *)NULL), + ":", url, SVN_VA_NULL), result_pool); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_wc/info.c b/contrib/subversion/subversion/libsvn_wc/info.c index dc80ee72d..78e5d7f7a 100644 --- a/contrib/subversion/subversion/libsvn_wc/info.c +++ b/contrib/subversion/subversion/libsvn_wc/info.c @@ -52,7 +52,7 @@ svn_wc_info_dup(const svn_wc_info_t *info, for (i = 0; i < info->conflicts->nelts; i++) { APR_ARRAY_PUSH(new_conflicts, svn_wc_conflict_description2_t *) - = svn_wc__conflict_description2_dup( + = svn_wc_conflict_description2_dup( APR_ARRAY_IDX(info->conflicts, i, const svn_wc_conflict_description2_t *), pool); @@ -141,7 +141,6 @@ build_info_for_node(svn_wc__info2_t **info, { /* Root or child of copy */ tmpinfo->rev = original_revision; - repos_relpath = original_repos_relpath; if (op_root) { @@ -167,34 +166,6 @@ build_info_for_node(svn_wc__info2_t **info, } } } - else if (op_root) - { - /* Local addition */ - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, - NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - - if (have_base) - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &tmpinfo->rev, NULL, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - } - else - { - /* Child of copy. ### Not WC-NG like */ - SVN_ERR(svn_wc__internal_get_origin(NULL, &tmpinfo->rev, - &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, NULL, - db, local_abspath, TRUE, - result_pool, scratch_pool)); - } /* ### We should be able to avoid both these calls with the information from read_info() in most cases */ @@ -222,14 +193,20 @@ build_info_for_node(svn_wc__info2_t **info, else wc_info->schedule = svn_wc_schedule_add; } - SVN_ERR(svn_wc__db_read_url(&tmpinfo->URL, db, local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); + + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); } else if (status == svn_wc__db_status_deleted) { - const char *work_del_abspath; + svn_wc__db_status_t w_status; - SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, + SVN_ERR(svn_wc__db_read_pristine_info(&w_status, &tmpinfo->kind, &tmpinfo->last_changed_rev, &tmpinfo->last_changed_date, &tmpinfo->last_changed_author, @@ -239,51 +216,32 @@ build_info_for_node(svn_wc__info2_t **info, db, local_abspath, result_pool, scratch_pool)); + if (w_status == svn_wc__db_status_deleted) + { + /* We have a working not-present status. We don't know anything + about this node, but it *is visible* in STATUS. + + Let's tell that it is excluded */ + + wc_info->depth = svn_depth_exclude; + tmpinfo->kind = svn_node_unknown; + } + /* And now fetch the url and revision of what will be deleted */ SVN_ERR(svn_wc__db_scan_deletion(NULL, &wc_info->moved_to_abspath, - &work_del_abspath, NULL, + NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); - if (work_del_abspath != NULL) - { - /* This is a deletion within a copied subtree. Get the copied-from - * revision. */ - const char *added_abspath = svn_dirent_dirname(work_del_abspath, - scratch_pool); - - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, - NULL, NULL, NULL, - &tmpinfo->rev, - db, added_abspath, - result_pool, scratch_pool)); - - tmpinfo->URL = svn_path_url_add_component2( - tmpinfo->repos_root_URL, - svn_relpath_join(repos_relpath, - svn_dirent_skip_ancestor(added_abspath, - local_abspath), - scratch_pool), - result_pool); - } - else - { - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &tmpinfo->rev, - &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - - tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, - repos_relpath, - result_pool); - } + + SVN_ERR(svn_wc__db_read_repos_info(&tmpinfo->rev, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); wc_info->schedule = svn_wc_schedule_delete; + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); } else if (status == svn_wc__db_status_not_present || status == svn_wc__db_status_server_excluded) @@ -291,6 +249,21 @@ build_info_for_node(svn_wc__info2_t **info, *info = NULL; return SVN_NO_ERROR; } + else if (status == svn_wc__db_status_excluded && !repos_relpath) + { + /* We have a WORKING exclude. Avoid segfault on no repos info */ + + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); + + wc_info->schedule = svn_wc_schedule_normal; + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); + tmpinfo->wc_info->depth = svn_depth_exclude; + } else { /* Just a BASE node. We have all the info we need */ @@ -298,10 +271,10 @@ build_info_for_node(svn_wc__info2_t **info, repos_relpath, result_pool); wc_info->schedule = svn_wc_schedule_normal; - } - if (status == svn_wc__db_status_excluded) - tmpinfo->wc_info->depth = svn_depth_exclude; + if (status == svn_wc__db_status_excluded) + wc_info->depth = svn_depth_exclude; + } /* A default */ tmpinfo->size = SVN_INVALID_FILESIZE; @@ -310,9 +283,10 @@ build_info_for_node(svn_wc__info2_t **info, local_abspath, result_pool, scratch_pool)); if (conflicted) - SVN_ERR(svn_wc__read_conflicts(&wc_info->conflicts, db, - local_abspath, - TRUE /* ### create tempfiles */, + SVN_ERR(svn_wc__read_conflicts(&wc_info->conflicts, NULL, + db, local_abspath, + FALSE /* create tempfiles */, + FALSE /* only tree conflicts */, result_pool, scratch_pool)); else wc_info->conflicts = NULL; @@ -373,7 +347,7 @@ struct found_entry_baton svn_boolean_t actual_only; svn_boolean_t first; /* The set of tree conflicts that have been found but not (yet) visited by - * the tree walker. Map of abspath -> svn_wc_conflict_description2_t. */ + * the tree walker. Map of abspath -> empty string. */ apr_hash_t *tree_conflicts; apr_pool_t *pool; }; @@ -533,9 +507,10 @@ svn_wc__get_info(svn_wc_context_t *wc_ctx, for (hi = apr_hash_first(scratch_pool, fe_baton.tree_conflicts); hi; hi = apr_hash_next(hi)) { - const char *this_abspath = svn__apr_hash_index_key(hi); + const char *this_abspath = apr_hash_this_key(hi); const svn_wc_conflict_description2_t *tree_conflict; svn_wc__info2_t *info; + const apr_array_header_t *conflicts; svn_pool_clear(iterpool); @@ -543,35 +518,35 @@ svn_wc__get_info(svn_wc_context_t *wc_ctx, if (!repos_root_url) { - SVN_ERR(svn_wc__internal_get_repos_info(NULL, NULL, - &repos_root_url, - &repos_uuid, - wc_ctx->db, - svn_dirent_dirname( + SVN_ERR(svn_wc__db_read_repos_info(NULL, NULL, + &repos_root_url, + &repos_uuid, + wc_ctx->db, + svn_dirent_dirname( this_abspath, iterpool), - scratch_pool, - iterpool)); + scratch_pool, iterpool)); } info->repos_root_URL = repos_root_url; info->repos_UUID = repos_uuid; - SVN_ERR(svn_wc__read_conflicts(&info->wc_info->conflicts, + SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, wc_ctx->db, this_abspath, - TRUE /* ### create tempfiles */, + FALSE /* create tempfiles */, + FALSE /* only tree conflicts */, iterpool, iterpool)); - - if (! info->wc_info->conflicts || ! info->wc_info->conflicts->nelts) + if (! conflicts || ! conflicts->nelts) continue; - tree_conflict = APR_ARRAY_IDX(info->wc_info->conflicts, 0, - svn_wc_conflict_description2_t *); + tree_conflict = APR_ARRAY_IDX(conflicts, 0, + const svn_wc_conflict_description2_t *); if (!depth_includes(local_abspath, depth, tree_conflict->local_abspath, tree_conflict->node_kind, iterpool)) continue; + info->wc_info->conflicts = conflicts; SVN_ERR(receiver(receiver_baton, this_abspath, info, iterpool)); } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/libsvn_wc/libsvn_wc.pc.in b/contrib/subversion/subversion/libsvn_wc/libsvn_wc.pc.in new file mode 100644 index 000000000..f44387e74 --- /dev/null +++ b/contrib/subversion/subversion/libsvn_wc/libsvn_wc.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_wc +Description: Subversion Working Copy Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_diff libsvn_subr +Libs: -L${libdir} -lsvn_wc +Cflags: -I${includedir} diff --git a/contrib/subversion/subversion/libsvn_wc/lock.c b/contrib/subversion/subversion/libsvn_wc/lock.c index 36fbb0eeb..2392c3fe4 100644 --- a/contrib/subversion/subversion/libsvn_wc/lock.c +++ b/contrib/subversion/subversion/libsvn_wc/lock.c @@ -1211,7 +1211,7 @@ open_anchor(svn_wc_adm_access_t **anchor_access, if (disjoint) { /* Switched or disjoint, so drop P_ACCESS. Don't close any - descendents, or we might blast the child. */ + descendants, or we might blast the child. */ err = close_single(p_access, FALSE /* preserve_lock */, pool); if (err) { @@ -1308,13 +1308,13 @@ do_close(svn_wc_adm_access_t *adm_access, /* Gather all the opened access batons from the DB. */ opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool); - /* Close any that are descendents of this baton. */ + /* Close any that are descendants of this baton. */ for (hi = apr_hash_first(scratch_pool, opened); hi; hi = apr_hash_next(hi)) { - const char *abspath = svn__apr_hash_index_key(hi); - svn_wc_adm_access_t *child = svn__apr_hash_index_val(hi); + const char *abspath = apr_hash_this_key(hi); + svn_wc_adm_access_t *child = apr_hash_this_val(hi); const char *path = child->path; if (IS_MISSING(child)) @@ -1630,7 +1630,7 @@ svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, scratch_pool, scratch_pool)); /* It's possible for the required lock path to be an ancestor - of, a descendent of, or equal to, the obtained lock path. If + of, a descendant of, or equal to, the obtained lock path. If it's an ancestor we have to try again, otherwise the obtained lock will do. */ child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath); @@ -1643,7 +1643,7 @@ svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, } else { - /* required should be a descendent of, or equal to, obtained */ + /* required should be a descendant of, or equal to, obtained */ SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath) || svn_dirent_skip_ancestor(obtained_abspath, required_abspath)); diff --git a/contrib/subversion/subversion/libsvn_wc/merge.c b/contrib/subversion/subversion/libsvn_wc/merge.c index e01c471ca..85ec63221 100644 --- a/contrib/subversion/subversion/libsvn_wc/merge.c +++ b/contrib/subversion/subversion/libsvn_wc/merge.c @@ -26,10 +26,11 @@ #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_pools.h" +#include "svn_props.h" #include "wc.h" -#include "adm_files.h" #include "conflicts.h" +#include "props.h" #include "translate.h" #include "workqueue.h" @@ -179,7 +180,7 @@ detranslate_wc_file(const char **detranslated_abspath, = prop ? (prop->value ? prop->value->data : NULL) : old_mime_value; old_is_binary = old_mime_value && svn_mime_type_is_binary(old_mime_value); - new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value);; + new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value); } /* See what translations we want to do */ @@ -391,6 +392,8 @@ do_text_merge(svn_boolean_t *contains_conflicts, const char *target_label, const char *left_label, const char *right_label, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { svn_diff_t *diff; @@ -415,13 +418,14 @@ do_text_merge(svn_boolean_t *contains_conflicts, ostream = svn_stream_from_aprfile2(result_f, TRUE, pool); - SVN_ERR(svn_diff_file_output_merge2(ostream, diff, + SVN_ERR(svn_diff_file_output_merge3(ostream, diff, left, detranslated_target, right, left_marker, target_marker, right_marker, "=======", /* separator */ svn_diff_conflict_display_modified_original_latest, + cancel_func, cancel_baton, pool)); SVN_ERR(svn_stream_close(ostream)); @@ -725,16 +729,20 @@ merge_file_trivial(svn_skel_t **work_items, { svn_stream_t *tmp_src; svn_stream_t *tmp_dst; + const char *tmp_dir; SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath, - NULL, NULL, - db, target_abspath, - scratch_pool, - scratch_pool)); + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir, db, + target_abspath, + scratch_pool, + scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_dst, &right_abspath, + tmp_dir, svn_io_file_del_none, + scratch_pool, scratch_pool)); SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst, cancel_func, cancel_baton, @@ -866,15 +874,17 @@ merge_text_file(svn_skel_t **work_items, target_label, left_label, right_label, + cancel_func, cancel_baton, pool)); SVN_ERR(svn_io_file_close(result_f, pool)); /* Determine the MERGE_OUTCOME, and record any conflict. */ - if (contains_conflicts && ! dry_run) + if (contains_conflicts) { *merge_outcome = svn_wc_merge_conflict; - if (*merge_outcome == svn_wc_merge_conflict) + + if (! dry_run) { const char *left_copy, *right_copy, *target_copy; @@ -902,12 +912,7 @@ merge_text_file(svn_skel_t **work_items, result_pool, scratch_pool)); } - - if (*merge_outcome == svn_wc_merge_merged) - goto done; } - else if (contains_conflicts && dry_run) - *merge_outcome = svn_wc_merge_conflict; else { svn_boolean_t same, special; @@ -941,7 +946,6 @@ merge_text_file(svn_skel_t **work_items, *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); } -done: /* Remove the tempfile after use */ SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, mt->local_abspath, result_target, @@ -1226,6 +1230,7 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, apr_hash_t *pristine_props = NULL; apr_hash_t *old_actual_props; apr_hash_t *new_actual_props = NULL; + svn_node_kind_t kind; SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath)); @@ -1238,7 +1243,6 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, /* Sanity check: the merge target must be a file under revision control */ { svn_wc__db_status_t status; - svn_node_kind_t kind; svn_boolean_t had_props; svn_boolean_t props_mod; svn_boolean_t conflicted; @@ -1401,7 +1405,7 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, svn_boolean_t text_conflicted, prop_conflicted; SVN_ERR(svn_wc__conflict_invoke_resolver( - wc_ctx->db, target_abspath, + wc_ctx->db, target_abspath, kind, conflict_skel, merge_options, conflict_func, conflict_baton, cancel_func, cancel_baton, diff --git a/contrib/subversion/subversion/libsvn_wc/node.c b/contrib/subversion/subversion/libsvn_wc/node.c index a1d6b02f5..560a899ea 100644 --- a/contrib/subversion/subversion/libsvn_wc/node.c +++ b/contrib/subversion/subversion/libsvn_wc/node.c @@ -54,18 +54,14 @@ /* Set *CHILDREN_ABSPATHS to a new array of the full paths formed by joining - * each name in REL_CHILDREN onto DIR_ABSPATH. If SHOW_HIDDEN is false then - * omit any paths that are reported as 'hidden' by svn_wc__db_node_hidden(). + * each name in REL_CHILDREN onto DIR_ABSPATH. * * Allocate the output array and its elements in RESULT_POOL. */ -static svn_error_t * -filter_and_make_absolute(const apr_array_header_t **children_abspaths, - svn_wc_context_t *wc_ctx, - const char *dir_abspath, - const apr_array_header_t *rel_children, - svn_boolean_t show_hidden, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +static void +make_absolute(const apr_array_header_t **children_abspaths, + const char *dir_abspath, + const apr_array_header_t *rel_children, + apr_pool_t *result_pool) { apr_array_header_t *children; int i; @@ -74,29 +70,13 @@ filter_and_make_absolute(const apr_array_header_t **children_abspaths, sizeof(const char *)); for (i = 0; i < rel_children->nelts; i++) { - const char *child_abspath = svn_dirent_join(dir_abspath, - APR_ARRAY_IDX(rel_children, - i, - const char *), - result_pool); - - /* Don't add hidden nodes to *CHILDREN if we don't want them. */ - if (!show_hidden) - { - svn_boolean_t child_is_hidden; - - SVN_ERR(svn_wc__db_node_hidden(&child_is_hidden, wc_ctx->db, - child_abspath, scratch_pool)); - if (child_is_hidden) - continue; - } - - APR_ARRAY_PUSH(children, const char *) = child_abspath; + const char *name = APR_ARRAY_IDX(rel_children, i, const char *); + APR_ARRAY_PUSH(children, const char *) = + svn_dirent_join(dir_abspath, name, + result_pool); } *children_abspaths = children; - - return SVN_NO_ERROR; } @@ -104,139 +84,36 @@ svn_error_t * svn_wc__node_get_children_of_working_node(const apr_array_header_t **children, svn_wc_context_t *wc_ctx, const char *dir_abspath, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const apr_array_header_t *rel_children; + const apr_array_header_t *child_names; - SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children, + SVN_ERR(svn_wc__db_read_children_of_working_node(&child_names, wc_ctx->db, dir_abspath, scratch_pool, scratch_pool)); - SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath, - rel_children, show_hidden, - result_pool, scratch_pool)); + make_absolute(children, dir_abspath, child_names, result_pool); return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__node_get_children(const apr_array_header_t **children, - svn_wc_context_t *wc_ctx, - const char *dir_abspath, - svn_boolean_t show_hidden, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const apr_array_header_t *rel_children; - - SVN_ERR(svn_wc__db_read_children(&rel_children, wc_ctx->db, dir_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath, - rel_children, show_hidden, - result_pool, scratch_pool)); - return SVN_NO_ERROR; -} - 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) +svn_wc__node_get_not_present_children(const apr_array_header_t **children, + svn_wc_context_t *wc_ctx, + const char *dir_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc__db_status_t status; - svn_boolean_t have_work; - - SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath, - 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, &have_work, - db, local_abspath, - result_pool, scratch_pool)); - - if ((repos_relpath ? *repos_relpath != NULL : TRUE) - && (repos_root_url ? *repos_root_url != NULL: TRUE) - && (repos_uuid ? *repos_uuid != NULL : TRUE)) - return SVN_NO_ERROR; /* We got the requested information */ - - if (!have_work) /* not-present, (server-)excluded? */ - { - return SVN_NO_ERROR; /* Can't fetch more */ - } - - if (status == svn_wc__db_status_deleted) - { - const char *base_del_abspath, *wrk_del_abspath; - - SVN_ERR(svn_wc__db_scan_deletion(&base_del_abspath, NULL, - &wrk_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - if (base_del_abspath) - { - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, repos_relpath, - repos_root_url, repos_uuid, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - db, base_del_abspath, - result_pool, scratch_pool)); - - /* If we have a repos_relpath, it is of the op-root */ - if (repos_relpath) - *repos_relpath = svn_relpath_join(*repos_relpath, - svn_dirent_skip_ancestor(base_del_abspath, - local_abspath), - result_pool); - /* We keep revision as SVN_INVALID_REVNUM */ - } - else if (wrk_del_abspath) - { - const char *op_root_abspath = NULL; - - SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath - ? &op_root_abspath : NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, svn_dirent_dirname( - wrk_del_abspath, - scratch_pool), - result_pool, scratch_pool)); - - /* If we have a repos_relpath, it is of the op-root */ - if (repos_relpath) - *repos_relpath = svn_relpath_join( - *repos_relpath, - svn_dirent_skip_ancestor(op_root_abspath, - local_abspath), - result_pool); - } - } - else /* added, or WORKING incomplete */ - { - const char *op_root_abspath = NULL; - - /* We have an addition. scan_addition() will find the intended - repository location by scanning up the tree. */ - SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath - ? &op_root_abspath : NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - } + const apr_array_header_t *child_names; - SVN_ERR_ASSERT(repos_root_url == NULL || *repos_root_url != NULL); - SVN_ERR_ASSERT(repos_uuid == NULL || *repos_uuid != NULL); + SVN_ERR(svn_wc__db_base_read_not_present_children( + &child_names, + wc_ctx->db, dir_abspath, + scratch_pool, scratch_pool)); + make_absolute(children, dir_abspath, child_names, result_pool); return SVN_NO_ERROR; } + svn_error_t * svn_wc__node_get_repos_info(svn_revnum_t *revision, const char **repos_relpath, @@ -248,12 +125,12 @@ svn_wc__node_get_repos_info(svn_revnum_t *revision, apr_pool_t *scratch_pool) { return svn_error_trace( - svn_wc__internal_get_repos_info(revision, - repos_relpath, - repos_root_url, - repos_uuid, - wc_ctx->db, local_abspath, - result_pool, scratch_pool)); + svn_wc__db_read_repos_info(revision, + repos_relpath, + repos_root_url, + repos_uuid, + wc_ctx->db, local_abspath, + result_pool, scratch_pool)); } /* Convert DB_KIND into the appropriate NODE_KIND value. @@ -322,21 +199,6 @@ svn_wc_read_kind2(svn_node_kind_t *kind, return SVN_NO_ERROR; } -svn_error_t * -svn_wc__node_get_depth(svn_depth_t *depth, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - return svn_error_trace( - svn_wc__db_read_info(NULL, NULL, 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, - wc_ctx->db, local_abspath, scratch_pool, - scratch_pool)); -} - svn_error_t * svn_wc__node_get_changed_info(svn_revnum_t *changed_rev, apr_time_t *changed_date, @@ -362,8 +224,18 @@ svn_wc__node_get_url(const char **url, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_wc__db_read_url(url, wc_ctx->db, local_abspath, - result_pool, scratch_pool)); + const char *repos_root_url; + const char *repos_relpath; + + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, &repos_root_url, + NULL, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + *url = svn_path_url_add_component2(repos_root_url, repos_relpath, + result_pool); + + return SVN_NO_ERROR; } /* A recursive node-walker, helper for svn_wc__internal_walk_children(). @@ -388,25 +260,24 @@ walker_helper(svn_wc__db_t *db, void *cancel_baton, apr_pool_t *scratch_pool) { - apr_hash_t *rel_children_info; - apr_hash_index_t *hi; apr_pool_t *iterpool; + const apr_array_header_t *items; + int i; if (depth == svn_depth_empty) return SVN_NO_ERROR; - SVN_ERR(svn_wc__db_read_children_walker_info(&rel_children_info, db, - dir_abspath, scratch_pool, - scratch_pool)); + iterpool = svn_pool_create(scratch_pool); + SVN_ERR(svn_wc__db_read_children_walker_info(&items, db, + dir_abspath, scratch_pool, + iterpool)); - iterpool = svn_pool_create(scratch_pool); - for (hi = apr_hash_first(scratch_pool, rel_children_info); - hi; - hi = apr_hash_next(hi)) + for (i = 0; i < items->nelts; i++) { - const char *child_name = svn__apr_hash_index_key(hi); - struct svn_wc__db_walker_info_t *wi = svn__apr_hash_index_val(hi); + struct svn_wc__db_walker_info_t *wi = + APR_ARRAY_IDX(items, i, struct svn_wc__db_walker_info_t *); + const char *child_name = wi->name; svn_node_kind_t child_kind = wi->kind; svn_wc__db_status_t child_status = wi->status; const char *child_abspath; @@ -489,6 +360,7 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, svn_node_kind_t kind; svn_wc__db_status_t status; apr_hash_t *changelist_hash = NULL; + const char *changelist = NULL; SVN_ERR_ASSERT(walk_depth >= svn_depth_empty && walk_depth <= svn_depth_infinity); @@ -501,14 +373,17 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, SVN_ERR(svn_wc__db_read_info(&status, &db_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, + changelist_hash ? &changelist : NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(convert_db_kind_to_node_kind(&kind, db_kind, status, show_hidden)); - if (svn_wc__internal_changelist_match(db, local_abspath, - changelist_hash, scratch_pool)) - SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool)); + if (!changelist_hash + || (changelist && svn_hash_gets(changelist_hash, changelist))) + { + SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool)); + } if (db_kind == svn_node_file || status == svn_wc__db_status_not_present @@ -530,54 +405,6 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, scratch_pool)); } -svn_error_t * -svn_wc__node_is_status_deleted(svn_boolean_t *is_deleted, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - - SVN_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, - wc_ctx->db, local_abspath, - scratch_pool, scratch_pool)); - - *is_deleted = (status == svn_wc__db_status_deleted); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__node_get_deleted_ancestor(const char **deleted_ancestor_abspath, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - - *deleted_ancestor_abspath = NULL; - - SVN_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, - wc_ctx->db, local_abspath, - scratch_pool, scratch_pool)); - - if (status == svn_wc__db_status_deleted) - SVN_ERR(svn_wc__db_scan_deletion(deleted_ancestor_abspath, NULL, NULL, - NULL, wc_ctx->db, local_abspath, - result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - svn_error_t * svn_wc__node_is_not_present(svn_boolean_t *is_not_present, svn_boolean_t *is_excluded, @@ -671,7 +498,6 @@ svn_wc__node_get_base(svn_node_kind_t *kind, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t ignore_enoent, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -691,9 +517,8 @@ svn_wc__node_get_base(svn_node_kind_t *kind, if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); else if (err - || (!err && !show_hidden - && (status != svn_wc__db_status_normal - && status != svn_wc__db_status_incomplete))) + || (status != svn_wc__db_status_normal + && status != svn_wc__db_status_incomplete)) { if (!ignore_enoent) { @@ -782,133 +607,6 @@ svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision, return SVN_NO_ERROR; } -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) -{ - svn_wc__db_status_t status; - svn_boolean_t op_root; - svn_boolean_t have_base; - svn_boolean_t have_work; - svn_boolean_t have_more_work; - const char *copyfrom_relpath; - - if (schedule) - *schedule = svn_wc_schedule_normal; - if (copied) - *copied = FALSE; - - SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, ©from_relpath, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &op_root, NULL, NULL, - &have_base, &have_more_work, &have_work, - db, local_abspath, scratch_pool, scratch_pool)); - - switch (status) - { - case svn_wc__db_status_not_present: - case svn_wc__db_status_server_excluded: - case svn_wc__db_status_excluded: - /* We used status normal in the entries world. */ - if (schedule) - *schedule = svn_wc_schedule_normal; - break; - case svn_wc__db_status_normal: - case svn_wc__db_status_incomplete: - break; - - case svn_wc__db_status_deleted: - { - if (schedule) - *schedule = svn_wc_schedule_delete; - - if (!copied) - break; - - if (have_more_work || !have_base) - *copied = TRUE; - else - { - const char *work_del_abspath; - - /* Find out details of our deletion. */ - SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, - &work_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - if (work_del_abspath) - *copied = TRUE; /* Working deletion */ - } - break; - } - case svn_wc__db_status_added: - { - if (!op_root) - { - if (copied) - *copied = TRUE; - - if (schedule) - *schedule = svn_wc_schedule_normal; - - break; - } - - if (copied) - *copied = (copyfrom_relpath != NULL); - - if (schedule) - *schedule = svn_wc_schedule_add; - else - break; - - /* Check for replaced */ - if (have_base || have_more_work) - { - svn_wc__db_status_t below_working; - SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work, - &below_working, - db, local_abspath, - scratch_pool)); - - /* If the node is not present or deleted (read: not present - in working), then the node is not a replacement */ - if (below_working != svn_wc__db_status_not_present - && below_working != svn_wc__db_status_deleted) - { - *schedule = svn_wc_schedule_replace; - break; - } - } - break; - } - default: - SVN_ERR_MALFUNCTION(); - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__node_get_schedule(svn_wc_schedule_t *schedule, - svn_boolean_t *copied, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - return svn_error_trace( - svn_wc__internal_node_get_schedule(schedule, - copied, - wc_ctx->db, - local_abspath, - scratch_pool)); -} - svn_error_t * svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -952,6 +650,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc__db_t *db, const char *local_abspath, @@ -964,20 +663,24 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char *original_repos_uuid; svn_revnum_t original_revision; svn_wc__db_status_t status; + svn_boolean_t have_more_work; + svn_boolean_t op_root; const char *tmp_repos_relpath; + if (copy_root_abspath) + *copy_root_abspath = NULL; if (!repos_relpath) repos_relpath = &tmp_repos_relpath; SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath, repos_root_url, repos_uuid, NULL, NULL, NULL, - NULL, NULL, NULL, + depth, NULL, NULL, &original_repos_relpath, &original_repos_root_url, &original_repos_uuid, &original_revision, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, is_copy, + NULL, NULL, NULL, NULL, NULL, &op_root, NULL, + NULL, NULL, &have_more_work, is_copy, db, local_abspath, result_pool, scratch_pool)); if (*repos_relpath) @@ -995,6 +698,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, if (original_repos_relpath) { + /* We an have a copy */ *repos_relpath = original_repos_relpath; if (revision) *revision = original_revision; @@ -1005,21 +709,19 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, if (copy_root_abspath == NULL) return SVN_NO_ERROR; + else if (op_root) + { + *copy_root_abspath = apr_pstrdup(result_pool, local_abspath); + return SVN_NO_ERROR; + } } { svn_boolean_t scan_working = FALSE; - if (status == svn_wc__db_status_added) + if (status == svn_wc__db_status_added + || (status == svn_wc__db_status_deleted && have_more_work)) scan_working = TRUE; - else if (status == svn_wc__db_status_deleted) - { - svn_boolean_t have_base; - /* Is this a BASE or a WORKING delete? */ - SVN_ERR(svn_wc__db_info_below_working(&have_base, &scan_working, - &status, db, local_abspath, - scratch_pool)); - } if (scan_working) { @@ -1079,6 +781,7 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -1088,7 +791,7 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, { return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision, repos_relpath, repos_root_url, repos_uuid, - copy_root_abspath, + depth, copy_root_abspath, wc_ctx->db, local_abspath, scan_deleted, result_pool, scratch_pool)); } @@ -1364,16 +1067,22 @@ svn_wc__node_was_moved_away(const char **moved_to_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_boolean_t is_deleted; + svn_wc__db_status_t status; if (moved_to_abspath) *moved_to_abspath = NULL; if (op_root_abspath) *op_root_abspath = NULL; - SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, local_abspath, - scratch_pool)); - if (is_deleted) + SVN_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, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL, op_root_abspath, wc_ctx->db, local_abspath, diff --git a/contrib/subversion/subversion/libsvn_wc/old-and-busted.c b/contrib/subversion/subversion/libsvn_wc/old-and-busted.c index b87be8555..8cd94af27 100644 --- a/contrib/subversion/subversion/libsvn_wc/old-and-busted.c +++ b/contrib/subversion/subversion/libsvn_wc/old-and-busted.c @@ -443,7 +443,7 @@ svn_wc__serialize_file_external(const char **str, SVN_ERR(opt_revision_to_string(&s1, path, peg_rev, pool)); SVN_ERR(opt_revision_to_string(&s2, path, rev, pool)); - s = apr_pstrcat(pool, s1, ":", s2, ":", path, (char *)NULL); + s = apr_pstrcat(pool, s1, ":", s2, ":", path, SVN_VA_NULL); } else s = NULL; @@ -1154,7 +1154,7 @@ resolve_to_defaults(apr_hash_t *entries, /* Then use it to fill in missing information in other entries. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { - svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi); + svn_wc_entry_t *this_entry = apr_hash_this_val(hi); if (this_entry == default_entry) /* THIS_DIR already has all the information it can possibly diff --git a/contrib/subversion/subversion/libsvn_wc/props.c b/contrib/subversion/subversion/libsvn_wc/props.c index a7b2339b0..664bcf170 100644 --- a/contrib/subversion/subversion/libsvn_wc/props.c +++ b/contrib/subversion/subversion/libsvn_wc/props.c @@ -62,31 +62,6 @@ #include "svn_private_config.h" -/* Forward declaration. */ -static svn_error_t * -prop_conflict_from_skel(const svn_string_t **conflict_desc, - const svn_skel_t *skel, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - -/* Given a *SINGLE* property conflict in PROP_SKEL, generate a description - for it, and write it to STREAM, along with a trailing EOL sequence. - - See prop_conflict_from_skel() for details on PROP_SKEL. */ -static svn_error_t * -append_prop_conflict(svn_stream_t *stream, - const svn_skel_t *prop_skel, - apr_pool_t *pool) -{ - /* TODO: someday, perhaps prefix each conflict_description with a - timestamp or something? */ - const svn_string_t *conflict_desc; - - SVN_ERR(prop_conflict_from_skel(&conflict_desc, prop_skel, pool, pool)); - - return svn_stream_puts(stream, conflict_desc->data); -} - /*---------------------------------------------------------------------*/ /*** Merging propchanges into the working copy ***/ @@ -352,7 +327,8 @@ svn_wc_merge_props3(svn_wc_notify_state_t *state, { svn_boolean_t prop_conflicted; - SVN_ERR(svn_wc__conflict_invoke_resolver(db, local_abspath, conflict_skel, + SVN_ERR(svn_wc__conflict_invoke_resolver(db, local_abspath, kind, + conflict_skel, NULL /* merge_options */, conflict_func, conflict_baton, cancel_func, cancel_baton, @@ -531,89 +507,96 @@ maybe_prop_value(const svn_skel_t *skel, } -/* Parse a property conflict description from the provided SKEL. - The result includes a descriptive message (see generate_conflict_message) - and maybe a diff of property values containing conflict markers. - The result will be allocated in RESULT_POOL. - - Note: SKEL is a single property conflict of the form: - - ("prop" ([ORIGINAL]) ([MINE]) ([INCOMING]) ([INCOMING_BASE])) - - See notes/wc-ng/conflict-storage for more information. */ +/* Create a property rejection description for the specified property. + The result will be allocated in RESULT_POOL. */ static svn_error_t * -prop_conflict_from_skel(const svn_string_t **conflict_desc, - const svn_skel_t *skel, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +prop_conflict_new(const svn_string_t **conflict_desc, + const char *propname, + const svn_string_t *original, + const svn_string_t *mine, + const svn_string_t *incoming, + const svn_string_t *incoming_base, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - const svn_string_t *original; - const svn_string_t *mine; - const svn_string_t *incoming; - const svn_string_t *incoming_base; - const char *propname; svn_diff_t *diff; svn_diff_file_options_t *diff_opts; svn_stringbuf_t *buf; - svn_boolean_t original_is_binary; + svn_boolean_t incoming_base_is_binary; svn_boolean_t mine_is_binary; svn_boolean_t incoming_is_binary; - /* Navigate to the property name. */ - skel = skel->children->next; - - /* We need to copy these into SCRATCH_POOL in order to nul-terminate - the values. */ - propname = apr_pstrmemdup(scratch_pool, skel->data, skel->len); - original = maybe_prop_value(skel->next, scratch_pool); - mine = maybe_prop_value(skel->next->next, scratch_pool); - incoming = maybe_prop_value(skel->next->next->next, scratch_pool); - incoming_base = maybe_prop_value(skel->next->next->next->next, scratch_pool); - buf = generate_conflict_message(propname, original, mine, incoming, incoming_base, scratch_pool); + /* Convert deleted or not-yet-added values to empty-string values, for the + purposes of diff generation and binary detection. */ if (mine == NULL) mine = svn_string_create_empty(scratch_pool); if (incoming == NULL) incoming = svn_string_create_empty(scratch_pool); + if (incoming_base == NULL) + incoming_base = svn_string_create_empty(scratch_pool); - /* Pick a suitable base for the conflict diff. - * The incoming value is always a change, - * but the local value might not have changed. */ - if (original == NULL) - { - if (incoming_base) - original = incoming_base; - else - original = svn_string_create_empty(scratch_pool); - } - else if (incoming_base && svn_string_compare(original, mine)) - original = incoming_base; + /* How we render the conflict: + + We have four sides: original, mine, incoming_base, incoming. + We render the conflict as a 3-way diff. A diff3 API has three parts, + called: + + <<< - original + ||| - modified (or "older") + === - latest (or "theirs") + >>> + + We fill those parts as follows: + + PART FILLED BY SKEL MEMBER USER-FACING ROLE + ==== ===================== ================ + original mine was WORKING tree at conflict creation + modified incoming_base left-hand side of merge + latest incoming right-hand side of merge + (none) original was BASE tree at conflict creation + + An 'update -r rN' is treated like a 'merge -r BASE:rN', i.e., in an + 'update' operation skel->original and skel->incoming_base coincide. + + Note that the term "original" is used both in the skel and in diff3 + with different meanings. Note also that the skel's ORIGINAL value was + at some point in the BASE tree, but the BASE tree need not have contained + the INCOMING_BASE value. + + Yes, it's confusing. */ /* If any of the property values involved in the diff is binary data, * do not generate a diff. */ - original_is_binary = svn_io_is_binary_data(original->data, original->len); + incoming_base_is_binary = svn_io_is_binary_data(incoming_base->data, + incoming_base->len); mine_is_binary = svn_io_is_binary_data(mine->data, mine->len); incoming_is_binary = svn_io_is_binary_data(incoming->data, incoming->len); - if (!(original_is_binary || mine_is_binary || incoming_is_binary)) + if (!(incoming_base_is_binary || mine_is_binary || incoming_is_binary)) { diff_opts = svn_diff_file_options_create(scratch_pool); diff_opts->ignore_space = svn_diff_file_ignore_space_none; diff_opts->ignore_eol_style = FALSE; diff_opts->show_c_function = FALSE; - SVN_ERR(svn_diff_mem_string_diff3(&diff, original, mine, incoming, + /* Pass skel member INCOMING_BASE into the formal parameter ORIGINAL. + Ignore the skel member ORIGINAL. */ + SVN_ERR(svn_diff_mem_string_diff3(&diff, incoming_base, mine, incoming, diff_opts, scratch_pool)); if (svn_diff_contains_conflicts(diff)) { svn_stream_t *stream; svn_diff_conflict_display_style_t style; const char *mine_marker = _("<<<<<<< (local property value)"); - const char *incoming_marker = _(">>>>>>> (incoming property value)"); + const char *incoming_marker = _(">>>>>>> (incoming 'changed to' value)"); + const char *incoming_base_marker = _("||||||| (incoming 'changed from' value)"); const char *separator = "======="; - svn_string_t *original_ascii = - svn_string_create(svn_utf_cstring_from_utf8_fuzzy(original->data, + svn_string_t *incoming_base_ascii = + svn_string_create(svn_utf_cstring_from_utf8_fuzzy(incoming_base->data, scratch_pool), scratch_pool); svn_string_t *mine_ascii = @@ -625,16 +608,18 @@ prop_conflict_from_skel(const svn_string_t **conflict_desc, scratch_pool), scratch_pool); - style = svn_diff_conflict_display_modified_latest; + style = svn_diff_conflict_display_modified_original_latest; stream = svn_stream_from_stringbuf(buf, scratch_pool); SVN_ERR(svn_stream_skip(stream, buf->len)); - SVN_ERR(svn_diff_mem_string_output_merge2(stream, diff, - original_ascii, + SVN_ERR(svn_diff_mem_string_output_merge3(stream, diff, + incoming_base_ascii, mine_ascii, incoming_ascii, - NULL, mine_marker, + incoming_base_marker, mine_marker, incoming_marker, separator, - style, scratch_pool)); + style, + cancel_func, cancel_baton, + scratch_pool)); SVN_ERR(svn_stream_close(stream)); *conflict_desc = svn_string_create_from_buf(buf, result_pool); @@ -669,6 +654,49 @@ prop_conflict_from_skel(const svn_string_t **conflict_desc, return SVN_NO_ERROR; } +/* Parse a property conflict description from the provided SKEL. + The result includes a descriptive message (see generate_conflict_message) + and maybe a diff of property values containing conflict markers. + The result will be allocated in RESULT_POOL. + + Note: SKEL is a single property conflict of the form: + + ("prop" ([ORIGINAL]) ([MINE]) ([INCOMING]) ([INCOMING_BASE])) + + Note: This is not the same format as the property conflicts we store in + wc.db since 1.8. This is the legacy format used in the Workqueue in 1.7-1.8 */ +static svn_error_t * +prop_conflict_from_skel(const svn_string_t **conflict_desc, + const svn_skel_t *skel, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_string_t *original; + const svn_string_t *mine; + const svn_string_t *incoming; + const svn_string_t *incoming_base; + const char *propname; + + /* Navigate to the property name. */ + skel = skel->children->next; + + /* We need to copy these into SCRATCH_POOL in order to nul-terminate + the values. */ + propname = apr_pstrmemdup(scratch_pool, skel->data, skel->len); + original = maybe_prop_value(skel->next, scratch_pool); + mine = maybe_prop_value(skel->next->next, scratch_pool); + incoming = maybe_prop_value(skel->next->next->next, scratch_pool); + incoming_base = maybe_prop_value(skel->next->next->next->next, scratch_pool); + + return svn_error_trace(prop_conflict_new(conflict_desc, + propname, + original, mine, + incoming, incoming_base, + cancel_func, cancel_baton, + result_pool, scratch_pool)); +} /* Create a property conflict file at PREJFILE based on the property conflicts in CONFLICT_SKEL. */ @@ -676,7 +704,9 @@ svn_error_t * svn_wc__create_prejfile(const char **tmp_prejfile_abspath, svn_wc__db_t *db, const char *local_abspath, - const svn_skel_t *conflict_skel, + const svn_skel_t *prop_conflict_data, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -694,11 +724,88 @@ svn_wc__create_prejfile(const char **tmp_prejfile_abspath, tempdir_abspath, svn_io_file_del_none, scratch_pool, iterpool)); - for (scan = conflict_skel->children->next; scan != NULL; scan = scan->next) + if (prop_conflict_data) { - svn_pool_clear(iterpool); + for (scan = prop_conflict_data->children->next; + scan != NULL; scan = scan->next) + { + const svn_string_t *conflict_desc; + + svn_pool_clear(iterpool); + + SVN_ERR(prop_conflict_from_skel(&conflict_desc, scan, + cancel_func, cancel_baton, + iterpool, iterpool)); - SVN_ERR(append_prop_conflict(stream, scan, iterpool)); + SVN_ERR(svn_stream_puts(stream, conflict_desc->data)); + } + } + else + { + svn_wc_operation_t operation; + 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_skel_t *conflicts; + + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, NULL, + db, local_abspath, + conflicts, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, + &mine_props, + &their_original_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_original_props; + + /* ### TODO: Sort conflicts? */ + for (hi = apr_hash_first(scratch_pool, conflicted_props); + hi; + hi = apr_hash_next(hi)) + { + const svn_string_t *conflict_desc; + const char *propname = apr_hash_this_key(hi); + const svn_string_t *old_value; + const svn_string_t *mine_value; + const svn_string_t *their_value; + const svn_string_t *their_original_value; + + svn_pool_clear(iterpool); + + old_value = old_props ? svn_hash_gets(old_props, propname) : NULL; + mine_value = mine_props ? svn_hash_gets(mine_props, propname) : NULL; + their_value = their_props ? svn_hash_gets(their_props, propname) + : NULL; + their_original_value = their_original_props + ? svn_hash_gets(their_original_props, propname) + : NULL; + + SVN_ERR(prop_conflict_new(&conflict_desc, + propname, old_value, mine_value, + their_value, their_original_value, + cancel_func, cancel_baton, + iterpool, iterpool)); + + SVN_ERR(svn_stream_puts(stream, conflict_desc->data)); + } } SVN_ERR(svn_stream_close(stream)); @@ -1167,7 +1274,7 @@ svn_wc__merge_props(svn_skel_t **conflict_skel, svn_pool_clear(iterpool); - to_val = to_val ? svn_string_dup(to_val, result_pool) : NULL; + to_val = svn_string_dup(to_val, result_pool); svn_hash_sets(their_props, propname, to_val); @@ -2030,8 +2137,8 @@ svn_wc__canonicalize_props(apr_hash_t **prepared_props, for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_string_t *value = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_string_t *value = apr_hash_this_val(hi); if (strcmp(name, SVN_PROP_MIME_TYPE) == 0) continue; diff --git a/contrib/subversion/subversion/libsvn_wc/props.h b/contrib/subversion/subversion/libsvn_wc/props.h index c648e3c42..c33e13e58 100644 --- a/contrib/subversion/subversion/libsvn_wc/props.h +++ b/contrib/subversion/subversion/libsvn_wc/props.h @@ -139,11 +139,17 @@ svn_wc__get_actual_props(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Creates a property reject file at *TMP_PREJFILE_ABSPATH, with + either the property conflict data from DB (when PROP_CONFLICT_DATA + is NULL) or the information in PROP_CONFLICT_DATA if it isn't. + */ svn_error_t * svn_wc__create_prejfile(const char **tmp_prejfile_abspath, svn_wc__db_t *db, const char *local_abspath, - const svn_skel_t *conflict_skel, + const svn_skel_t *prop_conflict_data, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_wc/questions.c b/contrib/subversion/subversion/libsvn_wc/questions.c index c2a42b6ad..08583639b 100644 --- a/contrib/subversion/subversion/libsvn_wc/questions.c +++ b/contrib/subversion/subversion/libsvn_wc/questions.c @@ -117,6 +117,7 @@ compare_and_verify(svn_boolean_t *modified_p, apr_hash_t *keywords; svn_boolean_t special = FALSE; svn_boolean_t need_translation; + svn_stream_t *v_stream; /* versioned_file */ SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath)); @@ -150,22 +151,24 @@ compare_and_verify(svn_boolean_t *modified_p, /* ### Other checks possible? */ - if (need_translation) + /* Reading files is necessary. */ + if (special && need_translation) { - /* Reading files is necessary. */ - svn_stream_t *v_stream; /* versioned_file */ - - if (special) - { - SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - } - else + SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath, + scratch_pool, scratch_pool)); + } + else + { + /* We don't use APR-level buffering because the comparison function + * will do its own buffering. */ + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, versioned_file_abspath, APR_READ, + APR_OS_DEFAULT, scratch_pool)); + v_stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool); + + if (need_translation) { - SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - - if (!exact_comparison && need_translation) + if (!exact_comparison) { if (eol_style == svn_subst_eol_style_native) eol_str = SVN_SUBST_NATIVE_EOL_STR; @@ -183,7 +186,7 @@ compare_and_verify(svn_boolean_t *modified_p, FALSE /* expand */, scratch_pool); } - else if (need_translation) + else { /* Wrap base stream to translate into working copy form, and * arrange to throw an error if its EOL style is inconsistent. */ @@ -193,21 +196,10 @@ compare_and_verify(svn_boolean_t *modified_p, scratch_pool); } } - - SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, - scratch_pool)); } - else - { - /* Translation would be a no-op, so compare the original file. */ - svn_stream_t *v_stream; /* versioned_file */ - SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, - scratch_pool)); - } + SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, + scratch_pool)); *modified_p = (! same); @@ -377,7 +369,8 @@ internal_conflicted_p(svn_boolean_t *text_conflicted_p, svn_boolean_t resolved_text = FALSE; svn_boolean_t resolved_props = FALSE; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); if (!conflicts) @@ -604,18 +597,150 @@ svn_wc__has_switched_subtrees(svn_boolean_t *is_switched, } +/* A baton for use with modcheck_found_entry(). */ +typedef struct modcheck_baton_t { + svn_boolean_t ignore_unversioned; + 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_ignored: + case svn_wc_status_none: + case svn_wc_status_external: + break; + + case svn_wc_status_incomplete: + if ((status->text_status != svn_wc_status_normal + && status->text_status != svn_wc_status_none) + || (status->prop_status != svn_wc_status_normal + && status->prop_status != svn_wc_status_none)) + { + mb->found_mod = TRUE; + mb->found_not_delete = TRUE; + /* Incomplete, but local modifications */ + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + } + break; + + case svn_wc_status_deleted: + mb->found_mod = TRUE; + if (!mb->ignore_unversioned + && status->actual_kind != svn_node_none + && status->actual_kind != svn_node_unknown) + { + /* The delete is obstructed by something unversioned */ + mb->found_not_delete = TRUE; + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + } + break; + + case svn_wc_status_unversioned: + if (mb->ignore_unversioned) + break; + /* else fall through */ + 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_boolean_t ignore_unversioned, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + modcheck_baton_t modcheck_baton = { FALSE, FALSE, FALSE }; + svn_error_t *err; + + if (!all_edits_are_deletes) + { + SVN_ERR(svn_wc__db_has_db_mods(modified, db, local_abspath, + scratch_pool)); + + if (*modified) + return SVN_NO_ERROR; + } + + modcheck_baton.ignore_unversioned = ignore_unversioned; + + /* 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; + if (all_edits_are_deletes) + *all_edits_are_deletes = (modcheck_baton.found_mod + && !modcheck_baton.found_not_delete); + + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__has_local_mods(svn_boolean_t *is_modified, svn_wc_context_t *wc_ctx, const char *local_abspath, + svn_boolean_t ignore_unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_wc__db_has_local_mods(is_modified, - wc_ctx->db, - local_abspath, - cancel_func, - cancel_baton, - scratch_pool)); + svn_boolean_t modified; + + SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, + wc_ctx->db, local_abspath, + ignore_unversioned, + cancel_func, cancel_baton, + scratch_pool)); + + *is_modified = modified; + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_wc/relocate.c b/contrib/subversion/subversion/libsvn_wc/relocate.c index 4a9df678a..e4b335b5b 100644 --- a/contrib/subversion/subversion/libsvn_wc/relocate.c +++ b/contrib/subversion/subversion/libsvn_wc/relocate.c @@ -148,7 +148,7 @@ svn_wc_relocate4(svn_wc_context_t *wc_ctx, if (old_url_len == from_len) new_url = to; else - new_url = apr_pstrcat(scratch_pool, to, old_url + from_len, (char *)NULL); + new_url = apr_pstrcat(scratch_pool, to, old_url + from_len, SVN_VA_NULL); if (! svn_path_is_url(new_url)) return svn_error_createf(SVN_ERR_WC_INVALID_RELOCATION, NULL, _("Invalid relocation destination: '%s' " diff --git a/contrib/subversion/subversion/libsvn_wc/revert.c b/contrib/subversion/subversion/libsvn_wc/revert.c index 5e190e89c..bba179986 100644 --- a/contrib/subversion/subversion/libsvn_wc/revert.c +++ b/contrib/subversion/subversion/libsvn_wc/revert.c @@ -46,6 +46,7 @@ #include "svn_private_config.h" #include "private/svn_io_private.h" #include "private/svn_wc_private.h" +#include "private/svn_sorts_private.h" /* Thoughts on Reversion. @@ -154,7 +155,7 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, void *cancel_baton, apr_pool_t *scratch_pool) { - const apr_array_header_t *copied_children; + apr_array_header_t *copied_children; svn_wc__db_revert_list_copied_child_info_t *child_info; int i; svn_node_kind_t on_disk; @@ -198,9 +199,7 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, * that still exist on disk (e.g. unversioned files within the copied tree). * So sort the children list such that longest paths come first and try to * remove each child directory in order. */ - qsort(copied_children->elts, copied_children->nelts, - sizeof(svn_wc__db_revert_list_copied_child_info_t *), - compare_revert_list_copied_children); + svn_sort__array(copied_children, compare_revert_list_copied_children); for (i = 0; i < copied_children->nelts; i++) { child_info = APR_ARRAY_IDX( @@ -247,6 +246,22 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, return SVN_NO_ERROR; } +/* Forward definition */ +static svn_error_t * +revert_wc_data(svn_boolean_t *run_wq, + svn_boolean_t *notify_required, + svn_wc__db_t *db, + const char *local_abspath, + svn_wc__db_status_t status, + svn_node_kind_t kind, + svn_node_kind_t reverted_kind, + svn_filesize_t recorded_size, + apr_time_t recorded_time, + svn_boolean_t copied_here, + svn_boolean_t use_commit_times, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); /* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the versioned tree. This function is called after svn_wc__db_op_revert @@ -255,56 +270,63 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, REVERT_ROOT is true for explicit revert targets and FALSE for targets reached via recursion. + + Sets *RUN_WQ to TRUE when the caller should (eventually) run the workqueue. + (The function sets it to FALSE when it has run the WQ itself) + + If INFO is NULL, LOCAL_ABSPATH doesn't exist in DB. Otherwise INFO + specifies the state of LOCAL_ABSPATH in DB. */ static svn_error_t * -revert_restore(svn_wc__db_t *db, +revert_restore(svn_boolean_t *run_wq, + svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t metadata_only, svn_boolean_t use_commit_times, svn_boolean_t revert_root, + const struct svn_wc__db_info_t *info, 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_wc__db_status_t status; svn_node_kind_t kind; - svn_node_kind_t on_disk; svn_boolean_t notify_required; const apr_array_header_t *conflict_files; svn_filesize_t recorded_size; apr_time_t recorded_time; - apr_finfo_t finfo; -#ifdef HAVE_SYMLINK - svn_boolean_t special; -#endif svn_boolean_t copied_here; svn_node_kind_t reverted_kind; - svn_boolean_t is_wcroot; - if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool)); - if (is_wcroot && !revert_root) + if (!revert_root) { - /* Issue #4162: Obstructing working copy. We can't access the working - copy data from the parent working copy for this node by just using - local_abspath */ + svn_boolean_t is_wcroot; - if (notify_func) + SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool)); + if (is_wcroot) { - svn_wc_notify_t *notify = svn_wc_create_notify( + /* Issue #4162: Obstructing working copy. We can't access the working + copy data from the parent working copy for this node by just using + local_abspath */ + + if (notify_func) + { + svn_wc_notify_t *notify = + svn_wc_create_notify( local_abspath, svn_wc_notify_update_skip_obstruction, scratch_pool); - notify_func(notify_baton, notify, scratch_pool); - } + notify_func(notify_baton, notify, scratch_pool); + } - return SVN_NO_ERROR; /* We don't revert obstructing working copies */ + return SVN_NO_ERROR; /* We don't revert obstructing working copies */ + } } SVN_ERR(svn_wc__db_revert_list_read(¬ify_required, @@ -313,17 +335,15 @@ revert_restore(svn_wc__db_t *db, db, local_abspath, scratch_pool, scratch_pool)); - err = svn_wc__db_read_info(&status, &kind, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &recorded_size, &recorded_time, 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) + if (info) + { + status = info->status; + kind = info->kind; + recorded_size = info->recorded_size; + recorded_time = info->recorded_time; + } + else { - svn_error_clear(err); - if (!copied_here) { if (notify_func && notify_required) @@ -350,9 +370,116 @@ revert_restore(svn_wc__db_t *db, recorded_time = 0; } } - else if (err) - return svn_error_trace(err); + if (!metadata_only) + { + SVN_ERR(revert_wc_data(run_wq, + ¬ify_required, + db, local_abspath, status, kind, + reverted_kind, recorded_size, recorded_time, + copied_here, use_commit_times, + cancel_func, cancel_baton, scratch_pool)); + } + + /* We delete these marker files even though they are not strictly metadata. + But for users that use revert as an API with metadata_only, these are. */ + if (conflict_files) + { + int i; + for (i = 0; i < conflict_files->nelts; i++) + { + SVN_ERR(remove_conflict_file(¬ify_required, + APR_ARRAY_IDX(conflict_files, i, + const char *), + local_abspath, scratch_pool)); + } + } + + if (notify_func && notify_required) + notify_func(notify_baton, + svn_wc_create_notify(local_abspath, svn_wc_notify_revert, + scratch_pool), + scratch_pool); + + if (depth == svn_depth_infinity && kind == svn_node_dir) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *children, *conflicts; + apr_hash_index_t *hi; + + SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE, + cancel_func, cancel_baton, + iterpool)); + + SVN_ERR(svn_wc__db_read_children_info(&children, &conflicts, + db, local_abspath, FALSE, + scratch_pool, iterpool)); + + for (hi = apr_hash_first(scratch_pool, children); + hi; + hi = apr_hash_next(hi)) + { + const char *child_name = apr_hash_this_key(hi); + const char *child_abspath; + + svn_pool_clear(iterpool); + + child_abspath = svn_dirent_join(local_abspath, child_name, iterpool); + + SVN_ERR(revert_restore(run_wq, + db, child_abspath, depth, metadata_only, + use_commit_times, FALSE /* revert root */, + apr_hash_this_val(hi), + cancel_func, cancel_baton, + notify_func, notify_baton, + iterpool)); + } + + /* Run the queue per directory */ + if (*run_wq) + { + SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, + iterpool)); + *run_wq = FALSE; + } + + svn_pool_destroy(iterpool); + } + + if (notify_func && (revert_root || kind == svn_node_dir)) + SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton, + db, local_abspath, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Perform the in-working copy revert of LOCAL_ABSPATH, to what is stored in DB */ +static svn_error_t * +revert_wc_data(svn_boolean_t *run_wq, + svn_boolean_t *notify_required, + svn_wc__db_t *db, + const char *local_abspath, + svn_wc__db_status_t status, + svn_node_kind_t kind, + svn_node_kind_t reverted_kind, + svn_filesize_t recorded_size, + apr_time_t recorded_time, + svn_boolean_t copied_here, + svn_boolean_t use_commit_times, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + apr_finfo_t finfo; + svn_node_kind_t on_disk; +#ifdef HAVE_SYMLINK + svn_boolean_t special; +#endif + + /* Would be nice to use svn_io_dirent2_t here, but the performance + improvement that provides doesn't work, because we need the read + only and executable bits later on, in the most likely code path */ err = svn_io_stat(&finfo, local_abspath, APR_FINFO_TYPE | APR_FINFO_LINK | APR_FINFO_SIZE | APR_FINFO_MTIME @@ -478,14 +605,14 @@ revert_restore(svn_wc__db_t *db, modified = FALSE; } else + /* Side effect: fixes recorded timestamps */ SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath, TRUE, scratch_pool)); if (modified) { - SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, - scratch_pool)); + /* Install will replace the file */ on_disk = svn_node_none; } else @@ -505,14 +632,14 @@ revert_restore(svn_wc__db_t *db, SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } else if (!needs_lock_prop && read_only) { SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } } @@ -533,14 +660,14 @@ revert_restore(svn_wc__db_t *db, SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } else if (!executable_prop && executable) { SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } } #endif @@ -564,90 +691,36 @@ revert_restore(svn_wc__db_t *db, { svn_skel_t *work_item; - /* ### Get the checksum from read_info above and pass in here? */ SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath, NULL, use_commit_times, TRUE, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item, scratch_pool)); - SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, - scratch_pool)); + *run_wq = TRUE; } - notify_required = TRUE; + *notify_required = TRUE; } - if (conflict_files) - { - int i; - for (i = 0; i < conflict_files->nelts; i++) - { - SVN_ERR(remove_conflict_file(¬ify_required, - APR_ARRAY_IDX(conflict_files, i, - const char *), - local_abspath, scratch_pool)); - } - } - - if (notify_func && notify_required) - notify_func(notify_baton, - svn_wc_create_notify(local_abspath, svn_wc_notify_revert, - scratch_pool), - scratch_pool); - - if (depth == svn_depth_infinity && kind == svn_node_dir) - { - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - const apr_array_header_t *children; - int i; - - SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE, - cancel_func, cancel_baton, - iterpool)); - - 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(revert_restore(db, child_abspath, depth, - use_commit_times, FALSE /* revert root */, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); - } - - svn_pool_destroy(iterpool); - } - - if (notify_func) - SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton, - db, local_abspath, scratch_pool)); return SVN_NO_ERROR; } - -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) +/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all reverts. */ +static svn_error_t * +revert(svn_wc__db_t *db, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t use_commit_times, + svn_boolean_t clear_changelists, + 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) { svn_error_t *err; + const struct svn_wc__db_info_t *info = NULL; + svn_boolean_t run_queue = FALSE; SVN_ERR_ASSERT(depth == svn_depth_empty || depth == svn_depth_infinity); @@ -667,15 +740,37 @@ svn_wc__revert_internal(svn_wc__db_t *db, SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool)); } - err = svn_wc__db_op_revert(db, local_abspath, depth, - scratch_pool, scratch_pool); + err = svn_error_trace( + svn_wc__db_op_revert(db, local_abspath, depth, clear_changelists, + scratch_pool, scratch_pool)); if (!err) - err = revert_restore(db, local_abspath, depth, - use_commit_times, TRUE /* revert root */, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool); + { + err = svn_error_trace( + svn_wc__db_read_single_info(&info, db, local_abspath, FALSE, + scratch_pool, scratch_pool)); + + if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + { + svn_error_clear(err); + err = NULL; + info = NULL; + } + } + + if (!err) + err = svn_error_trace( + revert_restore(&run_queue, db, local_abspath, depth, metadata_only, + use_commit_times, TRUE /* revert root */, + info, cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); + + if (run_queue) + err = svn_error_compose_create(err, + svn_wc__wq_run(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); err = svn_error_compose_create(err, svn_wc__db_revert_list_done(db, @@ -694,6 +789,8 @@ revert_changelist(svn_wc__db_t *db, svn_depth_t depth, svn_boolean_t use_commit_times, apr_hash_t *changelist_hash, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -710,11 +807,12 @@ revert_changelist(svn_wc__db_t *db, /* Revert this node (depth=empty) if it matches one of the changelists. */ if (svn_wc__internal_changelist_match(db, local_abspath, changelist_hash, scratch_pool)) - SVN_ERR(svn_wc__revert_internal(db, local_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); + SVN_ERR(revert(db, local_abspath, + svn_depth_empty, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); if (depth == svn_depth_empty) return SVN_NO_ERROR; @@ -746,6 +844,7 @@ revert_changelist(svn_wc__db_t *db, SVN_ERR(revert_changelist(db, child_abspath, depth, use_commit_times, changelist_hash, + clear_changelists, metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, iterpool)); @@ -770,6 +869,8 @@ revert_partial(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, svn_boolean_t use_commit_times, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -789,9 +890,10 @@ revert_partial(svn_wc__db_t *db, /* Revert the root node itself (depth=empty), then move on to the children. */ - SVN_ERR(svn_wc__revert_internal(db, local_abspath, svn_depth_empty, - use_commit_times, cancel_func, cancel_baton, - notify_func, notify_baton, iterpool)); + SVN_ERR(revert(db, local_abspath, svn_depth_empty, + use_commit_times, clear_changelists, metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, iterpool)); SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db, local_abspath, @@ -822,11 +924,12 @@ revert_partial(svn_wc__db_t *db, } /* Revert just this node (depth=empty). */ - SVN_ERR(svn_wc__revert_internal(db, child_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); + SVN_ERR(revert(db, child_abspath, + svn_depth_empty, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + iterpool)); } svn_pool_destroy(iterpool); @@ -836,11 +939,13 @@ revert_partial(svn_wc__db_t *db, svn_error_t * -svn_wc_revert4(svn_wc_context_t *wc_ctx, +svn_wc_revert5(svn_wc_context_t *wc_ctx, const char *local_abspath, svn_depth_t depth, svn_boolean_t use_commit_times, const apr_array_header_t *changelist_filter, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -856,17 +961,20 @@ svn_wc_revert4(svn_wc_context_t *wc_ctx, return svn_error_trace(revert_changelist(wc_ctx->db, local_abspath, depth, use_commit_times, changelist_hash, + clear_changelists, + metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); } if (depth == svn_depth_empty || depth == svn_depth_infinity) - return svn_error_trace(svn_wc__revert_internal(wc_ctx->db, local_abspath, - depth, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); + return svn_error_trace(revert(wc_ctx->db, local_abspath, + depth, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); /* The user may expect svn_depth_files/svn_depth_immediates to work on copied dirs with one level of children. It doesn't, the user @@ -877,6 +985,7 @@ svn_wc_revert4(svn_wc_context_t *wc_ctx, if (depth == svn_depth_files || depth == svn_depth_immediates) return svn_error_trace(revert_partial(wc_ctx->db, local_abspath, depth, use_commit_times, + clear_changelists, metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); diff --git a/contrib/subversion/subversion/libsvn_wc/revision_status.c b/contrib/subversion/subversion/libsvn_wc/revision_status.c index a4b9bea81..c53c45e6a 100644 --- a/contrib/subversion/subversion/libsvn_wc/revision_status.c +++ b/contrib/subversion/subversion/libsvn_wc/revision_status.c @@ -60,8 +60,14 @@ svn_wc_revision_status2(svn_wc_revision_status_t **result_p, &result->modified, &result->switched, wc_ctx->db, local_abspath, trail_url, - committed, cancel_func, cancel_baton, + committed, scratch_pool)); + if (!result->modified) + SVN_ERR(svn_wc__node_has_local_mods(&result->modified, NULL, + wc_ctx->db, local_abspath, TRUE, + cancel_func, cancel_baton, + scratch_pool)); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/libsvn_wc/status.c b/contrib/subversion/subversion/libsvn_wc/status.c index fa57b0aee..83fd3d4a3 100644 --- a/contrib/subversion/subversion/libsvn_wc/status.c +++ b/contrib/subversion/subversion/libsvn_wc/status.c @@ -36,7 +36,6 @@ #include "svn_string.h" #include "svn_error.h" #include "svn_dirent_uri.h" -#include "svn_path.h" #include "svn_io.h" #include "svn_config.h" #include "svn_time.h" @@ -47,14 +46,32 @@ #include "wc.h" #include "props.h" -#include "entries.h" -#include "translate.h" -#include "tree_conflicts.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" #include "private/svn_fspath.h" #include "private/svn_editor.h" + +/* The file internal variant of svn_wc_status3_t, with slightly more + data. + + Instead of directly creating svn_wc_status3_t instances, we really + create instances of this struct with slightly more data for processing + by the status walker and status editor. + + svn_wc_status3_dup() allocates space for this struct, but doesn't + copy the actual data. The remaining fields are copied by hash_stash(), + which is where the status editor stashes information for producing + later. */ +typedef struct svn_wc__internal_status_t +{ + svn_wc_status3_t s; /* First member; same pointer*/ + + svn_boolean_t has_descendants; + + /* Make sure to update hash_stash() when adding values here */ +} svn_wc__internal_status_t; /*** Baton used for walking the local status */ @@ -70,6 +87,9 @@ struct walk_status_baton /* Should we ignore text modifications? */ svn_boolean_t ignore_text_mods; + /* Scan the working copy for local modifications and missing nodes. */ + svn_boolean_t check_working_copy; + /* Externals info harvested during the status run. */ apr_hash_t *externals; @@ -92,7 +112,6 @@ struct edit_baton /* The DB handle for managing the working copy state. */ svn_wc__db_t *db; - svn_wc_context_t *wc_ctx; /* The overall depth of this edit (a dir baton may override this). * @@ -127,7 +146,7 @@ struct edit_baton const apr_array_header_t *ignores; /* Status item for the path represented by the anchor of the edit. */ - svn_wc_status3_t *anchor_status; + svn_wc__internal_status_t *anchor_status; /* Was open_root() called for this edit drive? */ svn_boolean_t root_opened; @@ -276,63 +295,23 @@ get_repos_root_url_relpath(const char **repos_relpath, *repos_root_url = apr_pstrdup(result_pool, parent_repos_root_url); *repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid); } - else if (info->status == svn_wc__db_status_added) - { - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - } - else if (info->status == svn_wc__db_status_deleted - && !info->have_more_work - && info->have_base) + else { - SVN_ERR(svn_wc__db_scan_base_repos(repos_relpath, repos_root_url, + SVN_ERR(svn_wc__db_read_repos_info(NULL, + repos_relpath, repos_root_url, repos_uuid, db, local_abspath, result_pool, scratch_pool)); } - else if (info->status == svn_wc__db_status_deleted) - { - const char *work_del_abspath; - const char *add_abspath; - - /* Handles working DELETE and the special case where there is just - svn_wc__db_status_not_present in WORKING */ - - SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, &work_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - /* The parent of what has been deleted must be added */ - add_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); - - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, repos_relpath, - repos_root_url, repos_uuid, NULL, - NULL, NULL, NULL, - db, add_abspath, - result_pool, scratch_pool)); - *repos_relpath = svn_relpath_join(*repos_relpath, - svn_dirent_skip_ancestor( - add_abspath, - local_abspath), - result_pool); - } - else - { - *repos_relpath = NULL; - *repos_root_url = NULL; - *repos_uuid = NULL; - } return SVN_NO_ERROR; } static svn_error_t * -internal_status(svn_wc_status3_t **status, +internal_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t check_working_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -350,12 +329,13 @@ internal_status(svn_wc_status3_t **status, *STATUS will be set to NULL. If GET_ALL is non-zero, then *STATUS will be allocated and returned no matter what. If IGNORE_TEXT_MODS is TRUE then don't check for text mods, assume there are none and set and *STATUS - returned to reflect that assumption. + returned to reflect that assumption. If CHECK_WORKING_COPY is FALSE, + do not adjust the result for missing working copy files. The status struct's repos_lock field will be set to REPOS_LOCK. */ static svn_error_t * -assemble_status(svn_wc_status3_t **status, +assemble_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, const char *parent_repos_root_url, @@ -365,18 +345,17 @@ assemble_status(svn_wc_status3_t **status, const svn_io_dirent2_t *dirent, svn_boolean_t get_all, svn_boolean_t ignore_text_mods, + svn_boolean_t check_working_copy, const svn_lock_t *repos_lock, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__internal_status_t *inner_stat; svn_wc_status3_t *stat; svn_boolean_t switched_p = FALSE; svn_boolean_t copied = FALSE; svn_boolean_t conflicted; const char *moved_from_abspath = NULL; - svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file)) - ? dirent->filesize - : SVN_INVALID_FILESIZE; /* Defaults for two main variables. */ enum svn_wc_status_kind node_status = svn_wc_status_normal; @@ -384,10 +363,6 @@ assemble_status(svn_wc_status3_t **status, enum svn_wc_status_kind prop_status = svn_wc_status_none; - if (!info) - SVN_ERR(svn_wc__db_read_single_info(&info, db, local_abspath, - result_pool, scratch_pool)); - if (!info->repos_relpath || !parent_repos_relpath) switched_p = FALSE; else @@ -426,7 +401,7 @@ assemble_status(svn_wc_status3_t **status, copied = TRUE; /* Working deletion */ } } - else + else if (check_working_copy) { /* Examine whether our target is missing or obstructed. To detect * obstructions, we have to look at the on-disk status in DIRENT. */ @@ -606,19 +581,21 @@ assemble_status(svn_wc_status3_t **status, && prop_status != svn_wc_status_none) node_status = prop_status; - /* 5. Easy out: unless we're fetching -every- entry, don't bother - to allocate a struct for an uninteresting entry. */ + /* 5. Easy out: unless we're fetching -every- node, don't bother + to allocate a struct for an uninteresting node. + This filter should match the filter in is_sendable_status() */ if (! get_all) if (((node_status == svn_wc_status_none) || (node_status == svn_wc_status_normal)) && (! switched_p) - && (! info->locked ) + && (! info->locked) && (! info->lock) && (! repos_lock) && (! info->changelist) - && (! conflicted)) + && (! conflicted) + && (! info->moved_to)) { *status = NULL; return SVN_NO_ERROR; @@ -626,7 +603,9 @@ assemble_status(svn_wc_status3_t **status, /* 6. Build and return a status structure. */ - stat = apr_pcalloc(result_pool, sizeof(**status)); + inner_stat = apr_pcalloc(result_pool, sizeof(*inner_stat)); + stat = &inner_stat->s; + inner_stat->has_descendants = info->has_descendants; switch (info->kind) { @@ -642,7 +621,22 @@ assemble_status(svn_wc_status3_t **status, stat->kind = svn_node_unknown; } stat->depth = info->depth; - stat->filesize = filesize; + + if (dirent) + { + stat->filesize = (dirent->kind == svn_node_file) + ? dirent->filesize + : SVN_INVALID_FILESIZE; + stat->actual_kind = dirent->special ? svn_node_symlink + : dirent->kind; + } + else + { + stat->filesize = SVN_INVALID_FILESIZE; + stat->actual_kind = ignore_text_mods ? svn_node_unknown + : svn_node_none; + } + stat->node_status = node_status; stat->text_status = text_status; stat->prop_status = prop_status; @@ -700,7 +694,7 @@ assemble_status(svn_wc_status3_t **status, stat->file_external = info->file_external; - *status = stat; + *status = inner_stat; return SVN_NO_ERROR; } @@ -714,7 +708,7 @@ assemble_status(svn_wc_status3_t **status, node_status to svn_wc_status_unversioned. */ static svn_error_t * -assemble_unversioned(svn_wc_status3_t **status, +assemble_unversioned(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, const svn_io_dirent2_t *dirent, @@ -723,17 +717,30 @@ assemble_unversioned(svn_wc_status3_t **status, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__internal_status_t *inner_status; svn_wc_status3_t *stat; /* return a fairly blank structure. */ - stat = apr_pcalloc(result_pool, sizeof(*stat)); + inner_status = apr_pcalloc(result_pool, sizeof(*inner_status)); + stat = &inner_status->s; /*stat->versioned = FALSE;*/ stat->kind = svn_node_unknown; /* not versioned */ stat->depth = svn_depth_unknown; - stat->filesize = (dirent && dirent->kind == svn_node_file) - ? dirent->filesize - : SVN_INVALID_FILESIZE; + if (dirent) + { + stat->actual_kind = dirent->special ? svn_node_symlink + : dirent->kind; + stat->filesize = (dirent->kind == svn_node_file) + ? dirent->filesize + : SVN_INVALID_FILESIZE; + } + else + { + stat->actual_kind = svn_node_none; + stat->filesize = SVN_INVALID_FILESIZE; + } + stat->node_status = svn_wc_status_none; stat->text_status = svn_wc_status_none; stat->prop_status = svn_wc_status_none; @@ -770,7 +777,7 @@ assemble_unversioned(svn_wc_status3_t **status, stat->conflicted = tree_conflicted; stat->changelist = NULL; - *status = stat; + *status = inner_status; return SVN_NO_ERROR; } @@ -791,7 +798,7 @@ send_status_structure(const struct walk_status_baton *wb, void *status_baton, apr_pool_t *scratch_pool) { - svn_wc_status3_t *statstruct; + svn_wc__internal_status_t *statstruct; const svn_lock_t *repos_lock = NULL; /* Check for a repository lock. */ @@ -819,12 +826,14 @@ send_status_structure(const struct walk_status_baton *wb, SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath, parent_repos_root_url, parent_repos_relpath, parent_repos_uuid, - info, dirent, get_all, wb->ignore_text_mods, + info, dirent, get_all, + wb->ignore_text_mods, wb->check_working_copy, repos_lock, scratch_pool, scratch_pool)); if (statstruct && status_func) return svn_error_trace((*status_func)(status_baton, local_abspath, - statstruct, scratch_pool)); + &statstruct->s, + scratch_pool)); return SVN_NO_ERROR; } @@ -940,7 +949,7 @@ is_external_path(apr_hash_t *externals, hi; hi = apr_hash_next(hi)) { - const char *external_abspath = svn__apr_hash_index_key(hi); + const char *external_abspath = apr_hash_this_key(hi); if (svn_dirent_is_child(local_abspath, external_abspath, NULL)) return TRUE; @@ -980,7 +989,7 @@ send_unversioned_item(const struct walk_status_baton *wb, { svn_boolean_t is_ignored; svn_boolean_t is_external; - svn_wc_status3_t *status; + svn_wc__internal_status_t *status; const char *base_name = svn_dirent_basename(local_abspath, NULL); is_ignored = svn_wc_match_ignore_list(base_name, patterns, scratch_pool); @@ -992,12 +1001,12 @@ send_unversioned_item(const struct walk_status_baton *wb, is_external = is_external_path(wb->externals, local_abspath, scratch_pool); if (is_external) - status->node_status = svn_wc_status_external; + status->s.node_status = svn_wc_status_external; /* We can have a tree conflict on an unversioned path, i.e. an incoming * delete on a locally deleted path during an update. Don't ever ignore * those! */ - if (status->conflicted) + if (status->s.conflicted) is_ignored = FALSE; /* If we aren't ignoring it, or if it's an externals path, pass this @@ -1006,7 +1015,7 @@ send_unversioned_item(const struct walk_status_baton *wb, || !is_ignored || is_external) return svn_error_trace((*status_func)(status_baton, local_abspath, - status, scratch_pool)); + &status->s, scratch_pool)); return SVN_NO_ERROR; } @@ -1109,7 +1118,7 @@ one_child_status(const struct walk_status_baton *wb, /* Descend in subdirectories. */ if (depth == svn_depth_infinity - && info->kind == svn_node_dir) + && info->has_descendants /* is dir, or was dir and tc descendants */) { SVN_ERR(get_dir_status(wb, local_abspath, TRUE, dir_repos_root_url, dir_repos_relpath, @@ -1132,11 +1141,16 @@ one_child_status(const struct walk_status_baton *wb, * look up the kinds in the conflict ... just show all. */ if (! conflicted) { - /* Selected node, but not found */ - if (dirent == NULL) - return SVN_NO_ERROR; + /* We have a node, but its not visible in the WC. It can be a marker + node (not present, (server) excluded), *or* it can be the explictly + passed target of the status walk operation that doesn't exist. - if (depth == svn_depth_files && dirent->kind == svn_node_dir) + We only report the node when the caller explicitly as + */ + if (dirent == NULL && strcmp(wb->target_abspath, local_abspath) != 0) + return SVN_NO_ERROR; /* Marker node */ + + if (depth == svn_depth_files && dirent && dirent->kind == svn_node_dir) return SVN_NO_ERROR; if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), @@ -1228,21 +1242,28 @@ get_dir_status(const struct walk_status_baton *wb, iterpool = svn_pool_create(scratch_pool); - err = svn_io_get_dirents3(&dirents, local_abspath, FALSE, scratch_pool, - iterpool); - if (err - && (APR_STATUS_IS_ENOENT(err->apr_err) - || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + if (wb->check_working_copy) { - svn_error_clear(err); - dirents = apr_hash_make(scratch_pool); + err = svn_io_get_dirents3(&dirents, local_abspath, + wb->ignore_text_mods /* only_check_type*/, + scratch_pool, iterpool); + if (err + && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + dirents = apr_hash_make(scratch_pool); + } + else + SVN_ERR(err); } else - SVN_ERR(err); + dirents = apr_hash_make(scratch_pool); if (!dir_info) - SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath, - scratch_pool, iterpool)); + SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath, + !wb->check_working_copy, + scratch_pool, iterpool)); SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, &dir_repos_uuid, dir_info, @@ -1256,6 +1277,7 @@ get_dir_status(const struct walk_status_baton *wb, hash are subsequently used. */ SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, wb->db, local_abspath, + !wb->check_working_copy, scratch_pool, iterpool)); all_children = apr_hash_overlay(scratch_pool, nodes, dirents); @@ -1404,6 +1426,7 @@ get_child_status(const struct walk_status_baton *wb, SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, parent_abspath, + !wb->check_working_copy, scratch_pool, scratch_pool)); SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, @@ -1455,9 +1478,15 @@ hash_stash(void *baton, { apr_hash_t *stat_hash = baton; apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash); + void *new_status = svn_wc_dup_status3(status, hash_pool); + const svn_wc__internal_status_t *old_status = (const void*)status; + + /* Copy the internal/private data. */ + svn_wc__internal_status_t *is = new_status; + is->has_descendants = old_status->has_descendants; + assert(! svn_hash_gets(stat_hash, path)); - svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), - svn_wc_dup_status3(status, hash_pool)); + svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), new_status); return SVN_NO_ERROR; } @@ -1497,6 +1526,7 @@ tweak_statushash(void *baton, void *this_dir_baton, svn_boolean_t is_dir_baton, svn_wc__db_t *db, + svn_boolean_t check_working_copy, const char *local_abspath, enum svn_wc_status_kind repos_node_status, enum svn_wc_status_kind repos_text_status, @@ -1521,6 +1551,7 @@ tweak_statushash(void *baton, /* If not, make it so. */ if (! statstruct) { + svn_wc__internal_status_t *i_stat; /* If this item isn't being added, then we're most likely dealing with a non-recursive (or at least partially non-recursive) working copy. Due to bugs in how the client @@ -1536,8 +1567,9 @@ tweak_statushash(void *baton, return SVN_NO_ERROR; /* Use the public API to get a statstruct, and put it into the hash. */ - SVN_ERR(internal_status(&statstruct, db, local_abspath, pool, - scratch_pool)); + SVN_ERR(internal_status(&i_stat, db, local_abspath, + check_working_copy, pool, scratch_pool)); + statstruct = &i_stat->s; statstruct->repos_lock = repos_lock; svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), statstruct); } @@ -1576,9 +1608,9 @@ tweak_statushash(void *baton, statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); statstruct->repos_root_url = - b->edit_baton->anchor_status->repos_root_url; + b->edit_baton->anchor_status->s.repos_root_url; statstruct->repos_uuid = - b->edit_baton->anchor_status->repos_uuid; + b->edit_baton->anchor_status->s.repos_uuid; } /* The last committed date, and author for deleted items @@ -1618,9 +1650,9 @@ tweak_statushash(void *baton, { statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); statstruct->repos_root_url = - b->edit_baton->anchor_status->repos_root_url; + b->edit_baton->anchor_status->s.repos_root_url; statstruct->repos_uuid = - b->edit_baton->anchor_status->repos_uuid; + b->edit_baton->anchor_status->s.repos_uuid; } statstruct->ood_kind = b->ood_kind; if (b->ood_changed_author) @@ -1636,7 +1668,7 @@ find_dir_repos_relpath(const struct dir_baton *db, apr_pool_t *pool) { /* If we have no name, we're the root, return the anchor URL. */ if (! db->name) - return db->edit_baton->anchor_status->repos_relpath; + return db->edit_baton->anchor_status->s.repos_relpath; else { const char *repos_relpath; @@ -1668,7 +1700,7 @@ make_dir_baton(void **dir_baton, struct edit_baton *eb = edit_baton; struct dir_baton *d; const char *local_abspath; - const svn_wc_status3_t *status_in_parent; + const svn_wc__internal_status_t *status_in_parent; apr_pool_t *dir_pool; if (parent_baton) @@ -1727,8 +1759,7 @@ make_dir_baton(void **dir_baton, status_in_parent = eb->anchor_status; if (status_in_parent - && status_in_parent->versioned - && (status_in_parent->kind == svn_node_dir) + && (status_in_parent->has_descendants) && (! d->excluded) && (d->depth == svn_depth_unknown || d->depth == svn_depth_infinity @@ -1740,9 +1771,9 @@ make_dir_baton(void **dir_baton, const apr_array_header_t *ignores = eb->ignores; SVN_ERR(get_dir_status(&eb->wb, local_abspath, TRUE, - status_in_parent->repos_root_url, + status_in_parent->s.repos_root_url, NULL /*parent_repos_relpath*/, - status_in_parent->repos_uuid, + status_in_parent->s.repos_uuid, NULL, NULL /* dirent */, ignores, d->depth == svn_depth_files @@ -1757,7 +1788,7 @@ make_dir_baton(void **dir_baton, this_dir_status = svn_hash_gets(d->statii, d->local_abspath); if (this_dir_status && this_dir_status->versioned && (d->depth == svn_depth_unknown - || d->depth > status_in_parent->depth)) + || d->depth > status_in_parent->s.depth)) { d->depth = this_dir_status->depth; } @@ -1799,12 +1830,15 @@ make_file_baton(struct dir_baton *parent_dir_baton, * Return a boolean answer to the question "Is @a status something that * should be reported?". @a no_ignore and @a get_all are the same as * svn_wc_get_status_editor4(). + * + * This implementation should match the filter in assemble_status() */ static svn_boolean_t -is_sendable_status(const svn_wc_status3_t *status, +is_sendable_status(const svn_wc__internal_status_t *i_status, svn_boolean_t no_ignore, svn_boolean_t get_all) { + const svn_wc_status3_t *status = &i_status->s; /* If the repository status was touched at all, it's interesting. */ if (status->repos_node_status != svn_wc_status_none) return TRUE; @@ -1830,8 +1864,8 @@ is_sendable_status(const svn_wc_status3_t *status, return TRUE; /* If the text, property or tree state is interesting, send it. */ - if ((status->node_status != svn_wc_status_none - && (status->node_status != svn_wc_status_normal))) + if ((status->node_status != svn_wc_status_none) + && (status->node_status != svn_wc_status_normal)) return TRUE; /* If it's switched, send it. */ @@ -1846,6 +1880,9 @@ is_sendable_status(const svn_wc_status3_t *status, if (status->changelist) return TRUE; + if (status->moved_to_abspath) + return TRUE; + /* Otherwise, don't send it. */ return FALSE; } @@ -1910,15 +1947,15 @@ handle_statii(struct edit_baton *eb, /* Loop over all the statii still in our hash, handling each one. */ for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi)) { - const char *local_abspath = svn__apr_hash_index_key(hi); - svn_wc_status3_t *status = svn__apr_hash_index_val(hi); + const char *local_abspath = apr_hash_this_key(hi); + svn_wc__internal_status_t *status = apr_hash_this_val(hi); /* Clear the subpool. */ svn_pool_clear(iterpool); /* Now, handle the status. We don't recurse for svn_depth_immediates because we already have the subdirectories' statii. */ - if (status->versioned && status->kind == svn_node_dir + if (status->has_descendants && (depth == svn_depth_unknown || depth == svn_depth_infinity)) { @@ -1934,9 +1971,9 @@ handle_statii(struct edit_baton *eb, iterpool)); } if (dir_was_deleted) - status->repos_node_status = svn_wc_status_deleted; + status->s.repos_node_status = svn_wc_status_deleted; if (is_sendable_status(status, eb->no_ignore, eb->get_all)) - SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status, + SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, &status->s, iterpool)); } @@ -1991,7 +2028,7 @@ delete_entry(const char *path, statushash immediately. No need to wait until close_file or close_dir, because there's no risk of having to honor the 'added' flag. We already know this item exists in the working copy. */ - SVN_ERR(tweak_statushash(db, db, TRUE, eb->db, + SVN_ERR(tweak_statushash(db, db, TRUE, eb->db, eb->wb.check_working_copy, local_abspath, svn_wc_status_deleted, 0, 0, revision, NULL, pool)); @@ -1999,7 +2036,8 @@ delete_entry(const char *path, is the root node and we're not supposed to report on the root node). */ if (db->parent_baton && (! *eb->target_basename)) - SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE,eb->db, + SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE, + eb->db, eb->wb.check_working_copy, db->local_abspath, svn_wc_status_modified, svn_wc_status_modified, 0, SVN_INVALID_REVNUM, NULL, pool)); @@ -2123,7 +2161,9 @@ close_directory(void *dir_baton, { /* ### When we add directory locking, we need to find a ### directory lock here. */ - SVN_ERR(tweak_statushash(pb, db, TRUE, eb->db, db->local_abspath, + SVN_ERR(tweak_statushash(pb, db, TRUE, + eb->db, eb->wb.check_working_copy, + db->local_abspath, repos_node_status, repos_text_status, repos_prop_status, SVN_INVALID_REVNUM, NULL, scratch_pool)); @@ -2133,17 +2173,17 @@ close_directory(void *dir_baton, /* We're editing the root dir of the WC. As its repos status info isn't otherwise set, set it directly to trigger invocation of the status callback below. */ - eb->anchor_status->repos_node_status = repos_node_status; - eb->anchor_status->repos_prop_status = repos_prop_status; - eb->anchor_status->repos_text_status = repos_text_status; + eb->anchor_status->s.repos_node_status = repos_node_status; + eb->anchor_status->s.repos_prop_status = repos_prop_status; + eb->anchor_status->s.repos_text_status = repos_text_status; /* If the root dir is out of date set the ood info directly too. */ - if (db->ood_changed_rev != eb->anchor_status->revision) + if (db->ood_changed_rev != eb->anchor_status->s.revision) { - eb->anchor_status->ood_changed_rev = db->ood_changed_rev; - eb->anchor_status->ood_changed_date = db->ood_changed_date; - eb->anchor_status->ood_kind = db->ood_kind; - eb->anchor_status->ood_changed_author = + eb->anchor_status->s.ood_changed_rev = db->ood_changed_rev; + eb->anchor_status->s.ood_changed_date = db->ood_changed_date; + eb->anchor_status->s.ood_kind = db->ood_kind; + eb->anchor_status->s.ood_changed_author = apr_pstrdup(pool, db->ood_changed_author); } } @@ -2154,25 +2194,25 @@ close_directory(void *dir_baton, if (pb && ! db->excluded) { svn_boolean_t was_deleted = FALSE; - const svn_wc_status3_t *dir_status; + svn_wc__internal_status_t *dir_status; /* See if the directory was deleted or replaced. */ dir_status = svn_hash_gets(pb->statii, db->local_abspath); if (dir_status && - ((dir_status->repos_node_status == svn_wc_status_deleted) - || (dir_status->repos_node_status == svn_wc_status_replaced))) + ((dir_status->s.repos_node_status == svn_wc_status_deleted) + || (dir_status->s.repos_node_status == svn_wc_status_replaced))) was_deleted = TRUE; /* Now do the status reporting. */ SVN_ERR(handle_statii(eb, - dir_status ? dir_status->repos_root_url : NULL, - dir_status ? dir_status->repos_relpath : NULL, - dir_status ? dir_status->repos_uuid : NULL, + dir_status ? dir_status->s.repos_root_url : NULL, + dir_status ? dir_status->s.repos_relpath : NULL, + dir_status ? dir_status->s.repos_uuid : NULL, db->statii, was_deleted, db->depth, scratch_pool)); if (dir_status && is_sendable_status(dir_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, - dir_status, scratch_pool)); + &dir_status->s, scratch_pool)); svn_hash_sets(pb->statii, db->local_abspath, NULL); } else if (! pb) @@ -2181,13 +2221,12 @@ close_directory(void *dir_baton, target, we should only report the target. */ if (*eb->target_basename) { - const svn_wc_status3_t *tgt_status; + const svn_wc__internal_status_t *tgt_status; tgt_status = svn_hash_gets(db->statii, eb->target_abspath); if (tgt_status) { - if (tgt_status->versioned - && tgt_status->kind == svn_node_dir) + if (tgt_status->has_descendants) { SVN_ERR(get_dir_status(&eb->wb, eb->target_abspath, TRUE, @@ -2202,7 +2241,7 @@ close_directory(void *dir_baton, } if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath, - tgt_status, scratch_pool)); + &tgt_status->s, scratch_pool)); } } else @@ -2211,15 +2250,15 @@ close_directory(void *dir_baton, Note that our directory couldn't have been deleted, because it is the root of the edit drive. */ SVN_ERR(handle_statii(eb, - eb->anchor_status->repos_root_url, - eb->anchor_status->repos_relpath, - eb->anchor_status->repos_uuid, + eb->anchor_status->s.repos_root_url, + eb->anchor_status->s.repos_relpath, + eb->anchor_status->s.repos_uuid, db->statii, FALSE, eb->default_depth, scratch_pool)); if (is_sendable_status(eb->anchor_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, - eb->anchor_status, scratch_pool)); + &eb->anchor_status->s, scratch_pool)); eb->anchor_status = NULL; } } @@ -2375,6 +2414,7 @@ close_file(void *file_baton, } return tweak_statushash(fb, NULL, FALSE, fb->edit_baton->db, + fb->edit_baton->wb.check_working_copy, fb->local_abspath, repos_node_status, repos_text_status, repos_prop_status, SVN_INVALID_REVNUM, repos_lock, pool); @@ -2393,18 +2433,18 @@ close_edit(void *edit_baton, if (eb->root_opened) return SVN_NO_ERROR; - SVN_ERR(svn_wc_walk_status(eb->wc_ctx, - eb->target_abspath, - eb->default_depth, - eb->get_all, - eb->no_ignore, - FALSE, - eb->ignores, - eb->status_func, - eb->status_baton, - eb->cancel_func, - eb->cancel_baton, - pool)); + SVN_ERR(svn_wc__internal_walk_status(eb->db, + eb->target_abspath, + eb->default_depth, + eb->get_all, + eb->no_ignore, + FALSE, + eb->ignores, + eb->status_func, + eb->status_baton, + eb->cancel_func, + eb->cancel_baton, + pool)); return SVN_NO_ERROR; } @@ -2423,6 +2463,7 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, const char *target_basename, svn_depth_t depth, svn_boolean_t get_all, + svn_boolean_t check_working_copy, svn_boolean_t no_ignore, svn_boolean_t depth_as_sticky, svn_boolean_t server_performs_filtering, @@ -2447,7 +2488,6 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, eb->default_depth = depth; eb->target_revision = edit_revision; eb->db = wc_ctx->db; - eb->wc_ctx = wc_ctx; eb->get_all = get_all; eb->no_ignore = no_ignore; eb->status_func = status_func; @@ -2463,7 +2503,8 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, eb->wb.db = wc_ctx->db; eb->wb.target_abspath = eb->target_abspath; - eb->wb.ignore_text_mods = FALSE; + eb->wb.ignore_text_mods = !check_working_copy; + eb->wb.check_working_copy = check_working_copy; eb->wb.repos_locks = NULL; eb->wb.repos_root = NULL; @@ -2488,7 +2529,7 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, /* The edit baton's status structure maps to PATH, and the editor have to be aware of whether that is the anchor or the target. */ SVN_ERR(internal_status(&(eb->anchor_status), wc_ctx->db, anchor_abspath, - result_pool, scratch_pool)); + check_working_copy, result_pool, scratch_pool)); /* Construct an editor. */ tree_editor->set_target_revision = set_target_revision; @@ -2594,6 +2635,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, wb.db = db; wb.target_abspath = local_abspath; wb.ignore_text_mods = ignore_text_mods; + wb.check_working_copy = TRUE; wb.repos_root = NULL; wb.repos_locks = NULL; @@ -2608,6 +2650,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, } err = svn_wc__db_read_single_info(&info, db, local_abspath, + FALSE /* base_tree_only */, scratch_pool, scratch_pool); if (err) @@ -2636,7 +2679,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, } if (info - && info->kind == svn_node_dir + && info->has_descendants /* is dir, or was dir and has tc descendants */ && info->status != svn_wc__db_status_not_present && info->status != svn_wc__db_status_excluded && info->status != svn_wc__db_status_server_excluded) @@ -2756,30 +2799,26 @@ svn_wc_get_default_ignores(apr_array_header_t **patterns, /* */ static svn_error_t * -internal_status(svn_wc_status3_t **status, +internal_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t check_working_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const svn_io_dirent2_t *dirent; - svn_node_kind_t node_kind; + const svn_io_dirent2_t *dirent = NULL; const char *parent_repos_relpath; const char *parent_repos_root_url; const char *parent_repos_uuid; - svn_wc__db_status_t node_status; - svn_boolean_t conflicted; + const struct svn_wc__db_info_t *info; svn_boolean_t is_root = FALSE; svn_error_t *err; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - err = svn_wc__db_read_info(&node_status, &node_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, local_abspath, - scratch_pool, scratch_pool); + err = svn_wc__db_read_single_info(&info, db, local_abspath, + !check_working_copy, + scratch_pool, scratch_pool); if (err) { @@ -2787,30 +2826,25 @@ internal_status(svn_wc_status3_t **status, return svn_error_trace(err); svn_error_clear(err); - node_kind = svn_node_unknown; - /* Ensure conflicted is always set, but don't hide tree conflicts - on 'hidden' nodes. */ - conflicted = FALSE; + info = NULL; - SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, - scratch_pool, scratch_pool)); + if (check_working_copy) + SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, + scratch_pool, scratch_pool)); } - else + else if (check_working_copy) SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath, scratch_pool, scratch_pool)); - if (node_kind != svn_node_unknown - && (node_status == svn_wc__db_status_not_present - || node_status == svn_wc__db_status_server_excluded - || node_status == svn_wc__db_status_excluded)) - { - node_kind = svn_node_unknown; - } - - if (node_kind == svn_node_unknown) + if (!info + || info->kind == svn_node_unknown + || info->status == svn_wc__db_status_not_present + || info->status == svn_wc__db_status_server_excluded + || info->status == svn_wc__db_status_excluded) return svn_error_trace(assemble_unversioned(status, db, local_abspath, - dirent, conflicted, + dirent, + info ? info->conflicted : FALSE, FALSE /* is_ignored */, result_pool, scratch_pool)); @@ -2819,30 +2853,31 @@ internal_status(svn_wc_status3_t **status, else SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool)); + /* Even though passing parent_repos_* is not required, assemble_status needs + these values to determine if a node is switched */ if (!is_root) { - svn_wc__db_status_t parent_status; - const char *parent_abspath = svn_dirent_dirname(local_abspath, - scratch_pool); - - err = svn_wc__db_read_info(&parent_status, NULL, NULL, - &parent_repos_relpath, &parent_repos_root_url, - &parent_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 && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND - || SVN_WC__ERR_IS_NOT_CURRENT_WC(err))) - { - svn_error_clear(err); - parent_repos_root_url = NULL; - parent_repos_relpath = NULL; - parent_repos_uuid = NULL; - } - else SVN_ERR(err); + const char *const parent_abspath = svn_dirent_dirname(local_abspath, + scratch_pool); + if (check_working_copy) + SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, + &parent_repos_relpath, + &parent_repos_root_url, + &parent_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)); + else + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, + &parent_repos_relpath, + &parent_repos_root_url, + &parent_repos_uuid, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + db, parent_abspath, + result_pool, scratch_pool)); } else { @@ -2855,10 +2890,10 @@ internal_status(svn_wc_status3_t **status, parent_repos_root_url, parent_repos_relpath, parent_repos_uuid, - NULL, + info, dirent, TRUE /* get_all */, - FALSE, + FALSE, check_working_copy, NULL /* repos_lock */, result_pool, scratch_pool)); } @@ -2871,16 +2906,21 @@ svn_wc_status3(svn_wc_status3_t **status, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_error_trace( - internal_status(status, wc_ctx->db, local_abspath, result_pool, - scratch_pool)); + svn_wc__internal_status_t *stat; + SVN_ERR(internal_status(&stat, wc_ctx->db, local_abspath, + TRUE /* check_working_copy */, + result_pool, scratch_pool)); + *status = &stat->s; + return SVN_NO_ERROR; } svn_wc_status3_t * svn_wc_dup_status3(const svn_wc_status3_t *orig_stat, apr_pool_t *pool) { - svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat)); + /* Allocate slightly more room */ + svn_wc__internal_status_t *new_istat = apr_palloc(pool, sizeof(*new_istat)); + svn_wc_status3_t *new_stat = &new_istat->s; /* Shallow copy all members. */ *new_stat = *orig_stat; diff --git a/contrib/subversion/subversion/libsvn_wc/token-map.h b/contrib/subversion/subversion/libsvn_wc/token-map.h index 9da12b8aa..9bf80d6ac 100644 --- a/contrib/subversion/subversion/libsvn_wc/token-map.h +++ b/contrib/subversion/subversion/libsvn_wc/token-map.h @@ -33,6 +33,7 @@ extern "C" { #endif +/* The kind values used on NODES */ static const svn_token_map_t kind_map[] = { { "file", svn_node_file }, /* MAP_FILE */ { "dir", svn_node_dir }, /* MAP_DIR */ @@ -41,6 +42,16 @@ static const svn_token_map_t kind_map[] = { { NULL } }; +/* Like kind_map, but also supports 'none' */ +static const svn_token_map_t kind_map_none[] = { + { "none", svn_node_none }, + { "file", svn_node_file }, + { "dir", svn_node_dir }, + { "symlink", svn_node_symlink }, + { "unknown", svn_node_unknown }, + { NULL } +}; + /* Note: we only decode presence values from the database. These are a subset of all the status values. */ static const svn_token_map_t presence_map[] = { diff --git a/contrib/subversion/subversion/libsvn_wc/translate.c b/contrib/subversion/subversion/libsvn_wc/translate.c index 9e0b26568..0e16235d7 100644 --- a/contrib/subversion/subversion/libsvn_wc/translate.c +++ b/contrib/subversion/subversion/libsvn_wc/translate.c @@ -30,6 +30,7 @@ #include #include +#include "svn_private_config.h" #include "svn_types.h" #include "svn_string.h" #include "svn_dirent_uri.h" @@ -45,25 +46,9 @@ #include "translate.h" #include "props.h" -#include "svn_private_config.h" #include "private/svn_wc_private.h" - -/* */ -static svn_error_t * -read_handler_unsupported(void *baton, char *buffer, apr_size_t *len) -{ - SVN_ERR_MALFUNCTION(); -} - -/* */ -static svn_error_t * -write_handler_unsupported(void *baton, const char *buffer, apr_size_t *len) -{ - SVN_ERR_MALFUNCTION(); -} - svn_error_t * svn_wc__internal_translated_stream(svn_stream_t **stream, svn_wc__db_t *db, @@ -133,16 +118,18 @@ svn_wc__internal_translated_stream(svn_stream_t **stream, FALSE /* expand */, result_pool); - /* Enforce our contract. TO_NF streams are readonly */ - svn_stream_set_write(*stream, write_handler_unsupported); + /* streams enforce our contract that TO_NF streams are read-only + * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to + * write to them. */ } else { *stream = svn_subst_stream_translated(*stream, eol, TRUE, keywords, TRUE, result_pool); - /* Enforce our contract. FROM_NF streams are write-only */ - svn_stream_set_read(*stream, read_handler_unsupported); + /* streams enforce our contract that FROM_NF streams are write-only + * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to + * read them. */ } } @@ -329,12 +316,15 @@ svn_wc__expand_keywords(apr_hash_t **keywords, db, local_abspath, scratch_pool, scratch_pool)); - if (repos_relpath) - url = svn_path_url_add_component2(repos_root_url, repos_relpath, - scratch_pool); - else - SVN_ERR(svn_wc__db_read_url(&url, db, local_abspath, scratch_pool, - scratch_pool)); + /* Handle special statuses (e.g. added) */ + if (!repos_relpath) + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &repos_root_url, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + url = svn_path_url_add_component2(repos_root_url, repos_relpath, + scratch_pool); } else { diff --git a/contrib/subversion/subversion/libsvn_wc/tree_conflicts.c b/contrib/subversion/subversion/libsvn_wc/tree_conflicts.c index 4445c96ec..caf39eda9 100644 --- a/contrib/subversion/subversion/libsvn_wc/tree_conflicts.c +++ b/contrib/subversion/subversion/libsvn_wc/tree_conflicts.c @@ -46,6 +46,7 @@ static const svn_token_map_t node_kind_map[] = { "file", svn_node_file }, { "dir", svn_node_dir }, { "", svn_node_unknown }, + /* ### should also map svn_node_symlink */ { NULL } }; @@ -353,13 +354,13 @@ svn_wc__serialize_conflict(svn_skel_t **skel, else SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool)); - /* reason */ - skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, conflict->reason, - result_pool); + /* local change */ + skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, + conflict->reason, result_pool); - /* action */ - skel_prepend_enum(c_skel, svn_wc__conflict_action_map, conflict->action, - result_pool); + /* incoming change */ + skel_prepend_enum(c_skel, svn_wc__conflict_action_map, + conflict->action, result_pool); /* operation */ skel_prepend_enum(c_skel, svn_wc__operation_map, conflict->operation, @@ -367,8 +368,10 @@ svn_wc__serialize_conflict(svn_skel_t **skel, /* node_kind */ SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir - || conflict->node_kind == svn_node_file); - skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, result_pool); + || conflict->node_kind == svn_node_file + || conflict->node_kind == svn_node_none); + skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, + result_pool); /* Victim path (escaping separator chars). */ victim_basename = svn_dirent_basename(conflict->local_abspath, result_pool); @@ -409,10 +412,9 @@ svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx, svn_error_t *err; SVN_ERR_ASSERT(conflict != NULL); - SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge - || (conflict->reason != svn_wc_conflict_reason_moved_away - && conflict->reason != svn_wc_conflict_reason_moved_here) - ); + SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge || + (conflict->reason != svn_wc_conflict_reason_moved_away && + conflict->reason != svn_wc_conflict_reason_moved_here)); /* Re-adding an existing tree conflict victim is an error. */ err = svn_wc__internal_conflicted_p(NULL, NULL, &existing_conflict, @@ -443,7 +445,7 @@ svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx, NULL, scratch_pool, scratch_pool)); - switch(conflict->operation) + switch (conflict->operation) { case svn_wc_operation_update: default: @@ -483,8 +485,10 @@ svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict, int i; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__read_conflicts(&conflicts, - wc_ctx->db, local_abspath, FALSE, + SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, + wc_ctx->db, local_abspath, + FALSE /* temp files */, + TRUE /* only tree conflicts */, scratch_pool, scratch_pool)); if (!conflicts || conflicts->nelts == 0) @@ -501,8 +505,7 @@ svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict, if (desc->kind == svn_wc_conflict_kind_tree) { - *tree_conflict = svn_wc__conflict_description2_dup(desc, - result_pool); + *tree_conflict = svn_wc_conflict_description2_dup(desc, result_pool); return SVN_NO_ERROR; } } diff --git a/contrib/subversion/subversion/libsvn_wc/update_editor.c b/contrib/subversion/subversion/libsvn_wc/update_editor.c index fd3e9ca92..5f4d64826 100644 --- a/contrib/subversion/subversion/libsvn_wc/update_editor.c +++ b/contrib/subversion/subversion/libsvn_wc/update_editor.c @@ -211,7 +211,7 @@ struct edit_baton /* If this is a 'switch' operation, the new relpath of target_abspath, else NULL. */ - const char *switch_relpath; + const char *switch_repos_relpath; /* The URL to the root of the repository. */ const char *repos_root; @@ -258,6 +258,9 @@ struct edit_baton /* Absolute path of the working copy root or NULL if not initialized yet */ const char *wcroot_abspath; + /* After closing the root directory a copy of its edited value */ + svn_boolean_t edited; + apr_pool_t *pool; }; @@ -295,7 +298,7 @@ struct dir_baton const char *local_abspath; /* The repository relative path this directory will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the directory before updating */ svn_revnum_t old_revision; @@ -342,9 +345,10 @@ struct dir_baton 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; + /* A hash of file names (only the hash key matters) seen by add_file and + add_directory and not yet added to the database, mapping to a const + char * node kind (via svn_node_kind_to_word(). */ + apr_hash_t *not_present_nodes; /* Set if an unversioned dir of the same name already existed in this directory. */ @@ -398,7 +402,7 @@ struct handler_baton struct file_baton *fb; /* Where we are assembling the new file. */ - const char *new_text_base_tmp_abspath; + svn_wc__db_install_data_t *install_data; /* 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 @@ -412,7 +416,7 @@ struct handler_baton 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 + /* The calculated checksum of the text source or NULL if the actual checksum is not being calculated. The checksum kind is identical to the kind of expected_source_checksum. */ svn_checksum_t *actual_source_checksum; @@ -486,47 +490,20 @@ cleanup_edit_baton(void *edit_baton) 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. */ +/* Calculate the new repos_relpath for a directory or file */ 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) +calculate_repos_relpath(const char **new_repos_relpath, + const char *local_abspath, + const char *old_repos_relpath, + struct edit_baton *eb, + struct dir_baton *pb, + apr_pool_t *result_pool, + 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; - } + const char *name = svn_dirent_basename(local_abspath, NULL); - /* Figure out the new_relpath for this directory. */ - if (eb->switch_relpath) + /* Figure out the new_repos_relpath for this directory. */ + if (eb->switch_repos_relpath) { /* Handle switches... */ @@ -535,18 +512,16 @@ make_dir_baton(struct dir_baton **d_p, 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; + the target of the switch. Thus, our new_repos_relpath will be + the switch_repos_relpath. */ + *new_repos_relpath = eb->switch_repos_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)); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } else @@ -554,36 +529,76 @@ make_dir_baton(struct dir_baton **d_p, /* 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. + new_repos_relpath to the switch_repos_relpath. + + Otherwise, we simply extend new_repos_relpath from the parent. */ - 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; + && strcmp(eb->target_basename, name) == 0) + *new_repos_relpath = eb->switch_repos_relpath; else - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_pool); } } else /* must be an update */ { /* If we are adding the node, then simply extend the parent's relpath for our own. */ - if (adding) + if (old_repos_relpath == NULL) { SVN_ERR_ASSERT(pb != NULL); - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_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); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } + return SVN_NO_ERROR; +} + +/* 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; + } + d->edit_baton = eb; d->parent_baton = pb; d->pool = dir_pool; @@ -594,7 +609,7 @@ make_dir_baton(struct dir_baton **d_p, 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); + d->not_present_nodes = apr_hash_make(dir_pool); /* Copy some flags from the parent baton */ if (pb) @@ -614,7 +629,6 @@ make_dir_baton(struct dir_baton **d_p, return SVN_NO_ERROR; } - /* Forward declarations. */ static svn_error_t * already_in_a_tree_conflict(svn_boolean_t *conflicted, @@ -680,7 +694,7 @@ struct file_baton const char *local_abspath; /* The repository relative path this file will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the file before updating */ svn_revnum_t old_revision; @@ -764,7 +778,6 @@ make_file_baton(struct file_baton **f_p, 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)); @@ -776,37 +789,6 @@ make_file_baton(struct file_baton **f_p, 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)); @@ -843,13 +825,16 @@ complete_conflict(svn_skel_t *conflict, const char *new_repos_relpath, svn_node_kind_t local_kind, svn_node_kind_t target_kind, + const svn_skel_t *delete_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_wc_conflict_version_t *original_version; + const svn_wc_conflict_version_t *original_version = NULL; svn_wc_conflict_version_t *target_version; svn_boolean_t is_complete; + SVN_ERR_ASSERT(new_repos_relpath); + if (!conflict) return SVN_NO_ERROR; /* Not conflicted */ @@ -865,20 +850,30 @@ complete_conflict(svn_skel_t *conflict, old_revision, local_kind, result_pool); - else - original_version = NULL; + else if (delete_conflict) + { + const apr_array_header_t *locations; - 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; + SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL, + eb->db, local_abspath, + delete_conflict, + scratch_pool, scratch_pool)); + + if (locations) + { + original_version = APR_ARRAY_IDX(locations, 0, + const svn_wc_conflict_version_t *); + } + } - if (eb->switch_relpath) + target_version = svn_wc_conflict_version_create2(eb->repos_root, + eb->repos_uuid, + new_repos_relpath, + *eb->target_revision, + target_kind, + result_pool); + + if (eb->switch_repos_relpath) SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, original_version, target_version, @@ -913,8 +908,9 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, db->local_abspath, @@ -924,7 +920,6 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *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; @@ -948,8 +943,9 @@ mark_file_edited(struct file_baton *fb, apr_pool_t *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, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, + NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, @@ -974,7 +970,6 @@ 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. */ @@ -1014,10 +1009,10 @@ window_handler(svn_txdelta_window_t *window, void *baton) { /* 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) + if (hb->install_data) { - svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, - TRUE, hb->pool)); + svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data, + hb->pool)); } } else @@ -1031,7 +1026,7 @@ window_handler(svn_txdelta_window_t *window, void *baton) /* 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, + SVN_ERR(svn_wc__db_pristine_install(hb->install_data, fb->new_text_base_sha1_checksum, fb->new_text_base_md5_checksum, hb->pool)); @@ -1154,17 +1149,6 @@ set_target_revision(void *edit_baton, 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, @@ -1228,10 +1212,27 @@ open_root(void *edit_baton, eb->db, db->local_abspath, db->pool, pool)); - if (conflict_ignored) + if (have_work) { - db->shadowed = TRUE; + 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)); } + else + base_status = status; + + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, NULL, + db->pool, pool)); + + if (conflict_ignored) + db->shadowed = TRUE; else if (have_work) { const char *move_src_root_abspath; @@ -1239,16 +1240,6 @@ open_root(void *edit_baton, 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) { @@ -1271,9 +1262,10 @@ open_root(void *edit_baton, SVN_ERR(complete_conflict(tree_conflict, eb, move_src_root_abspath, db->old_repos_relpath, - db->old_revision, db->new_relpath, + db->old_revision, + db->new_repos_relpath, svn_node_dir, svn_node_dir, - pool, pool)); + NULL, pool, pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, move_src_root_abspath, tree_conflict, @@ -1288,8 +1280,6 @@ open_root(void *edit_baton, 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') { @@ -1308,7 +1298,7 @@ open_root(void *edit_baton, SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, *eb->target_revision, pool)); } @@ -1320,99 +1310,6 @@ open_root(void *edit_baton, /* ===================================================================== */ /* 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) @@ -1447,7 +1344,6 @@ check_tree_conflict(svn_skel_t **pconflict, { 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; @@ -1486,15 +1382,15 @@ check_tree_conflict(svn_skel_t **pconflict, } 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; + /* The node is locally replaced but could also be moved-away, + but we can't report that it is moved away and replaced. + + And we wouldn't be able to store that each of a dozen + descendants was moved to other locations... + + Replaced is what actually happened... */ + + reason = svn_wc_conflict_reason_replaced; } break; @@ -1557,14 +1453,14 @@ check_tree_conflict(svn_skel_t **pconflict, * 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, + SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, + eb->db, local_abspath, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); if (modified) { - if (all_mods_are_deletes) + if (working_status == svn_wc__db_status_deleted) reason = svn_wc_conflict_reason_deleted; else reason = svn_wc_conflict_reason_edited; @@ -1711,7 +1607,8 @@ delete_entry(const char *path, const char *base = svn_relpath_basename(path, NULL); const char *local_abspath; const char *repos_relpath; - svn_node_kind_t kind, base_kind; + const char *deleted_repos_relpath; + svn_node_kind_t kind; svn_revnum_t old_revision; svn_boolean_t conflicted; svn_boolean_t have_work; @@ -1721,8 +1618,6 @@ delete_entry(const char *path, 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; @@ -1740,6 +1635,7 @@ delete_entry(const char *path, { svn_boolean_t is_root; + SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, scratch_pool)); @@ -1767,10 +1663,9 @@ delete_entry(const char *path, if (!have_work) { base_status = status; - base_kind = kind; } else - SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision, + SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision, &repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1815,11 +1710,9 @@ delete_entry(const char *path, || 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 */, + SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE, + deleting_target, FALSE, + *eb->target_revision, NULL, NULL, scratch_pool)); @@ -1842,18 +1735,13 @@ delete_entry(const char *path, { SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, status, TRUE, - (kind == svn_node_dir) - ? svn_node_dir - : svn_node_file, + kind, 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 @@ -1864,45 +1752,19 @@ delete_entry(const char *path, 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 */ + /* Whatever the kind of conflict, we can just clear BASE + by turning whatever is there into a copy */ } + /* Calculate the repository-relative path of the entry which was + * deleted. For updates it's the same as REPOS_RELPATH but for + * switches it is within the switch target. */ + SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath, + repos_relpath, eb, pb, scratch_pool, + scratch_pool)); 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, + old_revision, deleted_repos_relpath, + kind, svn_node_none, NULL, pb->pool, scratch_pool)); /* Issue a wq operation to delete the BASE_NODE data and to delete actual @@ -1917,7 +1779,8 @@ delete_entry(const char *path, { /* 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, + (tree_conflict != NULL), + FALSE, FALSE, SVN_INVALID_REVNUM /* not_present_rev */, tree_conflict, NULL, scratch_pool)); @@ -1926,7 +1789,8 @@ delete_entry(const char *path, { /* Delete, leaving a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, FALSE, + (tree_conflict != NULL), + TRUE, FALSE, *eb->target_revision, tree_conflict, NULL, scratch_pool)); @@ -1948,6 +1812,7 @@ delete_entry(const char *path, { if (eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, tree_conflict, NULL /* merge_options */, eb->conflict_func, @@ -1955,23 +1820,17 @@ delete_entry(const char *path, eb->cancel_func, eb->cancel_baton, scratch_pool)); - do_notification(eb, local_abspath, svn_node_unknown, + do_notification(eb, local_abspath, kind, 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); + do_notification(eb, local_abspath, kind, action, scratch_pool); } svn_pool_destroy(scratch_pool); @@ -1991,6 +1850,7 @@ add_directory(const char *path, struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *db; + apr_pool_t *scratch_pool = svn_pool_create(pool); svn_node_kind_t kind; svn_wc__db_status_t status; svn_node_kind_t wc_kind; @@ -2008,6 +1868,9 @@ add_directory(const char *path, if (db->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + NULL, eb, pb, db->pool, scratch_pool)); + SVN_ERR(mark_directory_edited(db, pool)); if (strcmp(eb->target_abspath, db->local_abspath) == 0) @@ -2037,13 +1900,26 @@ add_directory(const char *path, "administrative directory"), svn_dirent_local_style(db->local_abspath, pool)); - SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); + if (!eb->clean_checkout) + { + 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, + scratch_pool, scratch_pool); + } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + err = NULL; + } - 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) @@ -2056,63 +1932,68 @@ add_directory(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) { - /* !! 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; + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ } else if (status == svn_wc__db_status_normal - && (wc_kind == svn_node_file - || wc_kind == svn_node_symlink)) + || status == svn_wc__db_status_incomplete) { - /* We found a file external occupating the place we need in BASE. + svn_boolean_t root; + + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath, + scratch_pool)); - 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. + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - The reason we get here is that the adm crawler doesn't report - file externals. - */ + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). - SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); + 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_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* 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_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } + + SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_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); + do_notification(eb, db->local_abspath, wc_kind, + 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); @@ -2134,7 +2015,7 @@ add_directory(const char *path, eb->db, db->local_abspath, tree_conflict, - db->pool, db->pool)); + db->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(db->pool); @@ -2143,7 +2024,7 @@ add_directory(const char *path, eb->db, db->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - db->pool, db->pool)); + db->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -2154,7 +2035,8 @@ add_directory(const char *path, } else SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, - eb->db, db->local_abspath, pool)); + eb->db, db->local_abspath, + scratch_pool)); } /* Now the "usual" behaviour if already conflicted. Skip it. */ @@ -2177,18 +2059,13 @@ add_directory(const char *path, 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)); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); - /* ### TODO: Also print victim_path in the skip msg. */ do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_skip_conflicted, pool); + svn_wc_notify_skip_conflicted, scratch_pool); + + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } else if (conflict_ignored) @@ -2221,7 +2098,7 @@ add_directory(const char *path, SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, db->local_abspath, - pool, pool)); + scratch_pool, scratch_pool)); /* Is there *something* that is not a dir? */ @@ -2244,7 +2121,7 @@ add_directory(const char *path, db->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - pool, pool)); + db->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -2272,7 +2149,7 @@ add_directory(const char *path, eb->db, db->local_abspath, svn_wc_conflict_reason_unversioned, svn_wc_conflict_action_add, NULL, - db->pool, pool)); + db->pool, scratch_pool)); db->edit_conflict = tree_conflict; } } @@ -2280,14 +2157,17 @@ add_directory(const char *path, 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)); + db->new_repos_relpath, + wc_kind, svn_node_dir, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + db->name) + : NULL, + db->pool, scratch_pool)); SVN_ERR(svn_wc__db_base_add_incomplete_directory( eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, @@ -2296,28 +2176,20 @@ add_directory(const char *path, (! db->shadowed && status == svn_wc__db_status_added), tree_conflict, NULL, - pool)); + scratch_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)); + SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_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->edit_conflict = tree_conflict; db->already_notified = TRUE; do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_tree_conflict, pool); + svn_wc_notify_tree_conflict, scratch_pool); } @@ -2339,9 +2211,12 @@ add_directory(const char *path, db->already_notified = TRUE; - do_notification(eb, db->local_abspath, svn_node_dir, action, pool); + do_notification(eb, db->local_abspath, svn_node_dir, action, + scratch_pool); } + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } @@ -2416,6 +2291,10 @@ open_directory(const char *path, db->was_incomplete = (base_status == svn_wc__db_status_incomplete); + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, pb, + db->pool, pool)); + /* Is this path a conflict victim? */ if (db->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -2475,7 +2354,7 @@ open_directory(const char *path, /* 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, + db->new_repos_relpath, *eb->target_revision, pool)); @@ -2498,7 +2377,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_directory_edited(db, pool)); @@ -2619,7 +2498,7 @@ close_directory(void *dir_baton, hi != NULL; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_prop_t *prop = apr_array_push(regular_prop_changes); /* Record a deletion for PROPNAME. */ @@ -2702,7 +2581,8 @@ close_directory(void *dir_baton, /* 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); + apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, + db->new_repos_relpath); if (new_children != NULL) { @@ -2723,11 +2603,11 @@ close_directory(void *dir_baton, svn_pool_clear(iterpool); - child_name = svn__apr_hash_index_key(hi); + child_name = apr_hash_this_key(hi); child_abspath = svn_dirent_join(db->local_abspath, child_name, iterpool); - dirent = svn__apr_hash_index_val(hi); + dirent = apr_hash_this_val(hi); child_kind = (dirent->kind == svn_node_dir) ? svn_node_dir : svn_node_file; @@ -2758,7 +2638,7 @@ close_directory(void *dir_baton, svn_error_clear(err); - child_relpath = svn_relpath_join(db->new_relpath, child_name, + child_relpath = svn_relpath_join(db->new_repos_relpath, child_name, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, @@ -2776,7 +2656,7 @@ close_directory(void *dir_baton, } } - if (apr_hash_count(db->not_present_files)) + if (apr_hash_count(db->not_present_nodes)) { apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -2786,17 +2666,18 @@ close_directory(void *dir_baton, 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); + for (hi = apr_hash_first(scratch_pool, db->not_present_nodes); hi; hi = apr_hash_next(hi)) { - const char *child = svn__apr_hash_index_key(hi); + const char *child = apr_hash_this_key(hi); const char *child_abspath, *child_relpath; + svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi)); svn_pool_clear(iterpool); child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); - child_relpath = svn_dirent_join(db->new_relpath, child, iterpool); + child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, child_abspath, @@ -2804,7 +2685,7 @@ close_directory(void *dir_baton, eb->repos_root, eb->repos_uuid, *eb->target_revision, - svn_node_file, + kind, NULL, NULL, iterpool)); } @@ -2872,8 +2753,14 @@ close_directory(void *dir_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + (db->parent_baton + && db->parent_baton->deletion_conflicts) + ? svn_hash_gets( + db->parent_baton->deletion_conflicts, + db->name) + : NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -2904,7 +2791,7 @@ close_directory(void *dir_baton, SVN_ERR(svn_wc__db_base_add_directory( eb->db, db->local_abspath, eb->wcroot_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, props, @@ -2914,10 +2801,9 @@ close_directory(void *dir_baton, (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, + new_actual_props, iprops, + conflict_skel, all_work_items, scratch_pool)); } @@ -2926,8 +2812,12 @@ close_directory(void *dir_baton, eb->cancel_func, eb->cancel_baton, scratch_pool)); + if (db->parent_baton) + svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL); + if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, + svn_node_dir, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -2962,6 +2852,9 @@ close_directory(void *dir_baton, eb->notify_func(eb->notify_baton, notify, scratch_pool); } + if (db->edited) + eb->edited = db->edited; + /* We're done with this directory, so remove one reference from the bump information. */ SVN_ERR(maybe_release_dir_info(db)); @@ -2985,6 +2878,7 @@ absent_node(const char *path, svn_error_t *err; svn_wc__db_status_t status; svn_node_kind_t kind; + svn_skel_t *tree_conflict = NULL; if (pb->skip_this) return SVN_NO_ERROR; @@ -3073,24 +2967,26 @@ absent_node(const char *path, { /* 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. - */ + a copy). */ 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)); + if (!pb->shadowed && !pb->edit_obstructed) + SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, + status, FALSE, svn_node_unknown, + svn_wc_conflict_action_add, + scratch_pool, scratch_pool)); + } { const char *repos_relpath; - repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool); + repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool); + + if (tree_conflict) + SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, + NULL, SVN_INVALID_REVNUM, repos_relpath, + kind, svn_node_unknown, NULL, + scratch_pool, 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) */ @@ -3100,8 +2996,24 @@ absent_node(const char *path, *(eb->target_revision), absent_kind, svn_wc__db_status_server_excluded, - NULL, NULL, + tree_conflict, NULL, scratch_pool)); + + if (tree_conflict) + { + if (eb->conflict_func) + SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, + tree_conflict, + NULL /* merge_options */, + eb->conflict_func, + eb->conflict_baton, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, + scratch_pool); + } } svn_pool_destroy(scratch_pool); @@ -3142,13 +3054,13 @@ add_file(const char *path, 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; + svn_node_kind_t kind; + svn_node_kind_t wc_kind; + svn_wc__db_status_t status; apr_pool_t *scratch_pool; - svn_boolean_t conflicted = FALSE; + svn_boolean_t conflicted; svn_boolean_t conflict_ignored = FALSE; - svn_boolean_t versioned_locally_and_present = FALSE; + svn_boolean_t versioned_locally_and_present; svn_skel_t *tree_conflict = NULL; svn_error_t *err = SVN_NO_ERROR; @@ -3160,6 +3072,8 @@ add_file(const char *path, if (fb->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + NULL, eb, pb, fb->pool, pool)); SVN_ERR(mark_file_edited(fb, pool)); /* The file_pool can stick around for a *long* time, so we want to @@ -3186,6 +3100,13 @@ add_file(const char *path, eb->db, fb->local_abspath, scratch_pool, scratch_pool); } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + } if (err) { @@ -3198,58 +3119,68 @@ add_file(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) + { + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ + } + else if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) { - /* !! We found the root of a separate working copy obstructing the wc !! + svn_boolean_t root; - If the directory would be part of our own working copy then - we wouldn't have been called as an add_file(). + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath, + scratch_pool)); - 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); + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); - fb->skip_this = TRUE; - fb->already_notified = TRUE; + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). - do_notification(eb, fb->local_abspath, svn_node_file, - svn_wc_notify_update_skip_obstruction, scratch_pool); + 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_pool_destroy(scratch_pool); + svn_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* We found a file external occupating the place we need in BASE. - 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. - 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_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } - 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, + do_notification(eb, fb->local_abspath, wc_kind, 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); @@ -3274,7 +3205,7 @@ add_file(const char *path, eb->db, fb->local_abspath, tree_conflict, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(fb->pool); @@ -3283,7 +3214,7 @@ add_file(const char *path, eb->db, fb->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -3316,10 +3247,10 @@ add_file(const char *path, 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); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); - do_notification(eb, fb->local_abspath, svn_node_unknown, + do_notification(eb, fb->local_abspath, svn_node_file, svn_wc_notify_skip_conflicted, scratch_pool); svn_pool_destroy(scratch_pool); @@ -3380,7 +3311,7 @@ add_file(const char *path, fb->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - scratch_pool, scratch_pool)); + fb->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -3421,8 +3352,8 @@ add_file(const char *path, || *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); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); } if (tree_conflict != NULL) @@ -3432,9 +3363,12 @@ add_file(const char *path, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, - wc_kind, - svn_node_file, + fb->new_repos_relpath, + wc_kind, svn_node_file, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, @@ -3442,15 +3376,7 @@ add_file(const char *path, 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->edit_conflict = tree_conflict; fb->already_notified = TRUE; do_notification(eb, fb->local_abspath, svn_node_file, @@ -3514,7 +3440,6 @@ open_file(const char *path, /* 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, @@ -3536,6 +3461,10 @@ open_file(const char *path, eb->db, fb->local_abspath, fb->pool, scratch_pool)); + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + fb->old_repos_relpath, eb, pb, + fb->pool, scratch_pool)); + /* Is this path a conflict victim? */ if (fb->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -3615,12 +3544,6 @@ lazy_open_source(svn_stream_t **stream, 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, @@ -3628,13 +3551,23 @@ lazy_open_target(svn_stream_t **stream, 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)); + struct handler_baton *hb = baton; + svn_wc__db_install_data_t *install_data; + + /* By convention return value is undefined on error, but we rely + on HB->INSTALL_DATA value in window_handler() and abort + INSTALL_STREAM if is not NULL on error. + So we store INSTALL_DATA to local variable first, to leave + HB->INSTALL_DATA unchanged on error. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(stream, + &install_data, + &hb->new_text_base_sha1_checksum, + NULL, + hb->fb->edit_baton->db, + hb->fb->dir_baton->local_abspath, + result_pool, scratch_pool)); + + hb->install_data = install_data; return SVN_NO_ERROR; } @@ -3654,7 +3587,6 @@ apply_textdelta(void *file_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) @@ -3748,16 +3680,12 @@ apply_textdelta(void *file_baton, 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); + target = svn_stream_lazyopen_create(lazy_open_target, hb, 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 */, + fb->local_abspath /* error_info */, handler_pool, &hb->apply_handler, &hb->apply_baton); @@ -3788,7 +3716,7 @@ change_file_prop(void *file_baton, /* 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; + propchange->value = svn_string_dup(value, fb->pool); if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_file_edited(fb, scratch_pool)); @@ -3851,9 +3779,9 @@ change_file_prop(void *file_baton, SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, fb->local_abspath, fb->old_repos_relpath, - fb->old_revision, fb->new_relpath, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, - fb->pool, scratch_pool)); + NULL, 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 @@ -3913,13 +3841,13 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, const char *merge_left; svn_boolean_t delete_left = FALSE; const char *path_ext = ""; - const char *new_text_base_tmp_abspath; + const char *new_pristine_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, + SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, db, wri_abspath, new_checksum, scratch_pool, scratch_pool)); @@ -3972,7 +3900,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, &merge_outcome, db, merge_left, - new_text_base_tmp_abspath, + new_pristine_abspath, local_abspath, wri_abspath, oldrev_str, newrev_str, mine_str, @@ -4321,11 +4249,11 @@ close_file(void *file_baton, { /* 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) + if (! eb->switch_repos_relpath + || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0) { SVN_ERR_ASSERT(prop->value == NULL); - SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, + SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL, scratch_pool)); lock_state = svn_wc_notify_lock_state_unlocked; @@ -4564,8 +4492,13 @@ close_file(void *file_baton, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, + fb->new_repos_relpath, svn_node_file, svn_node_file, + fb->dir_baton->deletion_conflicts + ? svn_hash_gets( + fb->dir_baton->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -4593,7 +4526,7 @@ close_file(void *file_baton, SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, eb->wcroot_abspath, - fb->new_relpath, + fb->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, new_base_props, @@ -4618,6 +4551,7 @@ close_file(void *file_baton, if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, + svn_node_file, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -4628,7 +4562,7 @@ close_file(void *file_baton, /* Deal with the WORKING tree, based on updates to the BASE tree. */ - svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL); + svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL); /* Send a notification to the callback function. (Skip notifications about files which were already notified for another reason.) */ @@ -4687,6 +4621,78 @@ close_file(void *file_baton, } +/* Implements svn_wc__proplist_receiver_t. + * Check for the presence of an svn:keywords property and queues an install_file + * work queue item if present. Thus, when the work queue is run to complete the + * switch operation, all files with keywords will go through the translation + * process so URLs etc are updated. */ +static svn_error_t * +update_keywords_after_switch_cb(void *baton, + const char *local_abspath, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + svn_string_t *propval; + svn_boolean_t modified; + svn_boolean_t record_fileinfo; + svn_skel_t *work_items; + const char *install_from; + + propval = svn_hash_gets(props, SVN_PROP_KEYWORDS); + if (!propval) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, + local_abspath, FALSE, + scratch_pool)); + if (modified) + { + const char *temp_dir_abspath; + svn_stream_t *working_stream; + svn_stream_t *install_from_stream; + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db, + local_abspath, scratch_pool, + scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from, + temp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(working_stream, install_from_stream, + eb->cancel_func, eb->cancel_baton, + scratch_pool)); + record_fileinfo = FALSE; + } + else + { + install_from = NULL; + record_fileinfo = TRUE; + } + + SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath, + install_from, + eb->use_commit_times, + record_fileinfo, + scratch_pool, scratch_pool)); + if (install_from) + { + svn_skel_t *work_item; + + SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, + local_abspath, install_from, + scratch_pool, scratch_pool)); + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + } + + SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items, + scratch_pool)); + + return SVN_NO_ERROR; +} + + /* An svn_delta_editor_t function. */ static svn_error_t * close_edit(void *edit_baton, @@ -4728,12 +4734,13 @@ close_edit(void *edit_baton, SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, eb->target_abspath, eb->requested_depth, - eb->switch_relpath, + eb->switch_repos_relpath, eb->repos_root, eb->repos_uuid, *(eb->target_revision), eb->skipped_trees, eb->wcroot_iprops, + ! eb->edited, eb->notify_func, eb->notify_baton, eb->pool)); @@ -4773,15 +4780,36 @@ close_edit(void *edit_baton, 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 */, + TRUE, FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); } } } + /* Update keywords in switched files. + GOTO #1975 (the year of the Altair 8800). */ + if (eb->switch_repos_relpath) + { + svn_depth_t depth; + + if (eb->requested_depth > svn_depth_empty) + depth = eb->requested_depth; + else + depth = svn_depth_infinity; + + SVN_ERR(svn_wc__db_read_props_streamily(eb->db, + eb->target_abspath, + depth, + FALSE, /* pristine */ + NULL, /* changelists */ + update_keywords_after_switch_cb, + eb, + eb->cancel_func, + eb->cancel_baton, + 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. */ @@ -4854,9 +4882,11 @@ make_editor(svn_revnum_t *target_revision, /* 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)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root, + &repos_uuid, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + 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); @@ -4884,10 +4914,10 @@ make_editor(svn_revnum_t *target_revision, edit_pool, scratch_pool)); if (switch_url) - eb->switch_relpath = + eb->switch_repos_relpath = svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); else - eb->switch_relpath = NULL; + eb->switch_repos_relpath = NULL; if (svn_path_is_empty(target_basename)) eb->target_abspath = eb->anchor_abspath; @@ -4968,8 +4998,8 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) - dir_repos_relpath = eb->switch_relpath; + if (eb->switch_repos_relpath) + dir_repos_relpath = eb->switch_repos_relpath; SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, repos_root, dir_repos_relpath, @@ -5022,9 +5052,9 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) + if (eb->switch_repos_relpath) dir_repos_relpath = svn_relpath_join( - eb->switch_relpath, + eb->switch_repos_relpath, child_name, iterpool); SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, @@ -5223,6 +5253,8 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; + svn_stream_t *tmp_base_contents; + svn_wc__db_install_data_t *install_data; svn_error_t *err; apr_pool_t *pool = scratch_pool; @@ -5344,18 +5376,35 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, /* 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; + if (copyfrom_url) + { + SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents, + &install_data, + &new_text_base_sha1_checksum, + &new_text_base_md5_checksum, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + } + else + { + const char *tmp_dir_abspath; - 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)); - } + /* We are not installing a PRISTINE file, but we use the same code to + create whatever we want to install */ + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, + db, dir_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, + tmp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + + new_text_base_sha1_checksum = NULL; + new_text_base_md5_checksum = NULL; + } + 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 @@ -5378,7 +5427,7 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, text base. */ if (copyfrom_url != NULL) { - SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath, + SVN_ERR(svn_wc__db_pristine_install(install_data, new_text_base_sha1_checksum, new_text_base_md5_checksum, pool)); } @@ -5445,9 +5494,6 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, } } - /* ### 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, diff --git a/contrib/subversion/subversion/libsvn_wc/upgrade.c b/contrib/subversion/subversion/libsvn_wc/upgrade.c index af615fd61..aebf4eb1e 100644 --- a/contrib/subversion/subversion/libsvn_wc/upgrade.c +++ b/contrib/subversion/subversion/libsvn_wc/upgrade.c @@ -37,6 +37,7 @@ #include "tree_conflicts.h" #include "wc-queries.h" /* for STMT_* */ #include "workqueue.h" +#include "token-map.h" #include "svn_private_config.h" #include "private/svn_wc_private.h" @@ -193,7 +194,7 @@ read_many_wcprops(apr_hash_t **all_wcprops, hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); svn_pool_clear(iterpool); @@ -293,15 +294,15 @@ get_versioned_subdirs(apr_array_header_t **children, hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_wc_entry_t *entry = apr_hash_this_val(hi); const char *child_abspath; svn_boolean_t hidden; /* skip "this dir" */ if (*name == '\0') { - this_dir = svn__apr_hash_index_val(hi); + this_dir = apr_hash_this_val(hi); continue; } else if (entry->kind != svn_node_dir) @@ -400,7 +401,7 @@ build_lockfile_path(const char *local_dir_abspath, local_dir_abspath, svn_wc_get_adm_dir(result_pool), ADM_LOCK, - NULL); + SVN_VA_NULL); } @@ -612,13 +613,13 @@ ensure_repos_info(svn_wc_entry_t *entry, for (hi = apr_hash_first(scratch_pool, repos_cache); hi; hi = apr_hash_next(hi)) { - if (svn_uri__is_ancestor(svn__apr_hash_index_key(hi), entry->url)) + if (svn_uri__is_ancestor(apr_hash_this_key(hi), entry->url)) { if (!entry->repos) - entry->repos = svn__apr_hash_index_key(hi); + entry->repos = apr_hash_this_key(hi); if (!entry->uuid) - entry->uuid = svn__apr_hash_index_val(hi); + entry->uuid = apr_hash_this_val(hi); return SVN_NO_ERROR; } @@ -727,8 +728,7 @@ migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, hi; hi = apr_hash_next(hi)) { - const svn_wc_conflict_description2_t *conflict = - svn__apr_hash_index_val(hi); + const svn_wc_conflict_description2_t *conflict = apr_hash_this_val(hi); const char *conflict_relpath; const char *conflict_data; svn_sqlite__stmt_t *stmt; @@ -755,13 +755,13 @@ migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, { /* There is an existing ACTUAL row, so just update it. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_UPDATE_ACTUAL_CONFLICT_DATA)); + STMT_UPDATE_ACTUAL_CONFLICT)); } else { /* We need to insert an ACTUAL row with the tree conflict data. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_INSERT_ACTUAL_CONFLICT_DATA)); + STMT_INSERT_ACTUAL_CONFLICT)); } SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath, @@ -825,6 +825,190 @@ migrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) return SVN_NO_ERROR; } +/* ### 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. +*/ +static svn_error_t * +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_presence = svn_sqlite__column_token(stmt, 3, presence_map); + + /* There might be an intermediate layer on mixed-revision copies, + or when BASE is shadowed */ + if (below_presence == svn_wc__db_status_not_present + || below_presence == svn_wc__db_status_deleted) + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + { + below_presence = svn_sqlite__column_token(stmt, 3, presence_map); + below_op_depth = svn_sqlite__column_int(stmt, 0); + } + } + } + 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(svn_sqlite__get_statement(&stmt, sdb, + STMT_UPDATE_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props, + scratch_pool)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (affected_rows == 0) + { + /* We have to insert a row in ACTUAL */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + 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, working_props, + scratch_pool)); + return svn_error_trace(svn_sqlite__step_done(stmt)); + } + } + + return SVN_NO_ERROR; +} + struct bump_baton { const char *wcroot_abspath; @@ -875,21 +1059,21 @@ migrate_node_props(const char *dir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__BASE_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); revert_abspath = svn_dirent_join(basedir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__REVERT_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); working_abspath = svn_dirent_join(propsdir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__WORK_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); } @@ -900,7 +1084,7 @@ migrate_node_props(const char *dir_abspath, SVN_ERR(read_propfile(&working_props, working_abspath, scratch_pool, scratch_pool)); - return svn_error_trace(svn_wc__db_upgrade_apply_props( + return svn_error_trace(upgrade_apply_props( sdb, new_wcroot_abspath, svn_relpath_join(dir_relpath, name, scratch_pool), base_props, revert_props, working_props, @@ -1017,7 +1201,7 @@ migrate_text_bases(apr_hash_t **text_bases_info, for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - const char *text_base_basename = svn__apr_hash_index_key(hi); + const char *text_base_basename = apr_hash_this_key(hi); svn_checksum_t *md5_checksum; svn_checksum_t *sha1_checksum; @@ -1248,7 +1432,7 @@ rename_pristine_file(void *baton, == PRISTINE_BASENAME_OLD_LEN)) { const char *new_abspath - = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, (char *)NULL); + = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, SVN_VA_NULL); SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool)); } @@ -1349,7 +1533,8 @@ bump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) /* Rename all pristine files, adding a ".svn-base" suffix. */ pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath, svn_wc_get_adm_dir(scratch_pool), - PRISTINE_STORAGE_RELPATH, NULL); + PRISTINE_STORAGE_RELPATH, + SVN_VA_NULL); SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN, rename_pristine_file, NULL, scratch_pool)); @@ -1671,6 +1856,43 @@ bump_to_31(void *baton, return SVN_NO_ERROR; } +static svn_error_t * +upgrade_apply_dav_cache(svn_sqlite__db_t *sdb, + const char *dir_relpath, + apr_int64_t wc_id, + apr_hash_t *cache_values, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + svn_sqlite__stmt_t *stmt; + + 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 = apr_hash_this_key(hi); + apr_hash_t *props = apr_hash_this_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; +} + struct upgrade_data_t { svn_sqlite__db_t *sdb; @@ -1808,8 +2030,8 @@ upgrade_to_wcng(void **dir_baton, SVN_ERR(read_wcprops(&all_wcprops, dir_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_upgrade_apply_dav_cache(data->sdb, dir_relpath, - all_wcprops, scratch_pool)); + SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id, + all_wcprops, scratch_pool)); } /* Upgrade all the properties (including "this dir"). @@ -2144,40 +2366,6 @@ is_old_wcroot(const char *local_abspath, svn_dirent_local_style(parent_abspath, scratch_pool)); } -/* Data for upgrade_working_copy_txn(). */ -typedef struct upgrade_working_copy_baton_t -{ - svn_wc__db_t *db; - const char *dir_abspath; - svn_wc_upgrade_get_repos_info_t repos_info_func; - void *repos_info_baton; - apr_hash_t *repos_cache; - const struct upgrade_data_t *data; - svn_cancel_func_t cancel_func; - void *cancel_baton; - svn_wc_notify_func2_t notify_func; - void *notify_baton; - apr_pool_t *result_pool; -} upgrade_working_copy_baton_t; - - -/* Helper for svn_wc_upgrade. Implements svn_sqlite__transaction_callback_t */ -static svn_error_t * -upgrade_working_copy_txn(void *baton, - svn_sqlite__db_t *sdb, - apr_pool_t *scratch_pool) -{ - upgrade_working_copy_baton_t *b = baton; - - /* Upgrade the pre-wcng into a wcng in a temporary location. */ - return(upgrade_working_copy(NULL, b->db, b->dir_abspath, - b->repos_info_func, b->repos_info_baton, - b->repos_cache, b->data, - b->cancel_func, b->cancel_baton, - b->notify_func, b->notify_baton, - b->result_pool, scratch_pool)); -} - svn_error_t * svn_wc_upgrade(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -2197,7 +2385,6 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx, svn_wc_entry_t *this_dir; apr_hash_t *entries; const char *root_adm_abspath; - upgrade_working_copy_baton_t cb_baton; svn_error_t *err; int result_format; svn_boolean_t bumped_format; @@ -2295,22 +2482,14 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE, scratch_pool)); - cb_baton.db = db; - cb_baton.dir_abspath = local_abspath; - cb_baton.repos_info_func = repos_info_func; - cb_baton.repos_info_baton = repos_info_baton; - cb_baton.repos_cache = repos_cache; - cb_baton.data = &data; - cb_baton.cancel_func = cancel_func; - cb_baton.cancel_baton = cancel_baton; - cb_baton.notify_func = notify_func; - cb_baton.notify_baton = notify_baton; - cb_baton.result_pool = scratch_pool; - - SVN_ERR(svn_sqlite__with_lock(data.sdb, - upgrade_working_copy_txn, - &cb_baton, - scratch_pool)); + SVN_SQLITE__WITH_LOCK( + upgrade_working_copy(NULL, db, local_abspath, + repos_info_func, repos_info_baton, + repos_cache, &data, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool, scratch_pool), + data.sdb); /* A workqueue item to move the pristine dir into place */ pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH, diff --git a/contrib/subversion/subversion/libsvn_wc/util.c b/contrib/subversion/subversion/libsvn_wc/util.c index a527eda51..7bb2179ad 100644 --- a/contrib/subversion/subversion/libsvn_wc/util.c +++ b/contrib/subversion/subversion/libsvn_wc/util.c @@ -248,9 +248,34 @@ svn_wc_conflict_description_create_tree2( return conflict; } +svn_wc_conflict_version_t * +svn_wc_conflict_version_create2(const char *repos_url, + const char *repos_uuid, + const char *repos_relpath, + svn_revnum_t revision, + svn_node_kind_t kind, + apr_pool_t *result_pool) +{ + svn_wc_conflict_version_t *version; + + version = apr_pcalloc(result_pool, sizeof(*version)); + + SVN_ERR_ASSERT_NO_RETURN(svn_uri_is_canonical(repos_url, result_pool) + && svn_relpath_is_canonical(repos_relpath) + && SVN_IS_VALID_REVNUM(revision) + /* ### repos_uuid can be NULL :( */); + + version->repos_url = repos_url; + version->peg_rev = revision; + version->path_in_repos = repos_relpath; + version->node_kind = kind; + version->repos_uuid = repos_uuid; + + return version; +} svn_wc_conflict_description2_t * -svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, +svn_wc_conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, apr_pool_t *pool) { svn_wc_conflict_description2_t *new_conflict; @@ -281,36 +306,27 @@ svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict new_conflict->src_right_version = svn_wc_conflict_version_dup(conflict->src_right_version, pool); - return new_conflict; -} + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + if (conflict->prop_reject_abspath) + new_conflict->prop_reject_abspath = new_conflict->their_abspath; + + if (conflict->prop_value_base) + new_conflict->prop_value_base = + svn_string_dup(conflict->prop_value_base, pool); + if (conflict->prop_value_working) + new_conflict->prop_value_working = + svn_string_dup(conflict->prop_value_working, pool); + if (conflict->prop_value_incoming_old) + new_conflict->prop_value_incoming_old = + svn_string_dup(conflict->prop_value_incoming_old, pool); + if (conflict->prop_value_incoming_new) + new_conflict->prop_value_incoming_new = + svn_string_dup(conflict->prop_value_incoming_new, pool); -svn_wc_conflict_version_t * -svn_wc_conflict_version_create2(const char *repos_url, - const char *repos_uuid, - const char *repos_relpath, - svn_revnum_t revision, - svn_node_kind_t kind, - apr_pool_t *result_pool) -{ - svn_wc_conflict_version_t *version; - - version = apr_pcalloc(result_pool, sizeof(*version)); - - SVN_ERR_ASSERT_NO_RETURN(svn_uri_is_canonical(repos_url, result_pool) - && svn_relpath_is_canonical(repos_relpath) - && SVN_IS_VALID_REVNUM(revision) - /* ### repos_uuid can be NULL :( */); - - version->repos_url = repos_url; - version->peg_rev = revision; - version->path_in_repos = repos_relpath; - version->node_kind = kind; - version->repos_uuid = repos_uuid; - - return version; + return new_conflict; } - svn_wc_conflict_version_t * svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version, apr_pool_t *result_pool) @@ -339,7 +355,6 @@ svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version, return new_version; } - svn_wc_conflict_description_t * svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict, apr_pool_t *result_pool) @@ -402,145 +417,6 @@ svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict, } -svn_error_t * -svn_wc__status2_from_3(svn_wc_status2_t **status, - const svn_wc_status3_t *old_status, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const svn_wc_entry_t *entry = NULL; - - if (old_status == NULL) - { - *status = NULL; - return SVN_NO_ERROR; - } - - *status = apr_pcalloc(result_pool, sizeof(**status)); - - if (old_status->versioned) - { - svn_error_t *err; - err= svn_wc__get_entry(&entry, wc_ctx->db, local_abspath, FALSE, - svn_node_unknown, result_pool, scratch_pool); - - if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND) - svn_error_clear(err); - else - SVN_ERR(err); - } - - (*status)->entry = entry; - (*status)->copied = old_status->copied; - (*status)->repos_lock = svn_lock_dup(old_status->repos_lock, result_pool); - - if (old_status->repos_relpath) - (*status)->url = svn_path_url_add_component2(old_status->repos_root_url, - old_status->repos_relpath, - result_pool); - (*status)->ood_last_cmt_rev = old_status->ood_changed_rev; - (*status)->ood_last_cmt_date = old_status->ood_changed_date; - (*status)->ood_kind = old_status->ood_kind; - (*status)->ood_last_cmt_author = old_status->ood_changed_author; - - if (old_status->conflicted) - { - const svn_wc_conflict_description2_t *tree_conflict; - SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, wc_ctx, local_abspath, - scratch_pool, scratch_pool)); - (*status)->tree_conflict = svn_wc__cd2_to_cd(tree_conflict, result_pool); - } - - (*status)->switched = old_status->switched; - - (*status)->text_status = old_status->node_status; - (*status)->prop_status = old_status->prop_status; - - (*status)->repos_text_status = old_status->repos_node_status; - (*status)->repos_prop_status = old_status->repos_prop_status; - - /* Some values might be inherited from properties */ - if (old_status->node_status == svn_wc_status_modified - || old_status->node_status == svn_wc_status_conflicted) - (*status)->text_status = old_status->text_status; - - /* (Currently a no-op, but just make sure it is ok) */ - if (old_status->repos_node_status == svn_wc_status_modified - || old_status->repos_node_status == svn_wc_status_conflicted) - (*status)->repos_text_status = old_status->repos_text_status; - - if (old_status->node_status == svn_wc_status_added) - (*status)->prop_status = svn_wc_status_none; /* No separate info */ - - /* Find pristine_text_status value */ - switch (old_status->text_status) - { - case svn_wc_status_none: - case svn_wc_status_normal: - case svn_wc_status_modified: - (*status)->pristine_text_status = old_status->text_status; - break; - case svn_wc_status_conflicted: - default: - /* ### Fetch compare data, or fall back to the documented - not retrieved behavior? */ - (*status)->pristine_text_status = svn_wc_status_none; - break; - } - - /* Find pristine_prop_status value */ - switch (old_status->prop_status) - { - case svn_wc_status_none: - case svn_wc_status_normal: - case svn_wc_status_modified: - if (old_status->node_status != svn_wc_status_added - && old_status->node_status != svn_wc_status_deleted - && old_status->node_status != svn_wc_status_replaced) - { - (*status)->pristine_prop_status = old_status->prop_status; - } - else - (*status)->pristine_prop_status = svn_wc_status_none; - break; - case svn_wc_status_conflicted: - default: - /* ### Fetch compare data, or fall back to the documented - not retrieved behavior? */ - (*status)->pristine_prop_status = svn_wc_status_none; - break; - } - - if (old_status->versioned - && old_status->conflicted - && old_status->node_status != svn_wc_status_obstructed - && (old_status->kind == svn_node_file - || old_status->node_status != svn_wc_status_missing)) - { - svn_boolean_t text_conflict_p, prop_conflict_p; - - /* The entry says there was a conflict, but the user might have - marked it as resolved by deleting the artifact files, so check - for that. */ - SVN_ERR(svn_wc__internal_conflicted_p(&text_conflict_p, - &prop_conflict_p, - NULL, - wc_ctx->db, local_abspath, - scratch_pool)); - - if (text_conflict_p) - (*status)->text_status = svn_wc_status_conflicted; - - if (prop_conflict_p) - (*status)->prop_status = svn_wc_status_conflicted; - } - - return SVN_NO_ERROR; -} - - svn_error_t * svn_wc__fetch_kind_func(svn_node_kind_t *kind, void *baton, diff --git a/contrib/subversion/subversion/libsvn_wc/wc-checks.h b/contrib/subversion/subversion/libsvn_wc/wc-checks.h index ebc73ed03..35f15336f 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-checks.h +++ b/contrib/subversion/subversion/libsvn_wc/wc-checks.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.9.4/subversion/libsvn_wc/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_VERIFICATION_TRIGGERS 0 @@ -42,14 +42,189 @@ "END; " \ "" +#define STMT_STATIC_VERIFY 1 +#define STMT_1_INFO {"STMT_STATIC_VERIFY", NULL} +#define STMT_1 \ + "SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.local_relpath) " \ + "UNION ALL " \ + "SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' " \ + "FROM actual_node a " \ + "LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) from nodes i " \ + " WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) " \ + "WHERE (a.properties IS NOT NULL " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete'))) " \ + " OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != 'file')) " \ + " OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=n.wc_id " \ + " AND i.local_relpath=n.parent_relpath " \ + " AND i.op_depth <= n.op_depth) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 21, 'Unneeded node data' " \ + "FROM nodes " \ + "WHERE presence NOT IN ('normal', 'incomplete') " \ + "AND (properties IS NOT NULL " \ + " OR checksum IS NOT NULL " \ + " OR depth IS NOT NULL " \ + " OR symlink_target IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0) " \ + " OR changed_author IS NOT NULL " \ + " OR translated_size IS NOT NULL " \ + " OR last_mod_time IS NOT NULL " \ + " OR dav_cache IS NOT NULL " \ + " OR file_external IS NOT NULL " \ + " OR inherited_props IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' " \ + "FROM nodes " \ + "WHERE presence IN ('base-deleted') " \ + "AND (repos_id IS NOT NULL " \ + " OR repos_path IS NOT NULL " \ + " OR revision IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + "AND (kind IS NULL " \ + " OR (repos_path IS NULL " \ + " AND (properties IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR changed_author IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0))) " \ + " OR (CASE WHEN kind = 'file' AND repos_path IS NOT NULL " \ + " THEN checksum IS NULL " \ + " ELSE checksum IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'dir' THEN depth IS NULL " \ + " ELSE depth IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'symlink' THEN symlink_target IS NULL " \ + " ELSE symlink_target IS NOT NULL END)) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + " AND repos_path IS NULL " \ + " AND op_depth != relpath_depth(local_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' " \ + "FROM nodes n " \ + "WHERE op_depth < relpath_depth(local_relpath) " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes p " \ + " WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND p.op_depth=n.op_depth " \ + " AND (p.presence IN ('normal', 'incomplete') " \ + " OR (p.presence IN ('base-deleted', 'not-present') " \ + " AND n.presence = 'base-deleted'))) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' " \ + "FROM nodes n " \ + "JOIN nodes p " \ + " ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND n.op_depth=p.op_depth " \ + "WHERE n.op_depth > 0 AND n.presence IN ('normal', 'incomplete') " \ + " AND (n.repos_id != p.repos_id " \ + " OR n.repos_path != " \ + " (CASE WHEN (n.parent_relpath) = '' THEN (CASE WHEN (p.repos_path) = '' THEN (n.local_relpath) WHEN (n.local_relpath) = '' THEN (p.repos_path) ELSE (p.repos_path) || '/' || (n.local_relpath) END) WHEN (p.repos_path) = '' THEN (CASE WHEN (n.parent_relpath) = '' THEN (n.local_relpath) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN '' WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+2) END END) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN (p.repos_path) WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN (p.repos_path) || SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1) END END) " \ + " OR n.revision != p.revision " \ + " OR p.kind != 'dir' " \ + " OR n.moved_here IS NOT p.moved_here) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' " \ + "FROM nodes n " \ + "WHERE n.op_depth = relpath_depth(local_relpath) " \ + " AND presence NOT IN ('normal', 'incomplete', 'base-deleted') " \ + "UNION ALL " \ + "SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath " \ + " AND s.op_depth = relpath_depth(s.local_relpath) " \ + " AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth > n.op_depth) " \ + "WHERE n.presence IN ('normal', 'incomplete') " \ + " AND EXISTS(SELECT 1 " \ + " FROM nodes dn " \ + " WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth " \ + " AND dn.presence IN ('normal', 'incomplete') " \ + " AND (((dn.local_relpath) > (CASE (n.local_relpath) WHEN '' THEN '' ELSE (n.local_relpath) || '/' END)) AND ((dn.local_relpath) < CASE (n.local_relpath) WHEN '' THEN X'FFFF' ELSE (n.local_relpath) || '0' END)) " \ + " AND dn.file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 " \ + " FROM nodes ds " \ + " WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth " \ + " AND ds.local_relpath=dn.local_relpath)) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' " \ + "FROM nodes s " \ + "LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth < s.op_depth) " \ + "WHERE s.presence = 'base-deleted' " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete') " \ + " ) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' " \ + "FROM nodes n " \ + "WHERE n.op_depth = 0 " \ + " AND (n.moved_to IS NOT NULL " \ + " OR n.moved_here IS NOT NULL) " \ + "UNION ALL " \ + "SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' " \ + "FROM nodes d " \ + "WHERE d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes s " \ + " WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' " \ + "FROM nodes s " \ + "WHERE s.moved_to IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes d " \ + " WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to " \ + " AND d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here =1 AND d.repos_path IS NOT NULL) " \ + "" + #define WC_CHECKS_SQL_DECLARE_STATEMENTS(varname) \ static const char * const varname[] = { \ STMT_0, \ + STMT_1, \ NULL \ } #define WC_CHECKS_SQL_DECLARE_STATEMENT_INFO(varname) \ static const char * const varname[][2] = { \ STMT_0_INFO, \ + STMT_1_INFO, \ {NULL, NULL} \ } diff --git a/contrib/subversion/subversion/libsvn_wc/wc-checks.sql b/contrib/subversion/subversion/libsvn_wc/wc-checks.sql index a677270f3..fce7b4908 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-checks.sql +++ b/contrib/subversion/subversion/libsvn_wc/wc-checks.sql @@ -75,3 +75,217 @@ BEGIN SELECT RAISE(FAIL, 'WC DB validity check 04 failed'); END; +-- STMT_STATIC_VERIFY +SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' +FROM nodes n WHERE local_relpath != '' + AND (parent_relpath IS NULL + OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath) + OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) + +UNION ALL + +SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' +FROM actual_node a WHERE local_relpath != '' + AND (parent_relpath IS NULL + OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath) + OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) + +UNION ALL + +/* All ACTUAL nodes must have an equivalent NODE in NODES + or be only one level deep (delete-delete tc) */ +SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' +FROM actual_node a WHERE local_relpath != '' + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.parent_relpath) + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.local_relpath) + +UNION ALL +/* Verify if the ACTUAL data makes sense for the related node. + Only conflict data is valid if there is none */ +SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' +FROM actual_node a +LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath + AND n.op_depth = (SELECT MAX(op_depth) from nodes i + WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) +WHERE (a.properties IS NOT NULL + AND (n.presence IS NULL + OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE))) + OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != MAP_FILE)) + OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.parent_relpath) + +UNION ALL + +/* A parent node must exist for every normal node except the root. + That node must exist at a lower or equal op-depth */ +SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' +FROM nodes n WHERE local_relpath != '' + AND file_external IS NULL + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=n.wc_id + AND i.local_relpath=n.parent_relpath + AND i.op_depth <= n.op_depth) + +UNION ALL +/* If a node is not present in the working copy (normal, add, copy) it doesn't + have revision details stored on this record */ +SELECT local_relpath, op_depth, 21, 'Unneeded node data' +FROM nodes +WHERE presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE) +AND (properties IS NOT NULL + OR checksum IS NOT NULL + OR depth IS NOT NULL + OR symlink_target IS NOT NULL + OR changed_revision IS NOT NULL + OR (changed_date IS NOT NULL AND changed_date != 0) + OR changed_author IS NOT NULL + OR translated_size IS NOT NULL + OR last_mod_time IS NOT NULL + OR dav_cache IS NOT NULL + OR file_external IS NOT NULL + OR inherited_props IS NOT NULL) + +UNION ALL +/* base-deleted nodes don't have a repository location. They are just + shadowing without a replacement */ +SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' +FROM nodes +WHERE presence IN (MAP_BASE_DELETED) +AND (repos_id IS NOT NULL + OR repos_path IS NOT NULL + OR revision IS NOT NULL) + +UNION ALL +/* Verify if type specific data is set (or not set for wrong type) */ +SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' +FROM nodes +WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) +AND (kind IS NULL + OR (repos_path IS NULL + AND (properties IS NOT NULL + OR changed_revision IS NOT NULL + OR changed_author IS NOT NULL + OR (changed_date IS NOT NULL AND changed_date != 0))) + OR (CASE WHEN kind = MAP_FILE AND repos_path IS NOT NULL + THEN checksum IS NULL + ELSE checksum IS NOT NULL END) + OR (CASE WHEN kind = MAP_DIR THEN depth IS NULL + ELSE depth IS NOT NULL END) + OR (CASE WHEN kind = MAP_SYMLINK THEN symlink_target IS NULL + ELSE symlink_target IS NOT NULL END)) + +UNION ALL +/* Local-adds are always their own operation (read: they don't have + op-depth descendants, nor are op-depth descendants */ +SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' +FROM nodes +WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND repos_path IS NULL + AND op_depth != relpath_depth(local_relpath) + +UNION ALL +/* op-depth descendants are only valid if they have a direct parent + node at the same op-depth. Only certain types allow further + descendants */ +SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' +FROM nodes n +WHERE op_depth < relpath_depth(local_relpath) + AND file_external IS NULL + AND NOT EXISTS(SELECT 1 FROM nodes p + WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath + AND p.op_depth=n.op_depth + AND (p.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + OR (p.presence IN (MAP_BASE_DELETED, MAP_NOT_PRESENT) + AND n.presence = MAP_BASE_DELETED))) + +UNION ALL +/* Present op-depth descendants have the repository location implied by their + ancestor */ +SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' +FROM nodes n +JOIN nodes p + ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath + AND n.op_depth=p.op_depth +WHERE n.op_depth > 0 AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND (n.repos_id != p.repos_id + OR n.repos_path != + RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath) + OR n.revision != p.revision + OR p.kind != MAP_DIR + OR n.moved_here IS NOT p.moved_here) + +UNION ALL +/* Only certain presence values are valid as op-root. + Note that the wc-root always has presence normal or incomplete */ +SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' +FROM nodes n +WHERE n.op_depth = relpath_depth(local_relpath) + AND presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE, MAP_BASE_DELETED) + +UNION ALL +/* If a node is shadowed, all its present op-depth descendants + must be shadowed at the same op-depth as well */ +SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' +FROM nodes n +JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath + AND s.op_depth = relpath_depth(s.local_relpath) + AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d + WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath + AND d.op_depth > n.op_depth) +WHERE n.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND EXISTS(SELECT 1 + FROM nodes dn + WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth + AND dn.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND IS_STRICT_DESCENDANT_OF(dn.local_relpath, n.local_relpath) + AND dn.file_external IS NULL + AND NOT EXISTS(SELECT 1 + FROM nodes ds + WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth + AND ds.local_relpath=dn.local_relpath)) + +UNION ALL +/* A base-delete is only valid if it directly deletes a present node */ +SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' +FROM nodes s +LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath + AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d + WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath + AND d.op_depth < s.op_depth) +WHERE s.presence = MAP_BASE_DELETED + AND (n.presence IS NULL + OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE) + /*OR n.kind != s.kind*/) + +UNION ALL +/* Moves are stored in the working layers, not in BASE */ +SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' +FROM nodes n +WHERE n.op_depth = 0 + AND (n.moved_to IS NOT NULL + OR n.moved_here IS NOT NULL) + +UNION ALL +/* If moved_here is set on an op-root, there must be a proper moved_to */ +SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' +FROM nodes d +WHERE d.op_depth = relpath_depth(d.local_relpath) + AND d.moved_here IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM nodes s + WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) + +UNION ALL +/* If moved_to is set there should be an moved op root at the target */ +SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' +FROM nodes s +WHERE s.moved_to IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM nodes d + WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to + AND d.op_depth = relpath_depth(d.local_relpath) + AND d.moved_here =1 AND d.repos_path IS NOT NULL) diff --git a/contrib/subversion/subversion/libsvn_wc/wc-metadata.h b/contrib/subversion/subversion/libsvn_wc/wc-metadata.h index 83854d779..7b74d8374 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-metadata.h +++ b/contrib/subversion/subversion/libsvn_wc/wc-metadata.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.9.4/subversion/libsvn_wc/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_CREATE_SCHEMA 0 diff --git a/contrib/subversion/subversion/libsvn_wc/wc-metadata.sql b/contrib/subversion/subversion/libsvn_wc/wc-metadata.sql index 2a2358d7d..f52df93f4 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-metadata.sql +++ b/contrib/subversion/subversion/libsvn_wc/wc-metadata.sql @@ -108,7 +108,7 @@ CREATE TABLE PRISTINE ( ); CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum); - + /* ------------------------------------------------------------------------- */ /* The ACTUAL_NODE table describes text changes and property changes @@ -150,7 +150,7 @@ CREATE TABLE ACTUAL_NODE ( /* 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). */ @@ -170,7 +170,7 @@ CREATE TABLE ACTUAL_NODE ( /* 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? + 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 */ @@ -200,7 +200,7 @@ CREATE TABLE LOCK ( lock_owner TEXT, lock_comment TEXT, lock_date INTEGER, /* an APR date/time (usec since 1970) */ - + PRIMARY KEY (repos_id, repos_relpath) ); @@ -553,7 +553,7 @@ CREATE TABLE EXTERNALS ( /* the kind of the external. */ kind TEXT NOT NULL, - /* The local relpath of the directory NODE defining this external + /* 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, @@ -577,7 +577,7 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, 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 + statistics are 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. @@ -779,7 +779,7 @@ LIMIT 1 /* ------------------------------------------------------------------------- */ -/* Format 28 involves no schema changes, it only converts MD5 pristine +/* Format 28 involves no schema changes, it only converts MD5 pristine references to SHA1. */ -- STMT_UPGRADE_TO_28 diff --git a/contrib/subversion/subversion/libsvn_wc/wc-queries.h b/contrib/subversion/subversion/libsvn_wc/wc-queries.h index 50a1d9770..ad5ccb573 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-queries.h +++ b/contrib/subversion/subversion/libsvn_wc/wc-queries.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.8.14/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.9.4/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 @@ -54,6 +54,15 @@ #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 " \ + "FROM nodes " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ + "" + +#define STMT_SELECT_BASE_CHILDREN_INFO_LOCK 5 +#define STMT_5_INFO {"STMT_SELECT_BASE_CHILDREN_INFO_LOCK", NULL} +#define STMT_5 \ "SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, " \ " revision, depth, file_external, " \ " lock_token, lock_owner, lock_comment, lock_date " \ @@ -63,9 +72,9 @@ "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 \ +#define STMT_SELECT_WORKING_NODE 6 +#define STMT_6_INFO {"STMT_SELECT_WORKING_NODE", NULL} +#define STMT_6 \ "SELECT op_depth, presence, kind, checksum, translated_size, " \ " changed_revision, changed_date, changed_author, depth, symlink_target, " \ " repos_id, repos_path, revision, " \ @@ -76,19 +85,19 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_DEPTH_NODE 6 -#define STMT_6_INFO {"STMT_SELECT_DEPTH_NODE", NULL} -#define STMT_6 \ +#define STMT_SELECT_DEPTH_NODE 7 +#define STMT_7_INFO {"STMT_SELECT_DEPTH_NODE", NULL} +#define STMT_7 \ "SELECT repos_id, repos_path, presence, kind, revision, checksum, " \ " translated_size, changed_revision, changed_date, changed_author, depth, " \ - " symlink_target, last_mod_time, properties " \ + " symlink_target, properties, moved_to, moved_here " \ "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 \ +#define STMT_SELECT_LOWEST_WORKING_NODE 8 +#define STMT_8_INFO {"STMT_SELECT_LOWEST_WORKING_NODE", NULL} +#define STMT_8 \ "SELECT op_depth, presence, kind, moved_to " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \ @@ -96,9 +105,9 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_HIGHEST_WORKING_NODE 8 -#define STMT_8_INFO {"STMT_SELECT_HIGHEST_WORKING_NODE", NULL} -#define STMT_8 \ +#define STMT_SELECT_HIGHEST_WORKING_NODE 9 +#define STMT_9_INFO {"STMT_SELECT_HIGHEST_WORKING_NODE", NULL} +#define STMT_9 \ "SELECT op_depth " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth < ?3 " \ @@ -106,17 +115,17 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_ACTUAL_NODE 9 -#define STMT_9_INFO {"STMT_SELECT_ACTUAL_NODE", NULL} -#define STMT_9 \ +#define STMT_SELECT_ACTUAL_NODE 10 +#define STMT_10_INFO {"STMT_SELECT_ACTUAL_NODE", NULL} +#define STMT_10 \ "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 \ +#define STMT_SELECT_NODE_CHILDREN_INFO 11 +#define STMT_11_INFO {"STMT_SELECT_NODE_CHILDREN_INFO", NULL} +#define STMT_11 \ "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, " \ @@ -125,51 +134,67 @@ "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 " \ + "ORDER BY local_relpath DESC, op_depth DESC " \ "" -#define STMT_SELECT_NODE_CHILDREN_WALKER_INFO 11 -#define STMT_11_INFO {"STMT_SELECT_NODE_CHILDREN_WALKER_INFO", NULL} -#define STMT_11 \ +#define STMT_SELECT_BASE_NODE_CHILDREN_INFO 12 +#define STMT_12_INFO {"STMT_SELECT_BASE_NODE_CHILDREN_INFO", NULL} +#define STMT_12 \ + "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 AND op_depth = 0 " \ + "ORDER BY local_relpath DESC " \ + "" + +#define STMT_SELECT_NODE_CHILDREN_WALKER_INFO 13 +#define STMT_13_INFO {"STMT_SELECT_NODE_CHILDREN_WALKER_INFO", NULL} +#define STMT_13 \ "SELECT local_relpath, op_depth, presence, kind " \ "FROM nodes_current " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_ACTUAL_CHILDREN_INFO 12 -#define STMT_12_INFO {"STMT_SELECT_ACTUAL_CHILDREN_INFO", NULL} -#define STMT_12 \ +#define STMT_SELECT_ACTUAL_CHILDREN_INFO 14 +#define STMT_14_INFO {"STMT_SELECT_ACTUAL_CHILDREN_INFO", NULL} +#define STMT_14 \ "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 \ +#define STMT_SELECT_REPOSITORY_BY_ID 15 +#define STMT_15_INFO {"STMT_SELECT_REPOSITORY_BY_ID", NULL} +#define STMT_15 \ "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 \ +#define STMT_SELECT_WCROOT_NULL 16 +#define STMT_16_INFO {"STMT_SELECT_WCROOT_NULL", NULL} +#define STMT_16 \ "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 \ +#define STMT_SELECT_REPOSITORY 17 +#define STMT_17_INFO {"STMT_SELECT_REPOSITORY", NULL} +#define STMT_17 \ "SELECT id FROM repository WHERE root = ?1 " \ "" -#define STMT_INSERT_REPOSITORY 16 -#define STMT_16_INFO {"STMT_INSERT_REPOSITORY", NULL} -#define STMT_16 \ +#define STMT_INSERT_REPOSITORY 18 +#define STMT_18_INFO {"STMT_INSERT_REPOSITORY", NULL} +#define STMT_18 \ "INSERT INTO repository (root, uuid) VALUES (?1, ?2) " \ "" -#define STMT_INSERT_NODE 17 -#define STMT_17_INFO {"STMT_INSERT_NODE", NULL} -#define STMT_17 \ +#define STMT_INSERT_NODE 19 +#define STMT_19_INFO {"STMT_INSERT_NODE", NULL} +#define STMT_19 \ "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, " \ @@ -180,22 +205,9 @@ " ?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 \ +#define STMT_SELECT_WORKING_PRESENT 20 +#define STMT_20_INFO {"STMT_SELECT_WORKING_PRESENT", NULL} +#define STMT_20 \ "SELECT local_relpath, kind, checksum, translated_size, last_mod_time " \ "FROM nodes n " \ "WHERE wc_id = ?1 " \ @@ -208,25 +220,25 @@ "ORDER BY local_relpath DESC " \ "" -#define STMT_DELETE_NODE_RECURSIVE 20 -#define STMT_20_INFO {"STMT_DELETE_NODE_RECURSIVE", NULL} -#define STMT_20 \ +#define STMT_DELETE_NODE_RECURSIVE 21 +#define STMT_21_INFO {"STMT_DELETE_NODE_RECURSIVE", NULL} +#define STMT_21 \ "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 \ +#define STMT_DELETE_NODE 22 +#define STMT_22_INFO {"STMT_DELETE_NODE", NULL} +#define STMT_22 \ "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 \ +#define STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE 23 +#define STMT_23_INFO {"STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE", NULL} +#define STMT_23 \ "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 " \ @@ -240,113 +252,161 @@ " 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 \ +#define STMT_DELETE_WORKING_BASE_DELETE 24 +#define STMT_24_INFO {"STMT_DELETE_WORKING_BASE_DELETE", 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)) " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND presence = 'base-deleted' " \ - " AND op_depth > 0 " \ + " AND op_depth > ?3 " \ " 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) " \ + " AND op_depth > ?3) " \ "" -#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_DELETE_WORKING_BASE_DELETE_RECURSIVE 25 +#define STMT_25_INFO {"STMT_DELETE_WORKING_BASE_DELETE_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 " \ + " AND presence = 'base-deleted' " \ + " AND op_depth > ?3 " \ + " 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 > ?3) " \ "" -#define STMT_DELETE_WORKING_OP_DEPTH 26 -#define STMT_26_INFO {"STMT_DELETE_WORKING_OP_DEPTH", NULL} +#define STMT_DELETE_WORKING_RECURSIVE 26 +#define STMT_26_INFO {"STMT_DELETE_WORKING_RECURSIVE", 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 " \ + "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_ABOVE 27 -#define STMT_27_INFO {"STMT_DELETE_WORKING_OP_DEPTH_ABOVE", NULL} +#define STMT_DELETE_BASE_RECURSIVE 27 +#define STMT_27_INFO {"STMT_DELETE_BASE_RECURSIVE", 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 " \ + "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 " \ "" -#define STMT_SELECT_LOCAL_RELPATH_OP_DEPTH 28 -#define STMT_28_INFO {"STMT_SELECT_LOCAL_RELPATH_OP_DEPTH", NULL} +#define STMT_DELETE_WORKING_OP_DEPTH 28 +#define STMT_28_INFO {"STMT_DELETE_WORKING_OP_DEPTH", NULL} #define STMT_28 \ - "SELECT local_relpath " \ - "FROM nodes " \ + "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_CHILDREN_OP_DEPTH 29 -#define STMT_29_INFO {"STMT_SELECT_CHILDREN_OP_DEPTH", NULL} +#define STMT_SELECT_LAYER_FOR_REPLACE 29 +#define STMT_29_INFO {"STMT_SELECT_LAYER_FOR_REPLACE", NULL} #define STMT_29 \ + "SELECT s.local_relpath, s.kind, " \ + " (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (s.local_relpath) WHEN (s.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (s.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (s.local_relpath) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN '' WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((s.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN (?4) WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((s.local_relpath), LENGTH(?2)+1) END END) drp, 'normal' " \ + "FROM nodes s " \ + "WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.kind, " \ + " (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (s.local_relpath) WHEN (s.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (s.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (s.local_relpath) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN '' WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((s.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN (?4) WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((s.local_relpath), LENGTH(?2)+1) END END) drp, d.presence " \ + "FROM nodes s " \ + "LEFT OUTER JOIN nodes d ON d.wc_id= ?1 AND d.op_depth = ?5 " \ + " AND d.local_relpath = drp " \ + "WHERE s.wc_id = ?1 " \ + " AND (((s.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((s.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND s.op_depth = ?3 " \ + "ORDER BY s.local_relpath " \ + "" + +#define STMT_SELECT_DESCENDANTS_OP_DEPTH_RV 30 +#define STMT_30_INFO {"STMT_SELECT_DESCENDANTS_OP_DEPTH_RV", NULL} +#define STMT_30 \ "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 " \ + " AND presence in ('normal', 'incomplete') " \ "ORDER BY local_relpath DESC " \ "" -#define STMT_COPY_NODE_MOVE 30 -#define STMT_30_INFO {"STMT_COPY_NODE_MOVE", NULL} -#define STMT_30 \ +#define STMT_COPY_NODE_MOVE 31 +#define STMT_31_INFO {"STMT_COPY_NODE_MOVE", NULL} +#define STMT_31 \ "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 \ + " s.wc_id, ?4 , ?5 , ?6 , " \ + " s.repos_id, " \ + " s.repos_path, s.revision, s.presence, s.depth, s.kind, s.changed_revision, " \ + " s.changed_date, s.changed_author, s.checksum, s.properties, " \ + " CASE WHEN d.checksum=s.checksum THEN d.translated_size END, " \ + " CASE WHEN d.checksum=s.checksum THEN d.last_mod_time END, " \ + " s.symlink_target, 1, d.moved_to " \ + "FROM nodes s " \ + "LEFT JOIN nodes d ON d.wc_id=?1 AND d.local_relpath=?4 AND d.op_depth=?5 " \ + "WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 " \ + "" + +#define STMT_SELECT_NO_LONGER_MOVED_RV 32 +#define STMT_32_INFO {"STMT_SELECT_NO_LONGER_MOVED_RV", NULL} +#define STMT_32 \ + "SELECT d.local_relpath, (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (d.local_relpath) WHEN (d.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (d.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (d.local_relpath) WHEN SUBSTR((d.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(d.local_relpath) THEN '' WHEN SUBSTR((d.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((d.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((d.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(d.local_relpath) THEN (?4) WHEN SUBSTR((d.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((d.local_relpath), LENGTH(?2)+1) END END) srp, " \ + " b.presence, b.op_depth " \ + "FROM nodes d " \ + "LEFT OUTER JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = d.local_relpath " \ + " AND b.op_depth = (SELECT MAX(x.op_depth) FROM nodes x " \ + " WHERE x.wc_id = ?1 " \ + " AND x.local_relpath = b.local_relpath " \ + " AND x.op_depth < ?3) " \ + "WHERE d.wc_id = ?1 " \ + " AND (((d.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((d.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND d.op_depth = ?3 " \ + " AND NOT EXISTS(SELECT * FROM nodes s " \ + " WHERE s.wc_id = ?1 " \ + " AND s.local_relpath = srp " \ + " AND s.op_depth = ?5) " \ + "ORDER BY d.local_relpath DESC " \ + "" + +#define STMT_SELECT_OP_DEPTH_CHILDREN 33 +#define STMT_33_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN", NULL} +#define STMT_33 \ "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 " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_GE_OP_DEPTH_CHILDREN 32 -#define STMT_32_INFO {"STMT_SELECT_GE_OP_DEPTH_CHILDREN", NULL} -#define STMT_32 \ +#define STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS 34 +#define STMT_34_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS", NULL} +#define STMT_34 \ + "SELECT local_relpath, kind FROM nodes " \ + "WHERE wc_id = ?1 " \ + " AND parent_relpath = ?2 " \ + " AND op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete') " \ + "ORDER BY local_relpath " \ + "" + +#define STMT_SELECT_GE_OP_DEPTH_CHILDREN 35 +#define STMT_35_INFO {"STMT_SELECT_GE_OP_DEPTH_CHILDREN", NULL} +#define STMT_35 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ - " AND (op_depth > ?3 OR (op_depth = ?3 AND presence != 'base-deleted')) " \ + " AND (op_depth > ?3 OR (op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete'))) " \ "UNION ALL " \ "SELECT 1 FROM ACTUAL_NODE a " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ @@ -354,9 +414,9 @@ " 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 \ +#define STMT_DELETE_SHADOWED_RECURSIVE 36 +#define STMT_36_INFO {"STMT_DELETE_SHADOWED_RECURSIVE", NULL} +#define STMT_36 \ "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)) " \ @@ -364,32 +424,33 @@ " 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 \ +#define STMT_CLEAR_MOVED_TO_FROM_DEST 37 +#define STMT_37_INFO {"STMT_CLEAR_MOVED_TO_FROM_DEST", NULL} +#define STMT_37 \ "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 \ +#define STMT_SELECT_NOT_PRESENT_DESCENDANTS 38 +#define STMT_38_INFO {"STMT_SELECT_NOT_PRESENT_DESCENDANTS", NULL} +#define STMT_38 \ "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 \ +#define STMT_COMMIT_DESCENDANTS_TO_BASE 39 +#define STMT_39_INFO {"STMT_COMMIT_DESCENDANTS_TO_BASE", NULL} +#define STMT_39 \ "UPDATE NODES SET op_depth = 0, " \ " repos_id = ?4, " \ - " repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), " \ + " repos_path = (CASE WHEN (?2) = '' THEN (CASE WHEN (?5) = '' THEN (local_relpath) WHEN (local_relpath) = '' THEN (?5) ELSE (?5) || '/' || (local_relpath) END) WHEN (?5) = '' 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 (?5) WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?5) || SUBSTR((local_relpath), LENGTH(?2)+1) END END), " \ " revision = ?6, " \ " dav_cache = NULL, " \ " moved_here = NULL, " \ + " moved_to = NULL, " \ " presence = CASE presence " \ " WHEN 'normal' THEN 'normal' " \ " WHEN 'excluded' THEN 'excluded' " \ @@ -400,67 +461,78 @@ " 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 " \ +#define STMT_SELECT_NODE_CHILDREN 40 +#define STMT_40_INFO {"STMT_SELECT_NODE_CHILDREN", NULL} +#define STMT_40 \ + "SELECT DISTINCT local_relpath FROM nodes " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_WORKING_CHILDREN 38 -#define STMT_38_INFO {"STMT_SELECT_WORKING_CHILDREN", NULL} -#define STMT_38 \ - "SELECT local_relpath FROM nodes " \ +#define STMT_SELECT_WORKING_CHILDREN 41 +#define STMT_41_INFO {"STMT_SELECT_WORKING_CHILDREN", NULL} +#define STMT_41 \ + "SELECT DISTINCT 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')) " \ + " AND presence IN ('normal', 'incomplete'))) " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_NODE_PROPS 39 -#define STMT_39_INFO {"STMT_SELECT_NODE_PROPS", NULL} -#define STMT_39 \ +#define STMT_SELECT_BASE_NOT_PRESENT_CHILDREN 42 +#define STMT_42_INFO {"STMT_SELECT_BASE_NOT_PRESENT_CHILDREN", NULL} +#define STMT_42 \ + "SELECT local_relpath FROM nodes " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ + " AND presence = 'not-present' " \ + "ORDER BY local_relpath " \ + "" + +#define STMT_SELECT_NODE_PROPS 43 +#define STMT_43_INFO {"STMT_SELECT_NODE_PROPS", NULL} +#define STMT_43 \ "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 \ +#define STMT_SELECT_ACTUAL_PROPS 44 +#define STMT_44_INFO {"STMT_SELECT_ACTUAL_PROPS", NULL} +#define STMT_44 \ "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 \ +#define STMT_UPDATE_ACTUAL_PROPS 45 +#define STMT_45_INFO {"STMT_UPDATE_ACTUAL_PROPS", NULL} +#define STMT_45 \ "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 \ +#define STMT_INSERT_ACTUAL_PROPS 46 +#define STMT_46_INFO {"STMT_INSERT_ACTUAL_PROPS", NULL} +#define STMT_46 \ "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 \ +#define STMT_INSERT_LOCK 47 +#define STMT_47_INFO {"STMT_INSERT_LOCK", NULL} +#define STMT_47 \ "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 \ +#define STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE 48 +#define STMT_48_INFO {"STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE", NULL} +#define STMT_48 \ "SELECT nodes.repos_id, nodes.repos_path, lock_token " \ "FROM nodes " \ "LEFT JOIN lock ON nodes.repos_id = lock.repos_id " \ @@ -469,93 +541,73 @@ " 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 \ +#define STMT_INSERT_WCROOT 49 +#define STMT_49_INFO {"STMT_INSERT_WCROOT", NULL} +#define STMT_49 \ "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 \ +#define STMT_UPDATE_BASE_NODE_DAV_CACHE 50 +#define STMT_50_INFO {"STMT_UPDATE_BASE_NODE_DAV_CACHE", NULL} +#define STMT_50 \ "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 \ +#define STMT_SELECT_BASE_DAV_CACHE 51 +#define STMT_51_INFO {"STMT_SELECT_BASE_DAV_CACHE", NULL} +#define STMT_51 \ "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 " \ +#define STMT_SELECT_DELETION_INFO 52 +#define STMT_52_INFO {"STMT_SELECT_DELETION_INFO", NULL} +#define STMT_52 \ + "SELECT b.presence, w.presence, w.op_depth, w.moved_to " \ + "FROM nodes w " \ + "LEFT JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0 " \ + "WHERE w.wc_id = ?1 AND w.local_relpath = ?2 " \ + " AND w.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id = ?1 AND d.local_relpath = ?2 " \ + " AND d.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 \ +#define STMT_SELECT_MOVED_TO_NODE 53 +#define STMT_53_INFO {"STMT_SELECT_MOVED_TO_NODE", NULL} +#define STMT_53 \ "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 " \ +#define STMT_SELECT_OP_DEPTH_MOVED_TO 54 +#define STMT_54_INFO {"STMT_SELECT_OP_DEPTH_MOVED_TO", NULL} +#define STMT_54 \ + "SELECT op_depth, moved_to " \ "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 " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \ + " AND EXISTS(SELECT * from nodes " \ + " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete')) " \ + "ORDER BY op_depth ASC " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_MOVED_TO 52 -#define STMT_52_INFO {"STMT_SELECT_MOVED_TO", NULL} -#define STMT_52 \ +#define STMT_SELECT_MOVED_TO 55 +#define STMT_55_INFO {"STMT_SELECT_MOVED_TO", NULL} +#define STMT_55 \ "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 \ +#define STMT_SELECT_MOVED_BACK 56 +#define STMT_56_INFO {"STMT_SELECT_MOVED_BACK", NULL} +#define STMT_56 \ "SELECT u.local_relpath, " \ " u.presence, u.repos_id, u.repos_path, u.revision, " \ " l.presence, l.repos_id, l.repos_path, l.revision, " \ @@ -581,81 +633,71 @@ " 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 \ +#define STMT_DELETE_LOCK 57 +#define STMT_57_INFO {"STMT_DELETE_LOCK", NULL} +#define STMT_57 \ "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 \ +#define STMT_DELETE_LOCK_RECURSIVELY 58 +#define STMT_58_INFO {"STMT_DELETE_LOCK_RECURSIVELY", NULL} +#define STMT_58 \ "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 \ +#define STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE 59 +#define STMT_59_INFO {"STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE", NULL} +#define STMT_59 \ "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 \ +#define STMT_RECURSIVE_UPDATE_NODE_REPO 60 +#define STMT_60_INFO {"STMT_RECURSIVE_UPDATE_NODE_REPO", NULL} +#define STMT_60 \ "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 \ +#define STMT_UPDATE_LOCK_REPOS_ID 61 +#define STMT_61_INFO {"STMT_UPDATE_LOCK_REPOS_ID", NULL} +#define STMT_61 \ "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 \ +#define STMT_UPDATE_NODE_FILEINFO 62 +#define STMT_62_INFO {"STMT_UPDATE_NODE_FILEINFO", NULL} +#define STMT_62 \ "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 \ +#define STMT_INSERT_ACTUAL_CONFLICT 63 +#define STMT_63_INFO {"STMT_INSERT_ACTUAL_CONFLICT", NULL} +#define STMT_63 \ "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 \ +#define STMT_UPDATE_ACTUAL_CONFLICT 64 +#define STMT_64_INFO {"STMT_UPDATE_ACTUAL_CONFLICT", NULL} +#define STMT_64 \ "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 \ +#define STMT_UPDATE_ACTUAL_CHANGELISTS 65 +#define STMT_65_INFO {"STMT_UPDATE_ACTUAL_CHANGELISTS", NULL} +#define STMT_65 \ "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))) " \ @@ -665,16 +707,16 @@ " AND kind = 'file') " \ "" -#define STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST 65 -#define STMT_65_INFO {"STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST", NULL} -#define STMT_65 \ +#define STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST 66 +#define STMT_66_INFO {"STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST", NULL} +#define STMT_66 \ "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 \ +#define STMT_MARK_SKIPPED_CHANGELIST_DIRS 67 +#define STMT_67_INFO {"STMT_MARK_SKIPPED_CHANGELIST_DIRS", NULL} +#define STMT_67 \ "INSERT INTO changelist_list (wc_id, local_relpath, notify, changelist) " \ "SELECT wc_id, local_relpath, 7, ?3 " \ "FROM targets_list " \ @@ -683,17 +725,17 @@ " AND kind = 'dir' " \ "" -#define STMT_RESET_ACTUAL_WITH_CHANGELIST 67 -#define STMT_67_INFO {"STMT_RESET_ACTUAL_WITH_CHANGELIST", NULL} -#define STMT_67 \ +#define STMT_RESET_ACTUAL_WITH_CHANGELIST 68 +#define STMT_68_INFO {"STMT_RESET_ACTUAL_WITH_CHANGELIST", NULL} +#define STMT_68 \ "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 \ +#define STMT_CREATE_CHANGELIST_LIST 69 +#define STMT_69_INFO {"STMT_CREATE_CHANGELIST_LIST", NULL} +#define STMT_69 \ "DROP TABLE IF EXISTS changelist_list; " \ "CREATE TEMPORARY TABLE changelist_list ( " \ " wc_id INTEGER NOT NULL, " \ @@ -704,9 +746,9 @@ ") " \ "" -#define STMT_CREATE_CHANGELIST_TRIGGER 69 -#define STMT_69_INFO {"STMT_CREATE_CHANGELIST_TRIGGER", NULL} -#define STMT_69 \ +#define STMT_CREATE_CHANGELIST_TRIGGER 70 +#define STMT_70_INFO {"STMT_CREATE_CHANGELIST_TRIGGER", NULL} +#define STMT_70 \ "DROP TRIGGER IF EXISTS trigger_changelist_list_change; " \ "CREATE TEMPORARY TRIGGER trigger_changelist_list_change " \ "BEFORE UPDATE ON actual_node " \ @@ -721,25 +763,25 @@ "END " \ "" -#define STMT_FINALIZE_CHANGELIST 70 -#define STMT_70_INFO {"STMT_FINALIZE_CHANGELIST", NULL} -#define STMT_70 \ +#define STMT_FINALIZE_CHANGELIST 71 +#define STMT_71_INFO {"STMT_FINALIZE_CHANGELIST", NULL} +#define STMT_71 \ "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 \ +#define STMT_SELECT_CHANGELIST_LIST 72 +#define STMT_72_INFO {"STMT_SELECT_CHANGELIST_LIST", NULL} +#define STMT_72 \ "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 \ +#define STMT_CREATE_TARGETS_LIST 73 +#define STMT_73_INFO {"STMT_CREATE_TARGETS_LIST", NULL} +#define STMT_73 \ "DROP TABLE IF EXISTS targets_list; " \ "CREATE TEMPORARY TABLE targets_list ( " \ " wc_id INTEGER NOT NULL, " \ @@ -750,15 +792,15 @@ " ); " \ "" -#define STMT_DROP_TARGETS_LIST 73 -#define STMT_73_INFO {"STMT_DROP_TARGETS_LIST", NULL} -#define STMT_73 \ +#define STMT_DROP_TARGETS_LIST 74 +#define STMT_74_INFO {"STMT_DROP_TARGETS_LIST", NULL} +#define STMT_74 \ "DROP TABLE targets_list " \ "" -#define STMT_INSERT_TARGET 74 -#define STMT_74_INFO {"STMT_INSERT_TARGET", NULL} -#define STMT_74 \ +#define STMT_INSERT_TARGET 75 +#define STMT_75_INFO {"STMT_INSERT_TARGET", 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 " \ @@ -766,9 +808,9 @@ " AND local_relpath = ?2 " \ "" -#define STMT_INSERT_TARGET_DEPTH_FILES 75 -#define STMT_75_INFO {"STMT_INSERT_TARGET_DEPTH_FILES", NULL} -#define STMT_75 \ +#define STMT_INSERT_TARGET_DEPTH_FILES 76 +#define STMT_76_INFO {"STMT_INSERT_TARGET_DEPTH_FILES", 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 " \ @@ -777,9 +819,9 @@ " AND kind = 'file' " \ "" -#define STMT_INSERT_TARGET_DEPTH_IMMEDIATES 76 -#define STMT_76_INFO {"STMT_INSERT_TARGET_DEPTH_IMMEDIATES", NULL} -#define STMT_76 \ +#define STMT_INSERT_TARGET_DEPTH_IMMEDIATES 77 +#define STMT_77_INFO {"STMT_INSERT_TARGET_DEPTH_IMMEDIATES", 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 " \ @@ -787,9 +829,9 @@ " AND parent_relpath = ?2 " \ "" -#define STMT_INSERT_TARGET_DEPTH_INFINITY 77 -#define STMT_77_INFO {"STMT_INSERT_TARGET_DEPTH_INFINITY", NULL} -#define STMT_77 \ +#define STMT_INSERT_TARGET_DEPTH_INFINITY 78 +#define STMT_78_INFO {"STMT_INSERT_TARGET_DEPTH_INFINITY", NULL} +#define STMT_78 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT wc_id, local_relpath, parent_relpath, kind " \ "FROM nodes_current " \ @@ -797,9 +839,9 @@ " 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 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST 79 +#define STMT_79_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST", 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 " \ @@ -809,9 +851,9 @@ " 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 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES 80 +#define STMT_80_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES", 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 " \ @@ -822,9 +864,9 @@ " 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 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES 81 +#define STMT_81_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES", 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 " \ @@ -834,9 +876,9 @@ " 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 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY 82 +#define STMT_82_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY", NULL} +#define STMT_82 \ "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 " \ @@ -846,18 +888,28 @@ " AND A.changelist = ?3 " \ "" -#define STMT_INSERT_ACTUAL_EMPTIES 82 -#define STMT_82_INFO {"STMT_INSERT_ACTUAL_EMPTIES", NULL} -#define STMT_82 \ +#define STMT_INSERT_ACTUAL_EMPTIES 83 +#define STMT_83_INFO {"STMT_INSERT_ACTUAL_EMPTIES", NULL} +#define STMT_83 \ "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 \ +#define STMT_INSERT_ACTUAL_EMPTIES_FILES 84 +#define STMT_84_INFO {"STMT_INSERT_ACTUAL_EMPTIES_FILES", NULL} +#define STMT_84 \ + "INSERT OR IGNORE INTO actual_node ( " \ + " wc_id, local_relpath, parent_relpath) " \ + "SELECT wc_id, local_relpath, parent_relpath " \ + "FROM targets_list " \ + "WHERE kind='file' " \ + "" + +#define STMT_DELETE_ACTUAL_EMPTY 85 +#define STMT_85_INFO {"STMT_DELETE_ACTUAL_EMPTY", NULL} +#define STMT_85 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND properties IS NULL " \ @@ -869,12 +921,12 @@ " AND left_checksum IS NULL " \ "" -#define STMT_DELETE_ACTUAL_EMPTIES 84 -#define STMT_84_INFO {"STMT_DELETE_ACTUAL_EMPTIES", NULL} -#define STMT_84 \ +#define STMT_DELETE_ACTUAL_EMPTIES 86 +#define STMT_86_INFO {"STMT_DELETE_ACTUAL_EMPTIES", NULL} +#define STMT_86 \ "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 (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 NULL " \ " AND conflict_data IS NULL " \ " AND changelist IS NULL " \ @@ -884,25 +936,25 @@ " AND left_checksum IS NULL " \ "" -#define STMT_DELETE_BASE_NODE 85 -#define STMT_85_INFO {"STMT_DELETE_BASE_NODE", NULL} -#define STMT_85 \ +#define STMT_DELETE_BASE_NODE 87 +#define STMT_87_INFO {"STMT_DELETE_BASE_NODE", NULL} +#define STMT_87 \ "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 \ +#define STMT_DELETE_WORKING_NODE 88 +#define STMT_88_INFO {"STMT_DELETE_WORKING_NODE", NULL} +#define STMT_88 \ "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 \ +#define STMT_DELETE_LOWEST_WORKING_NODE 89 +#define STMT_89_INFO {"STMT_DELETE_LOWEST_WORKING_NODE", NULL} +#define STMT_89 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND op_depth = (SELECT MIN(op_depth) FROM nodes " \ @@ -910,16 +962,16 @@ " AND presence = 'base-deleted' " \ "" -#define STMT_DELETE_NODE_ALL_LAYERS 88 -#define STMT_88_INFO {"STMT_DELETE_NODE_ALL_LAYERS", NULL} -#define STMT_88 \ +#define STMT_DELETE_NODE_ALL_LAYERS 90 +#define STMT_90_INFO {"STMT_DELETE_NODE_ALL_LAYERS", NULL} +#define STMT_90 \ "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 \ +#define STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE 91 +#define STMT_91_INFO {"STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE", NULL} +#define STMT_91 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -927,25 +979,25 @@ " AND op_depth >= ?3 " \ "" -#define STMT_DELETE_ACTUAL_NODE 90 -#define STMT_90_INFO {"STMT_DELETE_ACTUAL_NODE", NULL} -#define STMT_90 \ +#define STMT_DELETE_ACTUAL_NODE 92 +#define STMT_92_INFO {"STMT_DELETE_ACTUAL_NODE", NULL} +#define STMT_92 \ "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 \ +#define STMT_DELETE_ACTUAL_NODE_RECURSIVE 93 +#define STMT_93_INFO {"STMT_DELETE_ACTUAL_NODE_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))) " \ "" -#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST 92 -#define STMT_92_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} -#define STMT_92 \ +#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST 94 +#define STMT_94_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} +#define STMT_94 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND local_relpath = ?2 " \ @@ -955,9 +1007,9 @@ " 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 \ +#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 95 +#define STMT_95_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} +#define STMT_95 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -969,9 +1021,9 @@ " 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 \ +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST 96 +#define STMT_96_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} +#define STMT_96 \ "UPDATE actual_node " \ "SET properties = NULL, " \ " text_mod = NULL, " \ @@ -983,9 +1035,23 @@ "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 \ +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT 97 +#define STMT_97_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT", NULL} +#define STMT_97 \ + "UPDATE actual_node " \ + "SET properties = NULL, " \ + " text_mod = NULL, " \ + " tree_conflict_data = NULL, " \ + " older_checksum = NULL, " \ + " left_checksum = NULL, " \ + " right_checksum = NULL, " \ + " changelist = NULL " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 " \ + "" + +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 98 +#define STMT_98_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} +#define STMT_98 \ "UPDATE actual_node " \ "SET properties = NULL, " \ " text_mod = NULL, " \ @@ -999,108 +1065,109 @@ " 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 \ +#define STMT_UPDATE_NODE_BASE_DEPTH 99 +#define STMT_99_INFO {"STMT_UPDATE_NODE_BASE_DEPTH", NULL} +#define STMT_99 \ "UPDATE nodes SET depth = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ " AND kind='dir' " \ + " AND presence IN ('normal', 'incomplete') " \ "" -#define STMT_UPDATE_NODE_BASE_PRESENCE 97 -#define STMT_97_INFO {"STMT_UPDATE_NODE_BASE_PRESENCE", NULL} -#define STMT_97 \ +#define STMT_UPDATE_NODE_BASE_PRESENCE 100 +#define STMT_100_INFO {"STMT_UPDATE_NODE_BASE_PRESENCE", NULL} +#define STMT_100 \ "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 \ +#define STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH 101 +#define STMT_101_INFO {"STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH", NULL} +#define STMT_101 \ "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 \ +#define STMT_LOOK_FOR_WORK 102 +#define STMT_102_INFO {"STMT_LOOK_FOR_WORK", NULL} +#define STMT_102 \ "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 \ +#define STMT_INSERT_WORK_ITEM 103 +#define STMT_103_INFO {"STMT_INSERT_WORK_ITEM", NULL} +#define STMT_103 \ "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 \ +#define STMT_SELECT_WORK_ITEM 104 +#define STMT_104_INFO {"STMT_SELECT_WORK_ITEM", NULL} +#define STMT_104 \ "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 \ +#define STMT_DELETE_WORK_ITEM 105 +#define STMT_105_INFO {"STMT_DELETE_WORK_ITEM", NULL} +#define STMT_105 \ "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 \ +#define STMT_INSERT_OR_IGNORE_PRISTINE 106 +#define STMT_106_INFO {"STMT_INSERT_OR_IGNORE_PRISTINE", NULL} +#define STMT_106 \ "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 \ +#define STMT_INSERT_PRISTINE 107 +#define STMT_107_INFO {"STMT_INSERT_PRISTINE", NULL} +#define STMT_107 \ "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 \ +#define STMT_SELECT_PRISTINE 108 +#define STMT_108_INFO {"STMT_SELECT_PRISTINE", NULL} +#define STMT_108 \ "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 \ +#define STMT_SELECT_PRISTINE_SIZE 109 +#define STMT_109_INFO {"STMT_SELECT_PRISTINE_SIZE", NULL} +#define STMT_109 \ "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 \ +#define STMT_SELECT_PRISTINE_BY_MD5 110 +#define STMT_110_INFO {"STMT_SELECT_PRISTINE_BY_MD5", NULL} +#define STMT_110 \ "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 \ +#define STMT_SELECT_UNREFERENCED_PRISTINES 111 +#define STMT_111_INFO {"STMT_SELECT_UNREFERENCED_PRISTINES", NULL} +#define STMT_111 \ "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 \ +#define STMT_DELETE_PRISTINE_IF_UNREFERENCED 112 +#define STMT_112_INFO {"STMT_DELETE_PRISTINE_IF_UNREFERENCED", NULL} +#define STMT_112 \ "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 \ +#define STMT_SELECT_COPY_PRISTINES 113 +#define STMT_113_INFO {"STMT_SELECT_COPY_PRISTINES", NULL} +#define STMT_113 \ "SELECT n.checksum, md5_checksum, size " \ "FROM nodes_current n " \ "LEFT JOIN pristine p ON n.checksum = p.checksum " \ @@ -1118,62 +1185,73 @@ " AND n.checksum IS NOT NULL " \ "" -#define STMT_VACUUM 111 -#define STMT_111_INFO {"STMT_VACUUM", NULL} -#define STMT_111 \ +#define STMT_VACUUM 114 +#define STMT_114_INFO {"STMT_VACUUM", NULL} +#define STMT_114 \ "VACUUM " \ "" -#define STMT_SELECT_CONFLICT_VICTIMS 112 -#define STMT_112_INFO {"STMT_SELECT_CONFLICT_VICTIMS", NULL} -#define STMT_112 \ +#define STMT_SELECT_CONFLICT_VICTIMS 115 +#define STMT_115_INFO {"STMT_SELECT_CONFLICT_VICTIMS", NULL} +#define STMT_115 \ "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 \ +#define STMT_INSERT_WC_LOCK 116 +#define STMT_116_INFO {"STMT_INSERT_WC_LOCK", NULL} +#define STMT_116 \ "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 \ +#define STMT_SELECT_WC_LOCK 117 +#define STMT_117_INFO {"STMT_SELECT_WC_LOCK", NULL} +#define STMT_117 \ "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 \ +#define STMT_SELECT_ANCESTOR_WCLOCKS 118 +#define STMT_118_INFO {"STMT_SELECT_ANCESTOR_WCLOCKS", NULL} +#define STMT_118 \ "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 \ +#define STMT_DELETE_WC_LOCK 119 +#define STMT_119_INFO {"STMT_DELETE_WC_LOCK", NULL} +#define STMT_119 \ "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 \ +#define STMT_FIND_WC_LOCK 120 +#define STMT_120_INFO {"STMT_FIND_WC_LOCK", NULL} +#define STMT_120 \ "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 \ +#define STMT_FIND_CONFLICT_DESCENDANT 121 +#define STMT_121_INFO {"STMT_FIND_CONFLICT_DESCENDANT", NULL} +#define STMT_121 \ + "SELECT 1 FROM actual_node " \ + "WHERE wc_id = ?1 " \ + " AND local_relpath > (?2 || '/') " \ + " AND local_relpath < (?2 || '0') " \ + " AND conflict_data IS NOT NULL " \ + "LIMIT 1 " \ + "" + +#define STMT_DELETE_WC_LOCK_ORPHAN 122 +#define STMT_122_INFO {"STMT_DELETE_WC_LOCK_ORPHAN", NULL} +#define STMT_122 \ "DELETE FROM wc_lock " \ "WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \ "AND NOT EXISTS (SELECT 1 FROM nodes " \ @@ -1181,9 +1259,9 @@ " 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 \ +#define STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE 123 +#define STMT_123_INFO {"STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE", NULL} +#define STMT_123 \ "DELETE FROM wc_lock " \ "WHERE wc_id = ?1 " \ " AND (local_dir_relpath = ?2 " \ @@ -1193,9 +1271,9 @@ " 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 \ +#define STMT_APPLY_CHANGES_TO_BASE_NODE 124 +#define STMT_124_INFO {"STMT_APPLY_CHANGES_TO_BASE_NODE", NULL} +#define STMT_124 \ "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, " \ @@ -1209,77 +1287,70 @@ " 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 ( " \ +#define STMT_INSTALL_WORKING_NODE_FOR_DELETE 125 +#define STMT_125_INFO {"STMT_INSTALL_WORKING_NODE_FOR_DELETE", NULL} +#define STMT_125 \ + "INSERT 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 \ +#define STMT_REPLACE_WITH_BASE_DELETED 126 +#define STMT_126_INFO {"STMT_REPLACE_WITH_BASE_DELETED", NULL} +#define STMT_126 \ "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 local_relpath = ?2 " \ " 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 \ +#define STMT_INSERT_DELETE_FROM_NODE_RECURSIVE 127 +#define STMT_127_INFO {"STMT_INSERT_DELETE_FROM_NODE_RECURSIVE", NULL} +#define STMT_127 \ "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 AND op_depth = ?3 " \ + "UNION ALL " \ + "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 (((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 " \ + "ORDER BY local_relpath " \ "" -#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 ( " \ +#define STMT_INSERT_WORKING_NODE_FROM_BASE_COPY 128 +#define STMT_128_INFO {"STMT_INSERT_WORKING_NODE_FROM_BASE_COPY", NULL} +#define STMT_128 \ + "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 ) " \ + " symlink_target, moved_to ) " \ "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 " \ + " last_mod_time, symlink_target, " \ + " (SELECT moved_to FROM nodes " \ + " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to " \ "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 \ +#define STMT_INSERT_DELETE_FROM_BASE 129 +#define STMT_129_INFO {"STMT_INSERT_DELETE_FROM_BASE", NULL} +#define STMT_129 \ "INSERT INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, presence, kind) " \ "SELECT wc_id, local_relpath, ?3 , parent_relpath, " \ @@ -1288,34 +1359,52 @@ "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 \ +#define STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE 130 +#define STMT_130_INFO {"STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE", NULL} +#define STMT_130 \ "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_COPY_OP_DEPTH_RECURSIVE 131 +#define STMT_131_INFO {"STMT_COPY_OP_DEPTH_RECURSIVE", NULL} +#define STMT_131 \ + "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, moved_here, moved_to ) " \ + "SELECT " \ + " wc_id, local_relpath, ?4, 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, NULL, NULL " \ + "FROM nodes " \ + "WHERE wc_id = ?1 AND op_depth = ?3 AND local_relpath = ?2 " \ + "UNION ALL " \ + "SELECT " \ + " wc_id, local_relpath, ?4, 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, NULL, NULL " \ + "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)) " \ + "ORDER BY local_relpath " \ "" -#define STMT_DOES_NODE_EXIST 129 -#define STMT_129_INFO {"STMT_DOES_NODE_EXIST", NULL} -#define STMT_129 \ +#define STMT_DOES_NODE_EXIST 132 +#define STMT_132_INFO {"STMT_DOES_NODE_EXIST", NULL} +#define STMT_132 \ "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 \ +#define STMT_HAS_SERVER_EXCLUDED_DESCENDANTS 133 +#define STMT_133_INFO {"STMT_HAS_SERVER_EXCLUDED_DESCENDANTS", NULL} +#define STMT_133 \ "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)) " \ @@ -1323,9 +1412,9 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_ALL_EXCLUDED_DESCENDANTS 131 -#define STMT_131_INFO {"STMT_SELECT_ALL_EXCLUDED_DESCENDANTS", NULL} -#define STMT_131 \ +#define STMT_SELECT_ALL_EXCLUDED_DESCENDANTS 134 +#define STMT_134_INFO {"STMT_SELECT_ALL_EXCLUDED_DESCENDANTS", NULL} +#define STMT_134 \ "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)) " \ @@ -1333,9 +1422,9 @@ " 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 \ +#define STMT_INSERT_WORKING_NODE_COPY_FROM 135 +#define STMT_135_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM", NULL} +#define STMT_135 \ "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, " \ @@ -1354,9 +1443,9 @@ "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 \ +#define STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH 136 +#define STMT_136_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH", NULL} +#define STMT_136 \ "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, " \ @@ -1375,49 +1464,49 @@ "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 \ +#define STMT_UPDATE_BASE_REVISION 137 +#define STMT_137_INFO {"STMT_UPDATE_BASE_REVISION", NULL} +#define STMT_137 \ "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 \ +#define STMT_UPDATE_BASE_REPOS 138 +#define STMT_138_INFO {"STMT_UPDATE_BASE_REPOS", NULL} +#define STMT_138 \ "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 \ +#define STMT_ACTUAL_HAS_CHILDREN 139 +#define STMT_139_INFO {"STMT_ACTUAL_HAS_CHILDREN", NULL} +#define STMT_139 \ "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 \ +#define STMT_INSERT_EXTERNAL 140 +#define STMT_140_INFO {"STMT_INSERT_EXTERNAL", NULL} +#define STMT_140 \ "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 \ +#define STMT_SELECT_EXTERNAL_INFO 141 +#define STMT_141_INFO {"STMT_SELECT_EXTERNAL_INFO", NULL} +#define STMT_141 \ "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 \ +#define STMT_DELETE_FILE_EXTERNALS 142 +#define STMT_142_INFO {"STMT_DELETE_FILE_EXTERNALS", NULL} +#define STMT_142 \ "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)) " \ @@ -1425,26 +1514,26 @@ " 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 \ +#define STMT_DELETE_FILE_EXTERNAL_REGISTATIONS 143 +#define STMT_143_INFO {"STMT_DELETE_FILE_EXTERNAL_REGISTATIONS", NULL} +#define STMT_143 \ "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 \ +#define STMT_DELETE_EXTERNAL_REGISTATIONS 144 +#define STMT_144_INFO {"STMT_DELETE_EXTERNAL_REGISTATIONS", NULL} +#define STMT_144 \ "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 \ +#define STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW 145 +#define STMT_145_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW", NULL} +#define STMT_145 \ "SELECT local_relpath, kind, def_repos_relpath, " \ " (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \ "FROM externals e " \ @@ -1462,9 +1551,9 @@ " 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 \ +#define STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW 146 +#define STMT_146_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW", NULL} +#define STMT_146 \ "SELECT local_relpath, kind, def_repos_relpath, " \ " (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \ "FROM externals e " \ @@ -1483,25 +1572,25 @@ " 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 \ +#define STMT_SELECT_EXTERNALS_DEFINED 147 +#define STMT_147_INFO {"STMT_SELECT_EXTERNALS_DEFINED", NULL} +#define STMT_147 \ "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 \ +#define STMT_DELETE_EXTERNAL 148 +#define STMT_148_INFO {"STMT_DELETE_EXTERNAL", NULL} +#define STMT_148 \ "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 \ +#define STMT_SELECT_EXTERNAL_PROPERTIES 149 +#define STMT_149_INFO {"STMT_SELECT_EXTERNAL_PROPERTIES", NULL} +#define STMT_149 \ "SELECT IFNULL((SELECT properties FROM actual_node a " \ " WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \ " properties), " \ @@ -1519,9 +1608,9 @@ " 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 \ +#define STMT_SELECT_CURRENT_PROPS_RECURSIVE 150 +#define STMT_150_INFO {"STMT_SELECT_CURRENT_PROPS_RECURSIVE", NULL} +#define STMT_150 \ "SELECT IFNULL((SELECT properties FROM actual_node a " \ " WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \ " properties), " \ @@ -1531,57 +1620,44 @@ " 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_PRAGMA_LOCKING_MODE 151 +#define STMT_151_INFO {"STMT_PRAGMA_LOCKING_MODE", NULL} +#define STMT_151 \ + "PRAGMA locking_mode = exclusive; " \ + "PRAGMA journal_mode = DELETE " \ "" -#define STMT_INSERT_ACTUAL_NODE 149 -#define STMT_149_INFO {"STMT_INSERT_ACTUAL_NODE", NULL} -#define STMT_149 \ +#define STMT_INSERT_ACTUAL_NODE 152 +#define STMT_152_INFO {"STMT_INSERT_ACTUAL_NODE", NULL} +#define STMT_152 \ "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 \ +#define STMT_SELECT_ALL_FILES 153 +#define STMT_153_INFO {"STMT_SELECT_ALL_FILES", NULL} +#define STMT_153 \ "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 \ +#define STMT_UPDATE_NODE_PROPS 154 +#define STMT_154_INFO {"STMT_UPDATE_NODE_PROPS", NULL} +#define STMT_154 \ "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 \ +#define STMT_PRAGMA_TABLE_INFO_NODES 155 +#define STMT_155_INFO {"STMT_PRAGMA_TABLE_INFO_NODES", NULL} +#define STMT_155 \ "PRAGMA table_info(\"NODES\") " \ "" -#define STMT_CREATE_TARGET_PROP_CACHE 155 -#define STMT_155_INFO {"STMT_CREATE_TARGET_PROP_CACHE", NULL} -#define STMT_155 \ +#define STMT_CREATE_TARGET_PROP_CACHE 156 +#define STMT_156_INFO {"STMT_CREATE_TARGET_PROP_CACHE", NULL} +#define STMT_156 \ "DROP TABLE IF EXISTS target_prop_cache; " \ "CREATE TEMPORARY TABLE target_prop_cache ( " \ " local_relpath TEXT NOT NULL PRIMARY KEY, " \ @@ -1590,9 +1666,9 @@ "); " \ "" -#define STMT_CACHE_TARGET_PROPS 156 -#define STMT_156_INFO {"STMT_CACHE_TARGET_PROPS", NULL} -#define STMT_156 \ +#define STMT_CACHE_TARGET_PROPS 157 +#define STMT_157_INFO {"STMT_CACHE_TARGET_PROPS", NULL} +#define STMT_157 \ "INSERT INTO target_prop_cache(local_relpath, kind, properties) " \ " SELECT n.local_relpath, n.kind, " \ " IFNULL((SELECT properties FROM actual_node AS a " \ @@ -1611,9 +1687,9 @@ " 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 \ +#define STMT_CACHE_TARGET_PRISTINE_PROPS 158 +#define STMT_158_INFO {"STMT_CACHE_TARGET_PRISTINE_PROPS", NULL} +#define STMT_158 \ "INSERT INTO target_prop_cache(local_relpath, kind, properties) " \ " SELECT n.local_relpath, n.kind, " \ " CASE n.presence " \ @@ -1638,22 +1714,22 @@ " 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 \ +#define STMT_SELECT_ALL_TARGET_PROP_CACHE 159 +#define STMT_159_INFO {"STMT_SELECT_ALL_TARGET_PROP_CACHE", NULL} +#define STMT_159 \ "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 \ +#define STMT_DROP_TARGET_PROP_CACHE 160 +#define STMT_160_INFO {"STMT_DROP_TARGET_PROP_CACHE", NULL} +#define STMT_160 \ "DROP TABLE target_prop_cache; " \ "" -#define STMT_CREATE_REVERT_LIST 160 -#define STMT_160_INFO {"STMT_CREATE_REVERT_LIST", NULL} -#define STMT_160 \ +#define STMT_CREATE_REVERT_LIST 161 +#define STMT_161_INFO {"STMT_CREATE_REVERT_LIST", NULL} +#define STMT_161 \ "DROP TABLE IF EXISTS revert_list; " \ "CREATE TEMPORARY TABLE revert_list ( " \ " local_relpath TEXT NOT NULL, " \ @@ -1687,8 +1763,9 @@ " WHERE n.wc_id = OLD.wc_id " \ " AND n.local_relpath = OLD.local_relpath) " \ " THEN 1 " \ - " ELSE NULL " \ - " END; " \ + " END notify " \ + " WHERE OLD.conflict_data IS NOT NULL " \ + " OR notify IS NOT NULL; " \ "END; " \ "DROP TRIGGER IF EXISTS trigger_revert_list_actual_update; " \ "CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update " \ @@ -1704,31 +1781,32 @@ " WHERE n.wc_id = OLD.wc_id " \ " AND n.local_relpath = OLD.local_relpath) " \ " THEN 1 " \ - " ELSE NULL " \ - " END; " \ + " END notify " \ + " WHERE OLD.conflict_data IS NOT NULL " \ + " OR notify IS NOT NULL; " \ "END " \ "" -#define STMT_DROP_REVERT_LIST_TRIGGERS 161 -#define STMT_161_INFO {"STMT_DROP_REVERT_LIST_TRIGGERS", NULL} -#define STMT_161 \ +#define STMT_DROP_REVERT_LIST_TRIGGERS 162 +#define STMT_162_INFO {"STMT_DROP_REVERT_LIST_TRIGGERS", NULL} +#define STMT_162 \ "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 \ +#define STMT_SELECT_REVERT_LIST 163 +#define STMT_163_INFO {"STMT_SELECT_REVERT_LIST", NULL} +#define STMT_163 \ "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 \ +#define STMT_SELECT_REVERT_LIST_COPIED_CHILDREN 164 +#define STMT_164_INFO {"STMT_SELECT_REVERT_LIST_COPIED_CHILDREN", NULL} +#define STMT_164 \ "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)) " \ @@ -1737,113 +1815,125 @@ "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_DELETE_REVERT_LIST 165 +#define STMT_165_INFO {"STMT_DELETE_REVERT_LIST", 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 " \ + "DELETE FROM revert_list WHERE local_relpath = ?1 " \ "" -#define STMT_DELETE_REVERT_LIST_RECURSIVE 166 -#define STMT_166_INFO {"STMT_DELETE_REVERT_LIST_RECURSIVE", NULL} +#define STMT_SELECT_REVERT_LIST_RECURSIVE 166 +#define STMT_166_INFO {"STMT_SELECT_REVERT_LIST_RECURSIVE", NULL} #define STMT_166 \ + "SELECT p.local_relpath, n.kind, a.notify, a.kind " \ + "FROM (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)))) p " \ + "LEFT JOIN revert_list n ON n.local_relpath=p.local_relpath AND n.actual=0 " \ + "LEFT JOIN revert_list a ON a.local_relpath=p.local_relpath AND a.actual=1 " \ + "ORDER BY p.local_relpath " \ + "" + +#define STMT_DELETE_REVERT_LIST_RECURSIVE 167 +#define STMT_167_INFO {"STMT_DELETE_REVERT_LIST_RECURSIVE", NULL} +#define STMT_167 \ "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 \ +#define STMT_DROP_REVERT_LIST 168 +#define STMT_168_INFO {"STMT_DROP_REVERT_LIST", NULL} +#define STMT_168 \ "DROP TABLE IF EXISTS revert_list " \ "" -#define STMT_CREATE_DELETE_LIST 168 -#define STMT_168_INFO {"STMT_CREATE_DELETE_LIST", NULL} -#define STMT_168 \ +#define STMT_CREATE_DELETE_LIST 169 +#define STMT_169_INFO {"STMT_CREATE_DELETE_LIST", NULL} +#define STMT_169 \ "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 \ +#define STMT_INSERT_DELETE_LIST 170 +#define STMT_170_INFO {"STMT_INSERT_DELETE_LIST", NULL} +#define STMT_170 \ "INSERT INTO delete_list(local_relpath) " \ + "SELECT ?2 " \ + "UNION ALL " \ "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 (((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 " \ + "ORDER by local_relpath " \ "" -#define STMT_SELECT_DELETE_LIST 170 -#define STMT_170_INFO {"STMT_SELECT_DELETE_LIST", NULL} -#define STMT_170 \ +#define STMT_SELECT_DELETE_LIST 171 +#define STMT_171_INFO {"STMT_SELECT_DELETE_LIST", NULL} +#define STMT_171 \ "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 \ +#define STMT_FINALIZE_DELETE 172 +#define STMT_172_INFO {"STMT_FINALIZE_DELETE", NULL} +#define STMT_172 \ "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 \ +#define STMT_CREATE_UPDATE_MOVE_LIST 173 +#define STMT_173_INFO {"STMT_CREATE_UPDATE_MOVE_LIST", NULL} +#define STMT_173 \ "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, " \ + " kind TEXT 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 \ +#define STMT_INSERT_UPDATE_MOVE_LIST 174 +#define STMT_174_INFO {"STMT_INSERT_UPDATE_MOVE_LIST", NULL} +#define STMT_174 \ "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 \ +#define STMT_SELECT_UPDATE_MOVE_LIST 175 +#define STMT_175_INFO {"STMT_SELECT_UPDATE_MOVE_LIST", NULL} +#define STMT_175 \ "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 \ +#define STMT_FINALIZE_UPDATE_MOVE 176 +#define STMT_176_INFO {"STMT_FINALIZE_UPDATE_MOVE", NULL} +#define STMT_176 \ "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 \ +#define STMT_MOVE_NOTIFY_TO_REVERT 177 +#define STMT_177_INFO {"STMT_MOVE_NOTIFY_TO_REVERT", NULL} +#define STMT_177 \ + "INSERT INTO revert_list (local_relpath, notify, kind, actual) " \ + " SELECT local_relpath, 2, kind, 1 FROM update_move_list; " \ + "DROP TABLE update_move_list " \ + "" + +#define STMT_SELECT_MIN_MAX_REVISIONS 178 +#define STMT_178_INFO {"STMT_SELECT_MIN_MAX_REVISIONS", NULL} +#define STMT_178 \ "SELECT MIN(revision), MAX(revision), " \ " MIN(changed_revision), MAX(changed_revision) FROM nodes " \ " WHERE wc_id = ?1 " \ @@ -1854,9 +1944,9 @@ " AND op_depth = 0 " \ "" -#define STMT_HAS_SPARSE_NODES 177 -#define STMT_177_INFO {"STMT_HAS_SPARSE_NODES", NULL} -#define STMT_177 \ +#define STMT_HAS_SPARSE_NODES 179 +#define STMT_179_INFO {"STMT_HAS_SPARSE_NODES", NULL} +#define STMT_179 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1868,9 +1958,9 @@ "LIMIT 1 " \ "" -#define STMT_SUBTREE_HAS_TREE_MODIFICATIONS 178 -#define STMT_178_INFO {"STMT_SUBTREE_HAS_TREE_MODIFICATIONS", NULL} -#define STMT_178 \ +#define STMT_SUBTREE_HAS_TREE_MODIFICATIONS 180 +#define STMT_180_INFO {"STMT_SUBTREE_HAS_TREE_MODIFICATIONS", NULL} +#define STMT_180 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1879,9 +1969,9 @@ "LIMIT 1 " \ "" -#define STMT_SUBTREE_HAS_PROP_MODIFICATIONS 179 -#define STMT_179_INFO {"STMT_SUBTREE_HAS_PROP_MODIFICATIONS", NULL} -#define STMT_179 \ +#define STMT_SUBTREE_HAS_PROP_MODIFICATIONS 181 +#define STMT_181_INFO {"STMT_SUBTREE_HAS_PROP_MODIFICATIONS", NULL} +#define STMT_181 \ "SELECT 1 FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1890,9 +1980,9 @@ "LIMIT 1 " \ "" -#define STMT_HAS_SWITCHED 180 -#define STMT_180_INFO {"STMT_HAS_SWITCHED", NULL} -#define STMT_180 \ +#define STMT_HAS_SWITCHED 182 +#define STMT_182_INFO {"STMT_HAS_SWITCHED", NULL} +#define STMT_182 \ "SELECT 1 " \ "FROM nodes " \ "WHERE wc_id = ?1 " \ @@ -1904,60 +1994,47 @@ "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 \ +#define STMT_SELECT_MOVED_FROM_RELPATH 183 +#define STMT_183_INFO {"STMT_SELECT_MOVED_FROM_RELPATH", NULL} +#define STMT_183 \ "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 \ +#define STMT_UPDATE_MOVED_TO_RELPATH 184 +#define STMT_184_INFO {"STMT_UPDATE_MOVED_TO_RELPATH", NULL} +#define STMT_184 \ "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 \ +#define STMT_CLEAR_MOVED_TO_RELPATH 185 +#define STMT_185_INFO {"STMT_CLEAR_MOVED_TO_RELPATH", NULL} +#define STMT_185 \ "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 \ +#define STMT_CLEAR_MOVED_HERE_RECURSIVE 186 +#define STMT_186_INFO {"STMT_CLEAR_MOVED_HERE_RECURSIVE", NULL} +#define STMT_186 \ "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 \ +#define STMT_SELECT_MOVED_HERE_CHILDREN 187 +#define STMT_187_INFO {"STMT_SELECT_MOVED_HERE_CHILDREN", NULL} +#define STMT_187 \ "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 \ +#define STMT_SELECT_MOVED_FOR_DELETE 188 +#define STMT_188_INFO {"STMT_SELECT_MOVED_FOR_DELETE", NULL} +#define STMT_188 \ "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 " \ @@ -1971,9 +2048,9 @@ " 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 \ +#define STMT_SELECT_MOVED_FROM_FOR_DELETE 189 +#define STMT_189_INFO {"STMT_SELECT_MOVED_FROM_FOR_DELETE", NULL} +#define STMT_189 \ "SELECT local_relpath, op_depth, " \ " (SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r " \ " WHERE r.wc_id = ?1 " \ @@ -1984,48 +2061,48 @@ "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 \ +#define STMT_UPDATE_MOVED_TO_DESCENDANTS 190 +#define STMT_190_INFO {"STMT_UPDATE_MOVED_TO_DESCENDANTS", NULL} +#define STMT_190 \ "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 \ +#define STMT_CLEAR_MOVED_TO_DESCENDANTS 191 +#define STMT_191_INFO {"STMT_CLEAR_MOVED_TO_DESCENDANTS", NULL} +#define STMT_191 \ "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 " \ + "SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind " \ + "FROM nodes n " \ + "JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath " \ + " AND d.op_depth = (SELECT MIN(dd.op_depth) " \ + " FROM nodes dd " \ + " WHERE dd.wc_id = ?1 " \ + " AND dd.local_relpath = d.local_relpath " \ + " AND dd.op_depth > ?3) " \ + "WHERE n.wc_id = ?1 AND n.local_relpath = ?2 AND n.op_depth = ?3 " \ + " AND d.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 " \ + "SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind " \ + "FROM nodes n " \ + "JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath " \ + " AND d.op_depth = (SELECT MIN(dd.op_depth) " \ + " FROM nodes dd " \ + " WHERE dd.wc_id = ?1 " \ + " AND dd.local_relpath = d.local_relpath " \ + " AND dd.op_depth > ?3) " \ + "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 d.moved_to IS NOT NULL " \ + "ORDER BY n.local_relpath " \ "" #define STMT_SELECT_MOVED_OUTSIDE 193 @@ -2039,44 +2116,27 @@ " 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_SELECT_MOVED_DESCENDANTS_SRC 194 +#define STMT_194_INFO {"STMT_SELECT_MOVED_DESCENDANTS_SRC", 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} + "SELECT s.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id = n.wc_id AND s.local_relpath = n.local_relpath " \ + " AND s.op_depth = (SELECT MIN(d.op_depth) " \ + " FROM nodes d " \ + " WHERE d.wc_id = ?1 " \ + " AND d.local_relpath = s.local_relpath " \ + " AND d.op_depth > ?3) " \ + "WHERE n.wc_id = ?1 AND n.op_depth = ?3 " \ + " AND (n.local_relpath = ?2 OR (((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 s.moved_to IS NOT NULL " \ + "" + +#define STMT_COMMIT_UPDATE_ORIGIN 195 +#define STMT_195_INFO {"STMT_COMMIT_UPDATE_ORIGIN", 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), " \ + " repos_path = (CASE WHEN (?2) = '' THEN (CASE WHEN (?5) = '' THEN (local_relpath) WHEN (local_relpath) = '' THEN (?5) ELSE (?5) || '/' || (local_relpath) END) WHEN (?5) = '' 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 (?5) WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?5) || SUBSTR((local_relpath), LENGTH(?2)+1) END END), " \ " revision = ?6 " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -2084,16 +2144,16 @@ " AND op_depth = ?3 " \ "" -#define STMT_HAS_LAYER_BETWEEN 197 -#define STMT_197_INFO {"STMT_HAS_LAYER_BETWEEN", NULL} -#define STMT_197 \ +#define STMT_HAS_LAYER_BETWEEN 196 +#define STMT_196_INFO {"STMT_HAS_LAYER_BETWEEN", NULL} +#define STMT_196 \ "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 \ +#define STMT_SELECT_REPOS_PATH_REVISION 197 +#define STMT_197_INFO {"STMT_SELECT_REPOS_PATH_REVISION", NULL} +#define STMT_197 \ "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)) " \ @@ -2101,50 +2161,52 @@ "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 \ +#define STMT_SELECT_HAS_NON_FILE_CHILDREN 198 +#define STMT_198_INFO {"STMT_SELECT_HAS_NON_FILE_CHILDREN", NULL} +#define STMT_198 \ "SELECT 1 FROM nodes " \ - "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 AND kind != 'file' " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND kind != 'file' " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_HAS_GRANDCHILDREN 200 -#define STMT_200_INFO {"STMT_SELECT_HAS_GRANDCHILDREN", NULL} -#define STMT_200 \ +#define STMT_SELECT_HAS_GRANDCHILDREN 199 +#define STMT_199_INFO {"STMT_SELECT_HAS_GRANDCHILDREN", NULL} +#define STMT_199 \ "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 op_depth = ?3 " \ " AND file_external IS NULL " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_ALL_NODES 201 -#define STMT_201_INFO {"STMT_SELECT_ALL_NODES", NULL} -#define STMT_201 \ +#define STMT_SELECT_ALL_NODES 200 +#define STMT_200_INFO {"STMT_SELECT_ALL_NODES", NULL} +#define STMT_200 \ "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 \ +#define STMT_SELECT_IPROPS 201 +#define STMT_201_INFO {"STMT_SELECT_IPROPS", NULL} +#define STMT_201 \ "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 \ +#define STMT_UPDATE_IPROP 202 +#define STMT_202_INFO {"STMT_UPDATE_IPROP", NULL} +#define STMT_202 \ "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 \ +#define STMT_SELECT_IPROPS_NODE 203 +#define STMT_203_INFO {"STMT_SELECT_IPROPS_NODE", NULL} +#define STMT_203 \ "SELECT local_relpath, repos_path FROM nodes " \ "WHERE wc_id = ?1 " \ " AND local_relpath = ?2 " \ @@ -2152,9 +2214,9 @@ " AND (inherited_props not null) " \ "" -#define STMT_SELECT_IPROPS_RECURSIVE 205 -#define STMT_205_INFO {"STMT_SELECT_IPROPS_RECURSIVE", NULL} -#define STMT_205 \ +#define STMT_SELECT_IPROPS_RECURSIVE 204 +#define STMT_204_INFO {"STMT_SELECT_IPROPS_RECURSIVE", NULL} +#define STMT_204 \ "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)) " \ @@ -2162,9 +2224,9 @@ " AND (inherited_props not null) " \ "" -#define STMT_SELECT_IPROPS_CHILDREN 206 -#define STMT_206_INFO {"STMT_SELECT_IPROPS_CHILDREN", NULL} -#define STMT_206 \ +#define STMT_SELECT_IPROPS_CHILDREN 205 +#define STMT_205_INFO {"STMT_SELECT_IPROPS_CHILDREN", NULL} +#define STMT_205 \ "SELECT local_relpath, repos_path FROM nodes " \ "WHERE wc_id = ?1 " \ " AND parent_relpath = ?2 " \ @@ -2172,16 +2234,16 @@ " AND (inherited_props not null) " \ "" -#define STMT_HAVE_STAT1_TABLE 207 -#define STMT_207_INFO {"STMT_HAVE_STAT1_TABLE", NULL} -#define STMT_207 \ +#define STMT_HAVE_STAT1_TABLE 206 +#define STMT_206_INFO {"STMT_HAVE_STAT1_TABLE", NULL} +#define STMT_206 \ "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 \ +#define STMT_CREATE_SCHEMA 207 +#define STMT_207_INFO {"STMT_CREATE_SCHEMA", NULL} +#define STMT_207 \ "CREATE TABLE REPOSITORY ( " \ " id INTEGER PRIMARY KEY AUTOINCREMENT, " \ " root TEXT UNIQUE NOT NULL, " \ @@ -2246,9 +2308,9 @@ "; " \ "" -#define STMT_CREATE_NODES 209 -#define STMT_209_INFO {"STMT_CREATE_NODES", NULL} -#define STMT_209 \ +#define STMT_CREATE_NODES 208 +#define STMT_208_INFO {"STMT_CREATE_NODES", NULL} +#define STMT_208 \ "CREATE TABLE NODES ( " \ " wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \ " local_relpath TEXT NOT NULL, " \ @@ -2288,9 +2350,9 @@ " WHERE op_depth = 0; " \ "" -#define STMT_CREATE_NODES_TRIGGERS 210 -#define STMT_210_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL} -#define STMT_210 \ +#define STMT_CREATE_NODES_TRIGGERS 209 +#define STMT_209_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL} +#define STMT_209 \ "CREATE TRIGGER nodes_insert_trigger " \ "AFTER INSERT ON nodes " \ "WHEN NEW.checksum IS NOT NULL " \ @@ -2316,9 +2378,9 @@ "END; " \ "" -#define STMT_CREATE_EXTERNALS 211 -#define STMT_211_INFO {"STMT_CREATE_EXTERNALS", NULL} -#define STMT_211 \ +#define STMT_CREATE_EXTERNALS 210 +#define STMT_210_INFO {"STMT_CREATE_EXTERNALS", NULL} +#define STMT_210 \ "CREATE TABLE EXTERNALS ( " \ " wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \ " local_relpath TEXT NOT NULL, " \ @@ -2337,9 +2399,9 @@ " local_relpath); " \ "" -#define STMT_INSTALL_SCHEMA_STATISTICS 212 -#define STMT_212_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL} -#define STMT_212 \ +#define STMT_INSTALL_SCHEMA_STATISTICS 211 +#define STMT_211_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL} +#define STMT_211 \ "ANALYZE sqlite_master; " \ "DELETE FROM sqlite_stat1 " \ "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); " \ @@ -2364,9 +2426,9 @@ "ANALYZE sqlite_master; " \ "" -#define STMT_UPGRADE_TO_20 213 -#define STMT_213_INFO {"STMT_UPGRADE_TO_20", NULL} -#define STMT_213 \ +#define STMT_UPGRADE_TO_20 212 +#define STMT_212_INFO {"STMT_UPGRADE_TO_20", NULL} +#define STMT_212 \ "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); " \ @@ -2407,59 +2469,59 @@ "PRAGMA user_version = 20; " \ "" -#define STMT_UPGRADE_TO_21 214 -#define STMT_214_INFO {"STMT_UPGRADE_TO_21", NULL} -#define STMT_214 \ +#define STMT_UPGRADE_TO_21 213 +#define STMT_213_INFO {"STMT_UPGRADE_TO_21", NULL} +#define STMT_213 \ "PRAGMA user_version = 21; " \ "" -#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 215 -#define STMT_215_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL} -#define STMT_215 \ +#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 214 +#define STMT_214_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL} +#define STMT_214 \ "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 216 -#define STMT_216_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL} -#define STMT_216 \ +#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 215 +#define STMT_215_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL} +#define STMT_215 \ "UPDATE actual_node SET tree_conflict_data = NULL " \ "" -#define STMT_UPGRADE_TO_22 217 -#define STMT_217_INFO {"STMT_UPGRADE_TO_22", NULL} -#define STMT_217 \ +#define STMT_UPGRADE_TO_22 216 +#define STMT_216_INFO {"STMT_UPGRADE_TO_22", NULL} +#define STMT_216 \ "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 218 -#define STMT_218_INFO {"STMT_UPGRADE_TO_23", NULL} -#define STMT_218 \ +#define STMT_UPGRADE_TO_23 217 +#define STMT_217_INFO {"STMT_UPGRADE_TO_23", NULL} +#define STMT_217 \ "PRAGMA user_version = 23; " \ "" -#define STMT_UPGRADE_23_HAS_WORKING_NODES 219 -#define STMT_219_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL} -#define STMT_219 \ +#define STMT_UPGRADE_23_HAS_WORKING_NODES 218 +#define STMT_218_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL} +#define STMT_218 \ "SELECT 1 FROM nodes WHERE op_depth > 0 " \ "LIMIT 1 " \ "" -#define STMT_UPGRADE_TO_24 220 -#define STMT_220_INFO {"STMT_UPGRADE_TO_24", NULL} -#define STMT_220 \ +#define STMT_UPGRADE_TO_24 219 +#define STMT_219_INFO {"STMT_UPGRADE_TO_24", NULL} +#define STMT_219 \ "UPDATE pristine SET refcount = " \ " (SELECT COUNT(*) FROM nodes " \ " WHERE checksum = pristine.checksum ); " \ "PRAGMA user_version = 24; " \ "" -#define STMT_UPGRADE_TO_25 221 -#define STMT_221_INFO {"STMT_UPGRADE_TO_25", NULL} -#define STMT_221 \ +#define STMT_UPGRADE_TO_25 220 +#define STMT_220_INFO {"STMT_UPGRADE_TO_25", NULL} +#define STMT_220 \ "DROP VIEW IF EXISTS NODES_CURRENT; " \ "CREATE VIEW NODES_CURRENT AS " \ " SELECT * FROM nodes " \ @@ -2471,9 +2533,9 @@ "PRAGMA user_version = 25; " \ "" -#define STMT_UPGRADE_TO_26 222 -#define STMT_222_INFO {"STMT_UPGRADE_TO_26", NULL} -#define STMT_222 \ +#define STMT_UPGRADE_TO_26 221 +#define STMT_221_INFO {"STMT_UPGRADE_TO_26", NULL} +#define STMT_221 \ "DROP VIEW IF EXISTS NODES_BASE; " \ "CREATE VIEW NODES_BASE AS " \ " SELECT * FROM nodes " \ @@ -2481,15 +2543,15 @@ "PRAGMA user_version = 26; " \ "" -#define STMT_UPGRADE_TO_27 223 -#define STMT_223_INFO {"STMT_UPGRADE_TO_27", NULL} -#define STMT_223 \ +#define STMT_UPGRADE_TO_27 222 +#define STMT_222_INFO {"STMT_UPGRADE_TO_27", NULL} +#define STMT_222 \ "PRAGMA user_version = 27; " \ "" -#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 224 -#define STMT_224_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL} -#define STMT_224 \ +#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 223 +#define STMT_223_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL} +#define STMT_223 \ "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) " \ @@ -2497,18 +2559,18 @@ "LIMIT 1 " \ "" -#define STMT_UPGRADE_TO_28 225 -#define STMT_225_INFO {"STMT_UPGRADE_TO_28", NULL} -#define STMT_225 \ +#define STMT_UPGRADE_TO_28 224 +#define STMT_224_INFO {"STMT_UPGRADE_TO_28", NULL} +#define STMT_224 \ "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 226 -#define STMT_226_INFO {"STMT_UPGRADE_TO_29", NULL} -#define STMT_226 \ +#define STMT_UPGRADE_TO_29 225 +#define STMT_225_INFO {"STMT_UPGRADE_TO_29", NULL} +#define STMT_225 \ "DROP TRIGGER IF EXISTS nodes_update_checksum_trigger; " \ "DROP TRIGGER IF EXISTS nodes_insert_trigger; " \ "DROP TRIGGER IF EXISTS nodes_delete_trigger; " \ @@ -2538,9 +2600,9 @@ "PRAGMA user_version = 29; " \ "" -#define STMT_UPGRADE_TO_30 227 -#define STMT_227_INFO {"STMT_UPGRADE_TO_30", NULL} -#define STMT_227 \ +#define STMT_UPGRADE_TO_30 226 +#define STMT_226_INFO {"STMT_UPGRADE_TO_30", NULL} +#define STMT_226 \ "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); " \ @@ -2548,9 +2610,9 @@ "UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL; " \ "" -#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 228 -#define STMT_228_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL} -#define STMT_228 \ +#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 227 +#define STMT_227_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL} +#define STMT_227 \ "SELECT wc_id, local_relpath, " \ " conflict_old, conflict_working, conflict_new, prop_reject, tree_conflict_data " \ "FROM actual_node " \ @@ -2562,24 +2624,24 @@ "ORDER by wc_id, local_relpath " \ "" -#define STMT_UPGRADE_30_SET_CONFLICT 229 -#define STMT_229_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL} -#define STMT_229 \ +#define STMT_UPGRADE_30_SET_CONFLICT 228 +#define STMT_228_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL} +#define STMT_228 \ "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 230 -#define STMT_230_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL} -#define STMT_230 \ +#define STMT_UPGRADE_TO_31_ALTER_TABLE 229 +#define STMT_229_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL} +#define STMT_229 \ "ALTER TABLE NODES ADD COLUMN inherited_props BLOB; " \ "" -#define STMT_UPGRADE_TO_31_FINALIZE 231 -#define STMT_231_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL} -#define STMT_231 \ +#define STMT_UPGRADE_TO_31_FINALIZE 230 +#define STMT_230_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL} +#define STMT_230 \ "DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \ "DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \ "DROP INDEX I_NODES_PARENT; " \ @@ -2591,9 +2653,9 @@ "PRAGMA user_version = 31; " \ "" -#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 232 -#define STMT_232_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL} -#define STMT_232 \ +#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 231 +#define STMT_231_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL} +#define STMT_231 \ "SELECT l.wc_id, l.local_relpath FROM nodes as l " \ "LEFT OUTER JOIN nodes as r " \ "ON l.wc_id = r.wc_id " \ @@ -2605,9 +2667,9 @@ " 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 233 -#define STMT_233_INFO {"STMT_UPGRADE_TO_32", NULL} -#define STMT_233 \ +#define STMT_UPGRADE_TO_32 232 +#define STMT_232_INFO {"STMT_UPGRADE_TO_32", NULL} +#define STMT_232 \ "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); " \ @@ -2660,9 +2722,9 @@ "DROP TABLE ACTUAL_NODE_BACKUP; " \ "" -#define STMT_VERIFICATION_TRIGGERS 234 -#define STMT_234_INFO {"STMT_VERIFICATION_TRIGGERS", NULL} -#define STMT_234 \ +#define STMT_VERIFICATION_TRIGGERS 233 +#define STMT_233_INFO {"STMT_VERIFICATION_TRIGGERS", NULL} +#define STMT_233 \ "CREATE TEMPORARY TRIGGER no_repository_updates BEFORE UPDATE ON repository " \ "BEGIN " \ " SELECT RAISE(FAIL, 'Updates to REPOSITORY are not allowed.'); " \ @@ -2701,6 +2763,179 @@ "END; " \ "" +#define STMT_STATIC_VERIFY 234 +#define STMT_234_INFO {"STMT_STATIC_VERIFY", NULL} +#define STMT_234 \ + "SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.local_relpath) " \ + "UNION ALL " \ + "SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' " \ + "FROM actual_node a " \ + "LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) from nodes i " \ + " WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) " \ + "WHERE (a.properties IS NOT NULL " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete'))) " \ + " OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != 'file')) " \ + " OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=n.wc_id " \ + " AND i.local_relpath=n.parent_relpath " \ + " AND i.op_depth <= n.op_depth) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 21, 'Unneeded node data' " \ + "FROM nodes " \ + "WHERE presence NOT IN ('normal', 'incomplete') " \ + "AND (properties IS NOT NULL " \ + " OR checksum IS NOT NULL " \ + " OR depth IS NOT NULL " \ + " OR symlink_target IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0) " \ + " OR changed_author IS NOT NULL " \ + " OR translated_size IS NOT NULL " \ + " OR last_mod_time IS NOT NULL " \ + " OR dav_cache IS NOT NULL " \ + " OR file_external IS NOT NULL " \ + " OR inherited_props IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' " \ + "FROM nodes " \ + "WHERE presence IN ('base-deleted') " \ + "AND (repos_id IS NOT NULL " \ + " OR repos_path IS NOT NULL " \ + " OR revision IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + "AND (kind IS NULL " \ + " OR (repos_path IS NULL " \ + " AND (properties IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR changed_author IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0))) " \ + " OR (CASE WHEN kind = 'file' AND repos_path IS NOT NULL " \ + " THEN checksum IS NULL " \ + " ELSE checksum IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'dir' THEN depth IS NULL " \ + " ELSE depth IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'symlink' THEN symlink_target IS NULL " \ + " ELSE symlink_target IS NOT NULL END)) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + " AND repos_path IS NULL " \ + " AND op_depth != relpath_depth(local_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' " \ + "FROM nodes n " \ + "WHERE op_depth < relpath_depth(local_relpath) " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes p " \ + " WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND p.op_depth=n.op_depth " \ + " AND (p.presence IN ('normal', 'incomplete') " \ + " OR (p.presence IN ('base-deleted', 'not-present') " \ + " AND n.presence = 'base-deleted'))) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' " \ + "FROM nodes n " \ + "JOIN nodes p " \ + " ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND n.op_depth=p.op_depth " \ + "WHERE n.op_depth > 0 AND n.presence IN ('normal', 'incomplete') " \ + " AND (n.repos_id != p.repos_id " \ + " OR n.repos_path != " \ + " (CASE WHEN (n.parent_relpath) = '' THEN (CASE WHEN (p.repos_path) = '' THEN (n.local_relpath) WHEN (n.local_relpath) = '' THEN (p.repos_path) ELSE (p.repos_path) || '/' || (n.local_relpath) END) WHEN (p.repos_path) = '' THEN (CASE WHEN (n.parent_relpath) = '' THEN (n.local_relpath) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN '' WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+2) END END) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN (p.repos_path) WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN (p.repos_path) || SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1) END END) " \ + " OR n.revision != p.revision " \ + " OR p.kind != 'dir' " \ + " OR n.moved_here IS NOT p.moved_here) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' " \ + "FROM nodes n " \ + "WHERE n.op_depth = relpath_depth(local_relpath) " \ + " AND presence NOT IN ('normal', 'incomplete', 'base-deleted') " \ + "UNION ALL " \ + "SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath " \ + " AND s.op_depth = relpath_depth(s.local_relpath) " \ + " AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth > n.op_depth) " \ + "WHERE n.presence IN ('normal', 'incomplete') " \ + " AND EXISTS(SELECT 1 " \ + " FROM nodes dn " \ + " WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth " \ + " AND dn.presence IN ('normal', 'incomplete') " \ + " AND (((dn.local_relpath) > (CASE (n.local_relpath) WHEN '' THEN '' ELSE (n.local_relpath) || '/' END)) AND ((dn.local_relpath) < CASE (n.local_relpath) WHEN '' THEN X'FFFF' ELSE (n.local_relpath) || '0' END)) " \ + " AND dn.file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 " \ + " FROM nodes ds " \ + " WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth " \ + " AND ds.local_relpath=dn.local_relpath)) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' " \ + "FROM nodes s " \ + "LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth < s.op_depth) " \ + "WHERE s.presence = 'base-deleted' " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete') " \ + " ) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' " \ + "FROM nodes n " \ + "WHERE n.op_depth = 0 " \ + " AND (n.moved_to IS NOT NULL " \ + " OR n.moved_here IS NOT NULL) " \ + "UNION ALL " \ + "SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' " \ + "FROM nodes d " \ + "WHERE d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes s " \ + " WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' " \ + "FROM nodes s " \ + "WHERE s.moved_to IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes d " \ + " WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to " \ + " AND d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here =1 AND d.repos_path IS NOT NULL) " \ + "" + #define WC_QUERIES_SQL_DECLARE_STATEMENTS(varname) \ static const char * const varname[] = { \ STMT_0, \ diff --git a/contrib/subversion/subversion/libsvn_wc/wc-queries.sql b/contrib/subversion/subversion/libsvn_wc/wc-queries.sql index 7cdb46cc0..3a8bf92a2 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc-queries.sql +++ b/contrib/subversion/subversion/libsvn_wc/wc-queries.sql @@ -71,6 +71,12 @@ LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id 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 +FROM nodes +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 + +-- STMT_SELECT_BASE_CHILDREN_INFO_LOCK SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, revision, depth, file_external, lock_token, lock_owner, lock_comment, lock_date @@ -79,6 +85,7 @@ 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, @@ -92,7 +99,7 @@ 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 + symlink_target, properties, moved_to, moved_here FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 @@ -118,8 +125,9 @@ 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. */ + doesn't work as the index is created without the DESC keyword. + Using both local_relpath and op_depth descending does work without any + performance penalty. */ 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, @@ -128,11 +136,27 @@ 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 +ORDER BY local_relpath DESC, op_depth DESC + +-- STMT_SELECT_BASE_NODE_CHILDREN_INFO +/* See above re: result ordering. The results of this query must be in +the same order as returned by STMT_SELECT_NODE_CHILDREN_INFO, because +read_children_info expects them to be. */ +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 AND op_depth = 0 +ORDER BY local_relpath DESC -- STMT_SELECT_NODE_CHILDREN_WALKER_INFO SELECT local_relpath, op_depth, presence, kind FROM nodes_current WHERE wc_id = ?1 AND parent_relpath = ?2 +ORDER BY local_relpath -- STMT_SELECT_ACTUAL_CHILDREN_INFO SELECT local_relpath, changelist, properties, conflict_data @@ -161,16 +185,6 @@ INSERT OR REPLACE INTO nodes ( 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 @@ -210,13 +224,23 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) -- STMT_DELETE_WORKING_BASE_DELETE DELETE FROM nodes +WHERE wc_id = ?1 AND local_relpath = ?2 + AND presence = MAP_BASE_DELETED + AND op_depth > ?3 + 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 > ?3) + +-- STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE +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 > ?3 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) + AND op_depth > ?3) -- STMT_DELETE_WORKING_RECURSIVE DELETE FROM nodes @@ -225,34 +249,42 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) -- STMT_DELETE_BASE_RECURSIVE DELETE FROM nodes -WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) +WHERE wc_id = ?1 AND (local_relpath = ?2 + OR 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 +/* Full layer replacement check code for handling moves +The op_root must exist (or there is no layer to replace) and an op-root + always has presence 'normal' */ +-- STMT_SELECT_LAYER_FOR_REPLACE +SELECT s.local_relpath, s.kind, + RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, 'normal' +FROM nodes s +WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 +UNION ALL +SELECT s.local_relpath, s.kind, + RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, d.presence +FROM nodes s +LEFT OUTER JOIN nodes d ON d.wc_id= ?1 AND d.op_depth = ?5 + AND d.local_relpath = drp +WHERE s.wc_id = ?1 + AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2) + AND s.op_depth = ?3 +ORDER BY s.local_relpath + +-- STMT_SELECT_DESCENDANTS_OP_DEPTH_RV SELECT local_relpath, kind FROM nodes WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND op_depth = ?3 + AND presence in (MAP_NORMAL, MAP_INCOMPLETE) ORDER BY local_relpath DESC -- STMT_COPY_NODE_MOVE @@ -262,25 +294,51 @@ INSERT OR REPLACE INTO nodes ( 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 + s.wc_id, ?4 /*local_relpath */, ?5 /*op_depth*/, ?6 /* parent_relpath */, + s.repos_id, + s.repos_path, s.revision, s.presence, s.depth, s.kind, s.changed_revision, + s.changed_date, s.changed_author, s.checksum, s.properties, + CASE WHEN d.checksum=s.checksum THEN d.translated_size END, + CASE WHEN d.checksum=s.checksum THEN d.last_mod_time END, + s.symlink_target, 1, d.moved_to +FROM nodes s +LEFT JOIN nodes d ON d.wc_id=?1 AND d.local_relpath=?4 AND d.op_depth=?5 +WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 + +-- STMT_SELECT_NO_LONGER_MOVED_RV +SELECT d.local_relpath, RELPATH_SKIP_JOIN(?2, ?4, d.local_relpath) srp, + b.presence, b.op_depth +FROM nodes d +LEFT OUTER JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = d.local_relpath + AND b.op_depth = (SELECT MAX(x.op_depth) FROM nodes x + WHERE x.wc_id = ?1 + AND x.local_relpath = b.local_relpath + AND x.op_depth < ?3) +WHERE d.wc_id = ?1 + AND IS_STRICT_DESCENDANT_OF(d.local_relpath, ?2) + AND d.op_depth = ?3 + AND NOT EXISTS(SELECT * FROM nodes s + WHERE s.wc_id = ?1 + AND s.local_relpath = srp + AND s.op_depth = ?5) +ORDER BY d.local_relpath DESC -- STMT_SELECT_OP_DEPTH_CHILDREN SELECT local_relpath, kind FROM nodes -WHERE wc_id = ?1 +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND presence != MAP_BASE_DELETED AND file_external is NULL +ORDER BY local_relpath + +-- STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS +SELECT local_relpath, kind FROM nodes +WHERE wc_id = ?1 + AND parent_relpath = ?2 + AND op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE) +ORDER BY local_relpath /* Used by non-recursive revert to detect higher level children, and actual-only rows that would be left orphans, if the revert @@ -288,7 +346,8 @@ WHERE wc_id = ?1 -- 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)) + AND (op_depth > ?3 OR (op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE))) UNION ALL SELECT 1 FROM ACTUAL_NODE a WHERE wc_id = ?1 AND parent_relpath = ?2 @@ -318,10 +377,11 @@ WHERE wc_id = ?1 AND op_depth = ?3 -- STMT_COMMIT_DESCENDANTS_TO_BASE UPDATE NODES SET op_depth = 0, repos_id = ?4, - repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), + repos_path = RELPATH_SKIP_JOIN(?2, ?5, local_relpath), revision = ?6, dav_cache = NULL, moved_here = NULL, + moved_to = NULL, presence = CASE presence WHEN MAP_NORMAL THEN MAP_NORMAL WHEN MAP_EXCLUDED THEN MAP_EXCLUDED @@ -334,22 +394,30 @@ WHERE wc_id = ?1 -- 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 +SELECT DISTINCT local_relpath FROM nodes WHERE wc_id = ?1 AND parent_relpath = ?2 +ORDER BY local_relpath -- 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 +SELECT DISTINCT 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)) + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE))) +ORDER BY local_relpath + +-- STMT_SELECT_BASE_NOT_PRESENT_CHILDREN +SELECT local_relpath FROM nodes +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 + AND presence = MAP_NOT_PRESENT +ORDER BY local_relpath -- STMT_SELECT_NODE_PROPS SELECT properties, presence FROM nodes @@ -396,25 +464,13 @@ 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 +SELECT b.presence, w.presence, w.op_depth, w.moved_to +FROM nodes w +LEFT JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0 +WHERE w.wc_id = ?1 AND w.local_relpath = ?2 + AND w.op_depth = (SELECT MAX(op_depth) FROM nodes d + WHERE d.wc_id = ?1 AND d.local_relpath = ?2 + AND d.op_depth > 0) LIMIT 1 -- STMT_SELECT_MOVED_TO_NODE @@ -424,24 +480,20 @@ 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 +SELECT op_depth, moved_to 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 +WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 + AND EXISTS(SELECT * from nodes + WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)) +ORDER BY op_depth ASC +LIMIT 1 -- 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, @@ -467,13 +519,6 @@ 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 @@ -498,7 +543,7 @@ 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 IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND repos_id = ?3) - + -- STMT_UPDATE_LOCK_REPOS_ID UPDATE lock SET repos_id = ?2 @@ -677,6 +722,13 @@ INSERT OR IGNORE INTO actual_node ( SELECT wc_id, local_relpath, parent_relpath FROM targets_list +-- STMT_INSERT_ACTUAL_EMPTIES_FILES +INSERT OR IGNORE INTO actual_node ( + wc_id, local_relpath, parent_relpath) +SELECT wc_id, local_relpath, parent_relpath +FROM targets_list +WHERE kind=MAP_FILE + -- STMT_DELETE_ACTUAL_EMPTY DELETE FROM actual_node WHERE wc_id = ?1 AND local_relpath = ?2 @@ -691,7 +743,7 @@ WHERE wc_id = ?1 AND local_relpath = ?2 -- STMT_DELETE_ACTUAL_EMPTIES DELETE FROM actual_node WHERE wc_id = ?1 - AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) + AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND properties IS NULL AND conflict_data IS NULL AND changelist IS NULL @@ -755,7 +807,7 @@ WHERE wc_id = ?1 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 + WHERE c.wc_id = ?1 AND c.local_relpath = actual_node.local_relpath AND c.kind = MAP_FILE)) @@ -770,6 +822,17 @@ SET properties = NULL, right_checksum = NULL WHERE wc_id = ?1 AND local_relpath = ?2 +-- STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT +UPDATE actual_node +SET properties = NULL, + text_mod = NULL, + tree_conflict_data = NULL, + older_checksum = NULL, + left_checksum = NULL, + right_checksum = NULL, + changelist = NULL +WHERE wc_id = ?1 AND local_relpath = ?2 + -- STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE UPDATE actual_node SET properties = NULL, @@ -787,6 +850,7 @@ WHERE wc_id = ?1 UPDATE nodes SET depth = ?3 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 AND kind=MAP_DIR + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE) -- STMT_UPDATE_NODE_BASE_PRESENCE UPDATE nodes SET presence = ?3 @@ -891,6 +955,14 @@ SELECT local_dir_relpath FROM wc_lock WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_dir_relpath, ?2) +-- STMT_FIND_CONFLICT_DESCENDANT +SELECT 1 FROM actual_node +WHERE wc_id = ?1 + AND local_relpath > (?2 || '/') + AND local_relpath < (?2 || '0') /* '0' = ascii('/') +1 */ + AND conflict_data IS NOT NULL +LIMIT 1 + -- STMT_DELETE_WC_LOCK_ORPHAN DELETE FROM wc_lock WHERE wc_id = ?1 AND local_dir_relpath = ?2 @@ -924,22 +996,11 @@ VALUES (?1, ?2, 0, AND op_depth = 0)) -- STMT_INSTALL_WORKING_NODE_FOR_DELETE -INSERT OR REPLACE INTO nodes ( +INSERT 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) @@ -947,33 +1008,41 @@ 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 local_relpath = ?2 AND op_depth = ?3 -/* If this query is updated, STMT_INSERT_DELETE_LIST should too. */ +/* If this query is updated, STMT_INSERT_DELETE_LIST should too. + Use UNION ALL instead of a simple 'OR' to avoid creating a temp table */ -- 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 AND op_depth = ?3 +UNION ALL +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 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 +ORDER BY local_relpath -- STMT_INSERT_WORKING_NODE_FROM_BASE_COPY -INSERT INTO nodes ( +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 ) + symlink_target, moved_to ) 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 + last_mod_time, symlink_target, + (SELECT moved_to FROM nodes + WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 @@ -992,11 +1061,30 @@ 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 +/* Duplicated SELECT body to avoid creating temporary table */ +-- STMT_COPY_OP_DEPTH_RECURSIVE +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, moved_here, moved_to ) +SELECT + wc_id, local_relpath, ?4, 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, NULL, NULL +FROM nodes +WHERE wc_id = ?1 AND op_depth = ?3 AND local_relpath = ?2 +UNION ALL +SELECT + wc_id, local_relpath, ?4, 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, NULL, NULL +FROM nodes +WHERE wc_id = ?1 AND op_depth = ?3 + AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) +ORDER BY local_relpath -- STMT_DOES_NODE_EXIST SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 @@ -1198,7 +1286,10 @@ 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 +PRAGMA locking_mode = exclusive; +/* Testing shows DELETE is faster than TRUNCATE on NFS and + exclusive-locking is mostly used on remote file systems. */ +PRAGMA journal_mode = DELETE /* ------------------------------------------------------------------------- */ @@ -1213,14 +1304,6 @@ 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 @@ -1333,8 +1416,9 @@ BEGIN WHERE n.wc_id = OLD.wc_id AND n.local_relpath = OLD.local_relpath) THEN 1 - ELSE NULL - END; + END notify + WHERE OLD.conflict_data IS NOT NULL + OR notify IS NOT NULL; END; DROP TRIGGER IF EXISTS trigger_revert_list_actual_update; CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update @@ -1350,8 +1434,9 @@ BEGIN WHERE n.wc_id = OLD.wc_id AND n.local_relpath = OLD.local_relpath) THEN 1 - ELSE NULL - END; + END notify + WHERE OLD.conflict_data IS NOT NULL + OR notify IS NOT NULL; END -- STMT_DROP_REVERT_LIST_TRIGGERS @@ -1377,12 +1462,15 @@ ORDER BY local_relpath 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 +SELECT p.local_relpath, n.kind, a.notify, a.kind +FROM (SELECT DISTINCT local_relpath + FROM revert_list + WHERE (local_relpath = ?1 + OR IS_STRICT_DESCENDANT_OF(local_relpath, ?1))) p + +LEFT JOIN revert_list n ON n.local_relpath=p.local_relpath AND n.actual=0 +LEFT JOIN revert_list a ON a.local_relpath=p.local_relpath AND a.actual=1 +ORDER BY p.local_relpath -- STMT_DELETE_REVERT_LIST_RECURSIVE DELETE FROM revert_list @@ -1404,16 +1492,18 @@ CREATE TEMPORARY TABLE delete_list ( A subquery is used instead of nodes_current to avoid a table scan */ -- STMT_INSERT_DELETE_LIST INSERT INTO delete_list(local_relpath) +SELECT ?2 +UNION ALL SELECT local_relpath FROM nodes AS n WHERE wc_id = ?1 - AND (local_relpath = ?2 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) + AND 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 +ORDER by local_relpath -- STMT_SELECT_DELETE_LIST SELECT local_relpath FROM delete_list @@ -1429,7 +1519,7 @@ CREATE TEMPORARY TABLE update_move_list ( ### 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, + kind TEXT NOT NULL, content_state INTEGER NOT NULL, prop_state INTEGER NOT NULL ) @@ -1447,6 +1537,11 @@ ORDER BY local_relpath -- STMT_FINALIZE_UPDATE_MOVE DROP TABLE IF EXISTS update_move_list +-- STMT_MOVE_NOTIFY_TO_REVERT +INSERT INTO revert_list (local_relpath, notify, kind, actual) + SELECT local_relpath, 2, kind, 1 FROM update_move_list; +DROP TABLE update_move_list + /* ------------------------------------------------------------------------- */ /* Queries for revision status. */ @@ -1499,16 +1594,6 @@ WHERE wc_id = ?1 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 @@ -1571,31 +1656,30 @@ 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 +SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind +FROM nodes n +JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath + AND d.op_depth = (SELECT MIN(dd.op_depth) + FROM nodes dd + WHERE dd.wc_id = ?1 + AND dd.local_relpath = d.local_relpath + AND dd.op_depth > ?3) +WHERE n.wc_id = ?1 AND n.local_relpath = ?2 AND n.op_depth = ?3 + AND d.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 +SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind +FROM nodes n +JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath + AND d.op_depth = (SELECT MIN(dd.op_depth) + FROM nodes dd + WHERE dd.wc_id = ?1 + AND dd.local_relpath = d.local_relpath + AND dd.op_depth > ?3) +WHERE n.wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2) + AND n.op_depth = ?3 + AND d.moved_to IS NOT NULL +ORDER BY n.local_relpath -- STMT_SELECT_MOVED_OUTSIDE SELECT local_relpath, moved_to, op_depth FROM nodes @@ -1605,40 +1689,22 @@ WHERE wc_id = ?1 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_SELECT_MOVED_DESCENDANTS_SRC +SELECT s.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to +FROM nodes n +JOIN nodes s ON s.wc_id = n.wc_id AND s.local_relpath = n.local_relpath + AND s.op_depth = (SELECT MIN(d.op_depth) + FROM nodes d + WHERE d.wc_id = ?1 + AND d.local_relpath = s.local_relpath + AND d.op_depth > ?3) +WHERE n.wc_id = ?1 AND n.op_depth = ?3 + AND (n.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)) + AND s.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), + repos_path = RELPATH_SKIP_JOIN(?2, ?5, local_relpath), revision = ?6 WHERE wc_id = ?1 AND (local_relpath = ?2 @@ -1658,14 +1724,16 @@ 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 +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND kind != MAP_FILE +LIMIT 1 -- 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 op_depth = ?3 AND file_external IS NULL +LIMIT 1 /* ------------------------------------------------------------------------- */ diff --git a/contrib/subversion/subversion/libsvn_wc/wc.h b/contrib/subversion/subversion/libsvn_wc/wc.h index d7cf017e3..ab6870d1a 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc.h +++ b/contrib/subversion/subversion/libsvn_wc/wc.h @@ -158,7 +158,7 @@ extern "C" { * Bumped in r1395109. * * == 1.8.x shipped with format 31 - * + * * Please document any further format changes here. */ @@ -253,52 +253,6 @@ svn_wc__context_create_with_db(svn_wc_context_t **wc_ctx, 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. ***/ @@ -612,14 +566,6 @@ svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, 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, @@ -627,6 +573,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc__db_t *db, const char *local_abspath, @@ -634,17 +581,6 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, 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 @@ -699,9 +635,11 @@ svn_wc__write_check(svn_wc__db_t *db, */ svn_error_t * svn_wc__read_conflicts(const apr_array_header_t **conflicts, + svn_skel_t **conflict_skel, svn_wc__db_t *db, const char *local_abspath, svn_boolean_t create_tempfiles, + svn_boolean_t only_tree_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -785,24 +723,12 @@ svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets, 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_boolean_t ignore_unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool); diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db.c b/contrib/subversion/subversion/libsvn_wc/wc_db.c index 6c0dd6107..ee17b85e3 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db.c @@ -27,6 +27,7 @@ #include #include +#include "svn_private_config.h" #include "svn_types.h" #include "svn_error.h" #include "svn_dirent_uri.h" @@ -48,7 +49,7 @@ #include "workqueue.h" #include "token-map.h" -#include "svn_private_config.h" +#include "private/svn_sorts_private.h" #include "private/svn_sqlite.h" #include "private/svn_skel.h" #include "private/svn_wc_private.h" @@ -277,10 +278,9 @@ add_work_items(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool); static svn_error_t * -set_actual_props(apr_int64_t wc_id, +set_actual_props(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_hash_t *props, - svn_sqlite__db_t *db, apr_pool_t *scratch_pool); static svn_error_t * @@ -353,13 +353,6 @@ 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, @@ -424,7 +417,7 @@ lock_from_columns(svn_sqlite__stmt_t *stmt, svn_error_t * svn_wc__db_fetch_repos_info(const char **repos_root_url, const char **repos_uuid, - svn_sqlite__db_t *sdb, + svn_wc__db_wcroot_t *wcroot, apr_int64_t repos_id, apr_pool_t *result_pool) { @@ -443,7 +436,7 @@ svn_wc__db_fetch_repos_info(const char **repos_root_url, return SVN_NO_ERROR; } - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_REPOSITORY_BY_ID)); SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -493,35 +486,6 @@ repos_location_from_columns(apr_int64_t *repos_id, } } - -/* 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 * @@ -575,12 +539,53 @@ blank_ibb(insert_base_baton_t *pibb) } -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) +/* 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. + + If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete + was recorded, otherwise to FALSE. + */ +static svn_error_t * +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; @@ -622,7 +627,7 @@ svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, } -/* This is the reverse of svn_wc__db_extend_parent_delete. +/* This is the reverse of 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 @@ -632,11 +637,11 @@ svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, 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) +static svn_error_t * +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; @@ -825,8 +830,8 @@ insert_base_node(const insert_base_baton_t *pibb, new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (pibb->kind == svn_node_dir && pibb->children) @@ -847,16 +852,16 @@ insert_base_node(const insert_base_baton_t *pibb, || (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)); + SVN_ERR(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)); + SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, + scratch_pool)); } } @@ -1101,8 +1106,8 @@ insert_working_node(const insert_working_baton_t *piwb, new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (piwb->kind == svn_node_dir) @@ -1148,159 +1153,44 @@ insert_working_node(const insert_working_baton_t *piwb, } -/* 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, + const char *parent_relpath, + int stmt_idx, + int op_depth, 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 *)); + apr_array_header_t *result; 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; + result = apr_array_make(result_pool, 16, sizeof(const char*)); - *children = apr_hash_make(result_pool); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath)); + if (op_depth >= 0) + SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth)); - 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)); + const char *name = svn_relpath_basename(child_relpath, result_pool); - *child_kind = svn_sqlite__column_token(stmt, 1, kind_map); - svn_hash_sets(*children, - svn_relpath_basename(child_relpath, result_pool), - child_kind); + APR_ARRAY_PUSH(result, const char *) = name; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + *children = result; return SVN_NO_ERROR; } - /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH. * Else, return FALSE. */ static svn_boolean_t @@ -1358,7 +1248,7 @@ flush_entries(svn_wc__db_wcroot_t *wcroot, hi; hi = apr_hash_next(hi)) { - const char *item_abspath = svn__apr_hash_index_key(hi); + const char *item_abspath = apr_hash_this_key(hi); if ((depth == svn_depth_files || depth == svn_depth_immediates) && is_immediate_child_path(local_abspath, item_abspath)) @@ -1485,12 +1375,12 @@ init_db(/* output values */ SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS)); SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS)); + SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool)); + /* 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)); @@ -1508,7 +1398,7 @@ init_db(/* output values */ *wc_id, /* 1 */ "", /* 2 */ 0, /* op_depth is 0 for base */ - NULL, /* 4 */ + SVN_VA_NULL, /* 4 */ *repos_id, root_node_repos_relpath, root_node_revision, @@ -1544,11 +1434,13 @@ create_db(svn_sqlite__db_t **sdb, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, svn_boolean_t exclusive, + apr_int32_t timeout, 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, + timeout, NULL /* my_statements */, result_pool, scratch_pool)); @@ -1577,6 +1469,8 @@ svn_wc__db_init(svn_wc__db_t *db, apr_int64_t wc_id; svn_wc__db_wcroot_t *wcroot; svn_boolean_t sqlite_exclusive = FALSE; + apr_int32_t sqlite_timeout = 0; /* default timeout */ + apr_hash_index_t *hi; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR_ASSERT(repos_relpath != NULL); @@ -1587,7 +1481,7 @@ svn_wc__db_init(svn_wc__db_t *db, /* ### 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_ERR(svn_config_get_bool(db->config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, FALSE)); @@ -1596,6 +1490,7 @@ svn_wc__db_init(svn_wc__db_t *db, 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, + sqlite_timeout, db->state_pool, scratch_pool)); /* Create the WCROOT for this directory. */ @@ -1603,9 +1498,31 @@ svn_wc__db_init(svn_wc__db_t *db, 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)); + /* Any previously cached children may now have a new WCROOT, most likely that + of the new WCROOT, but there might be descendant directories that are their + own working copy, in which case setting WCROOT to our new WCROOT might + actually break things for those. + + Clearing is the safest thing we can do in this case, as a test would lead + to unnecessary probing, while the standard code probes later anyway. So we + only lose a bit of memory + + ### Perhaps we could check wcroot->abspath to detect which case we have + where, but currently it is already very hard to trigger this from + the short living 'svn' client. (GUI clients like TortoiseSVN are far + more likely to get in these cases) + */ + for (hi = apr_hash_first(scratch_pool, db->dir_data); + hi; + hi = apr_hash_next(hi)) + { + const char *abspath = apr_hash_this_key(hi); + if (svn_dirent_is_ancestor(wcroot->abspath, abspath)) + svn_hash_sets(db->dir_data, abspath, NULL); + } + /* The WCROOT is complete. Stash it into DB. */ svn_hash_sets(db->dir_data, wcroot->abspath, wcroot); @@ -1714,10 +1631,10 @@ svn_wc__db_base_add_directory(svn_wc__db_t *db, 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 *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool) { @@ -2126,32 +2043,63 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, } /* 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. */ + * which moved a node to MOVED_TO_RELPATH. This transforms this side of the + * move into a simple copy. + */ static svn_error_t * -clear_moved_here(const char *src_relpath, - svn_wc__db_wcroot_t *wcroot, +clear_moved_here(svn_wc__db_wcroot_t *wcroot, + const char *moved_to_relpath, 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)); + int affected_rows; 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)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath, + relpath_depth(moved_to_relpath))); + + 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' was not found."), + path_for_error_message(wcroot, moved_to_relpath, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + const char *dst_relpath, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + int affected; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath, + delete_op_depth)); + SVN_ERR(svn_sqlite__update(&affected, stmt)); + + if (affected != 1) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("Path '%s' is not moved"), + path_for_error_message(wcroot, src_relpath, + scratch_pool)); + SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool)); + + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); return SVN_NO_ERROR; } + /* The body of svn_wc__db_base_remove(). */ static svn_error_t * @@ -2159,9 +2107,9 @@ 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_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2169,66 +2117,82 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; svn_wc__db_status_t status; + svn_revnum_t revision; apr_int64_t repos_id; const char *repos_relpath; svn_node_kind_t kind; svn_boolean_t keep_working; + int op_depth; + svn_node_kind_t wrk_kind; + svn_boolean_t no_delete_wc = FALSE; + svn_boolean_t file_external; - SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL, + SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision, &repos_relpath, &repos_id, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &file_external, wcroot, local_relpath, scratch_pool, scratch_pool)); - if (remove_locks) - { - svn_sqlite__stmt_t *lock_stmt; + /* Check if there is already a working node */ + 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__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 (!have_row) + return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */ + + op_depth = svn_sqlite__column_int(stmt, 0); + wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map); - if (status == svn_wc__db_status_normal - && keep_as_working) + if (op_depth > 0 + && op_depth == relpath_depth(local_relpath)) { - 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; + svn_wc__db_status_t presence; + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (presence == svn_wc__db_status_base_deleted) + { + keep_working = FALSE; + no_delete_wc = TRUE; + } + else + { + keep_working = TRUE; + } } else + keep_working = FALSE; + SVN_ERR(svn_sqlite__reset(stmt)); + + if (keep_as_working && op_depth == 0) { - /* 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)); + if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) + { + SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE, + NULL, NULL, + scratch_pool)); + } + keep_working = TRUE; } /* 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)) + if (!keep_working && !no_delete_wc) { 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) + if (wrk_kind == svn_node_dir) { apr_pool_t *iterpool; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_PRESENT)); + STMT_SELECT_WORKING_PRESENT)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); iterpool = svn_pool_create(scratch_pool); @@ -2307,27 +2271,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, ACTUAL_NODE records */ /* Step 3: Delete WORKING nodes */ - if (conflict) + if (!keep_working) { 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. + /* When deleting everything in working we should break moves from + here and to here. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE)); @@ -2338,12 +2287,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, iterpool = svn_pool_create(scratch_pool); while (have_row) { - const char *child_relpath; + const char *moved_to_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); + moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + err = clear_moved_here(wcroot, moved_to_relpath, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -2351,11 +2300,52 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, svn_pool_destroy(iterpool); SVN_ERR(svn_sqlite__reset(stmt)); } - if (keep_working) + else { + /* We are keeping things that are in WORKING, but we should still + break moves of things in BASE. (Mixed revisions make it + impossible to guarantee that we can keep everything moved) */ + + apr_pool_t *iterpool; + 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)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, 0)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + iterpool = svn_pool_create(scratch_pool); + while (have_row) + { + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath; + const char *dst_relpath; + svn_error_t *err; + + svn_pool_clear(iterpool); + + src_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool); + + err = svn_wc__db_op_break_move_internal(wcroot, src_relpath, + delete_op_depth, + dst_relpath, + NULL, + 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_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); SVN_ERR(svn_sqlite__step_done(stmt)); } else @@ -2372,42 +2362,69 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, 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)); - } + SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool)); - if (SVN_IS_VALID_REVNUM(not_present_revision)) + if (mark_not_present || mark_excluded) { struct insert_base_baton_t ibb; - blank_ibb(&ibb); + svn_boolean_t no_marker = FALSE; - 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; + if (file_external) + { + const char *parent_local_relpath; + const char *name; + svn_error_t *err; - /* Depending upon KIND, any of these might get used. */ - ibb.children = NULL; - ibb.depth = svn_depth_unknown; - ibb.checksum = NULL; - ibb.target = NULL; + /* For file externals we only want to place a not present marker + if there is a BASE parent */ + + svn_relpath_split(&parent_local_relpath, &name, local_relpath, + scratch_pool); - SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + 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, parent_local_relpath, + 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); + no_marker = TRUE; + } + else + { + /* Replace the repos_relpath with something more expected than + the unrelated old file external repository relpath, which + one day may come from a different repository */ + repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool); + } + } + + if (!no_marker) + { + blank_ibb(&ibb); + + ibb.repos_id = repos_id; + ibb.status = mark_excluded ? svn_wc__db_status_excluded + : svn_wc__db_status_not_present; + ibb.kind = kind; + ibb.repos_relpath = repos_relpath; + ibb.revision = SVN_IS_VALID_REVNUM(marker_revision) + ? marker_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; + + SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + } } SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -2423,9 +2440,9 @@ 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_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2440,8 +2457,9 @@ svn_wc__db_base_remove(svn_wc__db_t *db, 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, + db, keep_as_working, + mark_not_present, mark_excluded, + marker_revision, conflict, work_items, scratch_pool), wcroot); @@ -2638,7 +2656,7 @@ svn_wc__db_base_get_info(svn_wc__db_status_t *status, wcroot, local_relpath, result_pool, scratch_pool), svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, result_pool), + wcroot, repos_id, result_pool), SVN_NO_ERROR, SVN_NO_ERROR, wcroot); @@ -2647,28 +2665,26 @@ svn_wc__db_base_get_info(svn_wc__db_status_t *status, 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) +/* The implementation of svn_wc__db_base_get_children_info */ +static svn_error_t * +base_get_children_info(apr_hash_t **nodes, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t obtain_locks, + 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); + apr_int64_t last_repos_id = INVALID_REPOS_ID; + const char *last_repos_root_url = NULL; *nodes = apr_hash_make(result_pool); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_CHILDREN_INFO)); + obtain_locks + ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK + : 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)); @@ -2676,7 +2692,6 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, 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); @@ -2694,16 +2709,26 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, info->update_root = svn_sqlite__column_boolean(stmt, 7); - info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool); + if (obtain_locks) + 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 (repos_id != last_repos_id) + { + svn_error_t *err; - if (err) - return svn_error_trace( - svn_error_compose_create(err, - svn_sqlite__reset(stmt))); + err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL, + wcroot, repos_id, + result_pool); + + if (err) + return svn_error_trace( + svn_error_compose_create(err, + svn_sqlite__reset(stmt))); + + last_repos_id = repos_id; + } + info->repos_root_url = last_repos_root_url; svn_hash_sets(*nodes, name, info); @@ -2715,6 +2740,30 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, 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_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); + + return svn_error_trace(base_get_children_info(nodes, + wcroot, + local_relpath, + TRUE /* obtain_locks */, + result_pool, + scratch_pool)); +} + svn_error_t * svn_wc__db_base_get_props(apr_hash_t **props, @@ -2761,8 +2810,10 @@ svn_wc__db_base_get_children(const apr_array_header_t **children, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_repo_children(children, wcroot, local_relpath, 0, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, 0, + result_pool, scratch_pool)); } @@ -2772,12 +2823,20 @@ svn_wc__db_base_set_dav_cache(svn_wc__db_t *db, const apr_hash_t *props, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; 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_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_UPDATE_BASE_NODE_DAV_CACHE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); @@ -2799,11 +2858,20 @@ svn_wc__db_base_get_dav_cache(apr_hash_t **props, 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(get_statement_for_path(&stmt, db, local_abspath, - STMT_SELECT_BASE_DAV_CACHE, 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, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_BASE_DAV_CACHE)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (!have_row) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, @@ -2944,21 +3012,21 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, } if (had_props) { - *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13); + *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12); } 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, + SVN_ERR(svn_sqlite__column_properties(props, stmt, 12, result_pool, scratch_pool)); if (*props == NULL) *props = apr_hash_make(result_pool); } else { - assert(svn_sqlite__column_is_null(stmt, 13)); + assert(svn_sqlite__column_is_null(stmt, 12)); *props = NULL; } } @@ -2975,6 +3043,11 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } +/* 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); /* Baton for passing args to with_triggers(). */ struct with_triggers_baton_t { @@ -3051,8 +3124,13 @@ with_finalization(svn_wc__db_wcroot_t *wcroot, svn_error_t *err1; svn_error_t *err2; - err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton, - scratch_pool); + err1 = svn_sqlite__begin_savepoint(wcroot->sdb); + if (!err1) + { + err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool); + + err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1); + } if (err1 == NULL && notify_func != NULL) { @@ -3438,11 +3516,18 @@ db_external_remove(const svn_skel_t *work_items, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + int affected_rows; 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(svn_sqlite__update(&affected_rows, stmt)); + + if (!affected_rows) + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' is not an external."), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -3543,7 +3628,7 @@ svn_wc__db_external_read(svn_wc__db_status_t *status, err = svn_error_compose_create( err, svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, + wcroot, repos_id, result_pool)); } @@ -3925,14 +4010,14 @@ get_moved_to(const char **moved_to_relpath_p, /* 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) +scan_deletion(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; @@ -3956,9 +4041,7 @@ scan_deletion_txn(const char **base_del_relpath, 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)); + &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -4078,6 +4161,25 @@ scan_deletion_txn(const char **base_del_relpath, return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_scan_deletion_internal( + 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) +{ + return svn_error_trace( + scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath, + moved_to_op_root_relpath, + wcroot, local_relpath, + result_pool, scratch_pool)); +} + + svn_error_t * svn_wc__db_scan_deletion(const char **base_del_abspath, const char **moved_to_abspath, @@ -4100,9 +4202,9 @@ svn_wc__db_scan_deletion(const char **base_del_abspath, 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), + scan_deletion(&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) @@ -4200,10 +4302,10 @@ get_info_for_copy(apr_int64_t *copyfrom_id, { 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)); + SVN_ERR(scan_deletion(&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; @@ -4264,7 +4366,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, working copies in a single db)! */ SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - src_wcroot->sdb, *copyfrom_id, + src_wcroot, *copyfrom_id, scratch_pool)); SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid, @@ -4532,8 +4634,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcroot, 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)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, scratch_pool)); } else children = NULL; @@ -4684,25 +4787,23 @@ struct op_copy_baton const char *dst_op_root_relpath; }; -/* Helper for svn_wc__db_op_copy(). - * - * Implements svn_sqlite__transaction_callback_t. */ +/* Helper for svn_wc__db_op_copy(). */ static svn_error_t * -op_copy_txn(void * baton, - svn_sqlite__db_t *sdb, +op_copy_txn(svn_wc__db_wcroot_t *wcroot, + struct op_copy_baton *ocb, apr_pool_t *scratch_pool) { - struct op_copy_baton *ocb = baton; int move_op_depth; - if (sdb != ocb->dst_wcroot->sdb) + if (wcroot != ocb->dst_wcroot) { /* Source and destination databases differ; so also start a lock - in the destination database, by calling ourself in a lock. */ + in the destination database, by calling ourself in an extra lock. */ + + SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool), + ocb->dst_wcroot); - return svn_error_trace( - svn_sqlite__with_lock(ocb->dst_wcroot->sdb, - op_copy_txn, ocb, scratch_pool)); + return SVN_NO_ERROR; } /* From this point we can assume a lock in the src and dst databases */ @@ -4753,84 +4854,313 @@ svn_wc__db_op_copy(svn_wc__db_t *db, /* 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)); + SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool), + ocb.src_wcroot); return SVN_NO_ERROR; } -/* The txn body of svn_wc__db_op_handle_move_back */ +/* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */ 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) +clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + 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_boolean_t have_row, shadowed; + svn_boolean_t keep_conflict = FALSE; - SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NODE_INFO)); - 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)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (status != svn_wc__db_status_added || !op_root) - return SVN_NO_ERROR; + if (have_row) + { + svn_wc__db_status_t presence; - /* 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))); + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (shadowed && presence == svn_wc__db_status_base_deleted) + { + keep_conflict = TRUE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + else + shadowed = FALSE; + } + } else - from_op_depth = 0; + shadowed = FALSE; - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_BACK)); + SVN_ERR(svn_sqlite__reset(stmt)); + if (shadowed) + return SVN_NO_ERROR; - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, - local_relpath, - from_op_depth, - relpath_depth(local_relpath))); + if (keep_conflict) + { + /* We don't want to accidentally remove delete-delete conflicts */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT)); + 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)); + } + else + { + 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)); + } - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + return SVN_NO_ERROR; +} - SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */ +svn_error_t * +svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_op_relpath, + int src_op_depth, + const char *dst_op_relpath, + svn_skel_t *conflict, + svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt, *stmt2; + svn_boolean_t have_row; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int dst_op_depth = relpath_depth(dst_op_relpath); + svn_boolean_t locked; + svn_error_t *err = NULL; - { - svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9); - const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL); + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath, + FALSE, scratch_pool)); - if (!moved_here - || !moved_to - || strcmp(moved_to, moved_from_relpath)) - { - different = TRUE; - have_row = FALSE; - } - } + if (!locked) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, dst_op_relpath, + scratch_pool)); + SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_COPY_NODE_MOVE)); + + /* Replace entire subtree at one op-depth. */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_LAYER_FOR_REPLACE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id, + src_op_relpath, src_op_depth, + dst_op_relpath, dst_op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { - svn_wc__db_status_t upper_status; - svn_wc__db_status_t lower_status; + const char *src_relpath; + const char *dst_relpath; - upper_status = svn_sqlite__column_token(stmt, 1, presence_map); + svn_pool_clear(iterpool); - if (svn_sqlite__column_is_null(stmt, 5)) + src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool); + + err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, + src_relpath, src_op_depth, + dst_relpath, dst_op_depth, + svn_relpath_dirname(dst_relpath, iterpool)); + if (!err) + err = svn_sqlite__step_done(stmt2); + + /* stmt2 is reset (never modified or by step_done) */ + + if (err) + break; + + /* The node can't be deleted where it is added, so extension of + an existing shadowing is only interesting 2 levels deep. */ + if (relpath_depth(dst_relpath) > (dst_op_depth+1)) { - /* No lower layer replaced. */ + svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3); + + if (exists) + { + svn_wc__db_status_t presence; + + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (presence != svn_wc__db_status_normal) + exists = FALSE; + } + + if (!exists) + { + svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map); + + err = db_extend_parent_delete(wcroot, dst_relpath, + kind, dst_op_depth, iterpool); + + if (err) + break; + } + } + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + /* And now remove the records that are no longer needed */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NO_LONGER_MOVED_RV)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id, + dst_op_relpath, dst_op_depth, + src_op_relpath, src_op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *dst_relpath; + svn_wc__db_status_t shadowed_presence; + + svn_pool_clear(iterpool); + + dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + + if (!svn_sqlite__column_is_null(stmt, 2)) + shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map); + else + shadowed_presence = svn_wc__db_status_not_present; + + if (shadowed_presence != svn_wc__db_status_normal + && shadowed_presence != svn_wc__db_status_incomplete) + { + err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_DELETE_NODE); + } + else + { + err =svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_REPLACE_WITH_BASE_DELETED); + } + + if (!err) + err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath, + dst_op_depth); + + if (!err) + err = svn_sqlite__step_done(stmt2); + + /* stmt2 is reset (never modified or by step_done) */ + if (err) + break; + + /* Delete ACTUAL information about this node that we just deleted */ + err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth, + scratch_pool); + + if (err) + break; + + /* Retract base-delete for the node itself */ + err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth, + scratch_pool); + + 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))); + + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + + if (conflict) + SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */, + conflict, 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; @@ -4919,7 +5249,7 @@ handle_move_back(svn_boolean_t *moved_back, 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)); + STMT_DELETE_WORKING_OP_DEPTH)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, @@ -5044,7 +5374,7 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, const char *repos_uuid; SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - src_wcroot->sdb, node_repos_id, + src_wcroot, node_repos_id, scratch_pool)); SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid, @@ -5166,8 +5496,9 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, return SVN_NO_ERROR; } - SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath, - src_op_depth, scratch_pool, iterpool)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -5196,15 +5527,12 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, return SVN_NO_ERROR; } -/* Helper for svn_wc__db_op_copy_shadowed_layer(). - * - * Implements svn_sqlite__transaction_callback_t. */ +/* Helper for svn_wc__db_op_copy_shadowed_layer(). */ static svn_error_t * -op_copy_shadowed_layer_txn(void *baton, - svn_sqlite__db_t *sdb, +op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot, + struct op_copy_baton *ocb, 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; @@ -5214,15 +5542,16 @@ op_copy_shadowed_layer_txn(void *baton, apr_int64_t repos_id = INVALID_REPOS_ID; svn_revnum_t revision = SVN_INVALID_REVNUM; - if (sdb != ocb->dst_wcroot->sdb) + if (wcroot != ocb->dst_wcroot) { - /* Source and destination databases differ; so also start a lock - in the destination database, by calling ourself in a lock. */ + /* Source and destination databases differ; so also start a lock + in the destination database, by calling ourself in an extra lock. */ + + SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb, + scratch_pool), + ocb->dst_wcroot); - return svn_error_trace( - svn_sqlite__with_lock(ocb->dst_wcroot->sdb, - op_copy_shadowed_layer_txn, - ocb, scratch_pool)); + return SVN_NO_ERROR; } /* From this point we can assume a lock in the src and dst databases */ @@ -5304,9 +5633,9 @@ svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db, /* 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)); + SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb, + scratch_pool), + ocb.src_wcroot); return SVN_NO_ERROR; } @@ -5776,27 +6105,39 @@ svn_wc__db_global_record_fileinfo(svn_wc__db_t *db, * 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, +set_actual_props(svn_wc__db_wcroot_t *wcroot, 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__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->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 */ + { + /* Perhaps the entire ACTUAL record is unneeded now? */ + if (!props && affected_rows) + { + 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)); + } + + 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)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_INSERT_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); if (*local_relpath != '\0') SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_relpath_dirname(local_relpath, @@ -5805,6 +6146,24 @@ set_actual_props(apr_int64_t wc_id, return svn_error_trace(svn_sqlite__step_done(stmt)); } +svn_error_t * +svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_hash_t *props, + svn_boolean_t clear_recorded_info, + apr_pool_t *scratch_pool) +{ + SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool)); + + if (clear_recorded_info) + { + SVN_ERR(db_record_fileinfo(wcroot, local_relpath, + SVN_INVALID_FILESIZE, 0, + scratch_pool)); + } + + return SVN_NO_ERROR; +} /* The body of svn_wc__db_op_set_props(). @@ -5840,15 +6199,8 @@ set_props_txn(svn_wc__db_wcroot_t *wcroot, 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)); - } + SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props, + clear_recorded_info, scratch_pool)); /* And finally. */ SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -6080,7 +6432,7 @@ set_changelist_txn(void *baton, if (scb->new_changelist) { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_ACTUAL_EMPTIES)); + STMT_INSERT_ACTUAL_EMPTIES_FILES)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -6297,15 +6649,15 @@ svn_wc__db_op_mark_conflict(svn_wc__db_t *db, /* 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_error_t * +svn_wc__db_op_mark_resolved_internal(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; @@ -6403,7 +6755,8 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - db_op_mark_resolved(wcroot, local_relpath, db, + svn_wc__db_op_mark_resolved_internal( + wcroot, local_relpath, db, resolved_text, resolved_props, resolved_tree, work_items, scratch_pool), wcroot); @@ -6412,40 +6765,116 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, 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. */ +/* Clear moved-to information at the delete-half of the move which moved + * MOVED_TO_RELPATH here. This transforms the delete part of the move into a + * normal delete. + * + * Note that the moved-to location is always an op-root, while this is not the + * case for a moved-from location. + */ static svn_error_t * -clear_moved_to(const char *local_relpath, - svn_wc__db_wcroot_t *wcroot, +clear_moved_to(svn_wc__db_wcroot_t *wcroot, + const char *moved_to_relpath, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; const char *moved_from_relpath; + int moved_from_op_depth; 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; - } + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath)); + SVN_ERR(svn_sqlite__step_row(stmt)); moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + moved_from_op_depth = svn_sqlite__column_int(stmt, 1); 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)); + moved_from_relpath, moved_from_op_depth)); + SVN_ERR(svn_sqlite__update(NULL, stmt)); + + return SVN_NO_ERROR; +} + +/* Helper function for op_revert_txn. Raises move tree conflicts on + descendants to ensure database stability on a non recursive revert + of an ancestor that contains a possible move related tree conflict. + */ +static svn_error_t * +revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot, + svn_wc__db_t *db, + const char *local_relpath, + int op_depth_below, + apr_pool_t *scratch_pool) +{ + svn_skel_t *conflict; + svn_wc_operation_t operation; + svn_boolean_t tree_conflicted; + const apr_array_header_t *locations; + svn_wc_conflict_reason_t reason; + svn_wc_conflict_action_t action; + + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot, + local_relpath, + scratch_pool, scratch_pool)); + + if (!conflict) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, 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)) + { + return SVN_NO_ERROR; + } + + 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 + || reason == svn_wc_conflict_reason_replaced) + { + SVN_ERR(svn_wc__db_op_raise_moved_away_internal( + wcroot, local_relpath, op_depth_below, db, + operation, action, + (locations && locations->nelts > 0) + ? APR_ARRAY_IDX(locations, 0, + const svn_wc_conflict_version_t *) + : NULL, + (locations && locations->nelts > 1) + ? APR_ARRAY_IDX(locations, 1, + const svn_wc_conflict_version_t *) + : NULL, + scratch_pool)); + + /* Transform the move information into revert information */ + SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, + STMT_MOVE_NOTIFY_TO_REVERT)); + } return SVN_NO_ERROR; } +/* Baton for op_revert_txn and op_revert_recursive_txn */ +struct revert_baton_t +{ + svn_wc__db_t *db; + svn_boolean_t clear_changelists; +}; + /* One of the two alternative bodies of svn_wc__db_op_revert(). * * Implements svn_wc__db_txn_callback_t. */ @@ -6455,13 +6884,15 @@ op_revert_txn(void *baton, const char *local_relpath, apr_pool_t *scratch_pool) { - svn_wc__db_t *db = baton; + struct revert_baton_t *rvb = baton; + svn_wc__db_t *db = rvb->db; 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; + int op_depth_below; /* ### Similar structure to op_revert_recursive_txn, should they be combined? */ @@ -6508,58 +6939,26 @@ op_revert_txn(void *baton, 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)); - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + op_depth_below = svn_sqlite__column_int(stmt, 0); 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; + op_depth_below = -1; - 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)); + SVN_ERR(svn_sqlite__reset(stmt)); - 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 (moved_to) + { + SVN_ERR(svn_wc__db_op_break_move_internal(wcroot, + local_relpath, op_depth, + moved_to, NULL, scratch_pool)); } if (op_depth > 0 && op_depth == relpath_depth(local_relpath)) { + int op_depth_increased; + /* Can't do non-recursive revert if children exist */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_GE_OP_DEPTH_CHILDREN)); @@ -6582,7 +6981,7 @@ op_revert_txn(void *baton, 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__update(&op_depth_increased, stmt)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WORKING_NODE)); @@ -6597,19 +6996,35 @@ op_revert_txn(void *baton, /* 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(clear_moved_to(wcroot, local_relpath, scratch_pool)); + + /* If the node was moved itself, we don't have interesting moved + children (and the move itself was already broken) */ + if (op_depth_increased && !moved_to) + SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath, + op_depth_below, 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) + if (rvb->clear_changelists) + { + 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)); + } + else { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST)); + 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; @@ -6625,6 +7040,7 @@ op_revert_recursive_txn(void *baton, const char *local_relpath, apr_pool_t *scratch_pool) { + struct revert_baton_t *rvb = baton; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; int op_depth; @@ -6680,14 +7096,14 @@ op_revert_recursive_txn(void *baton, SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { - const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *dst_relpath = svn_sqlite__column_text(stmt, 1, 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); + err = svn_wc__db_op_break_move_internal(wcroot, + src_relpath, move_op_depth, + dst_relpath, NULL, scratch_pool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); @@ -6706,17 +7122,28 @@ op_revert_recursive_txn(void *baton, 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)); + if (rvb->clear_changelists) + { + 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 + { + 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_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, @@ -6740,7 +7167,7 @@ op_revert_recursive_txn(void *baton, 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); + err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool); if (err) return svn_error_trace(svn_error_compose_create( err, @@ -6754,7 +7181,7 @@ op_revert_recursive_txn(void *baton, /* 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)); + SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool)); return SVN_NO_ERROR; } @@ -6763,22 +7190,27 @@ svn_error_t * svn_wc__db_op_revert(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t clear_changelists, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + struct revert_baton_t rvb; 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)); + rvb.db = db; + rvb.clear_changelists = clear_changelists; + wtb.cb_baton = &rvb; + 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; @@ -6914,7 +7346,7 @@ svn_wc__db_revert_list_read(svn_boolean_t *reverted, 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_array_header_t **children_p, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -6957,7 +7389,7 @@ revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot, svn_error_t * -svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children, +svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -7004,16 +7436,39 @@ svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func, while (have_row) { const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL); + svn_wc_notify_t *notify; 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); + notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, + notify_relpath, + iterpool), + svn_wc_notify_revert, + iterpool); + + if (!svn_sqlite__column_is_null(stmt, 1)) + notify->kind = svn_sqlite__column_token(stmt, 1, kind_map); + else + { + if (!svn_sqlite__column_is_null(stmt, 3)) + notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none); + + switch (svn_sqlite__column_int(stmt, 2)) + { + case 0: + continue; + case 1: + /* standard revert */ + break; + case 2: + notify->action = svn_wc_notify_tree_conflict; + break; + default: + SVN_ERR_MALFUNCTION(); + } + } + + notify_func(notify_baton, notify, iterpool); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -7055,9 +7510,6 @@ remove_node_txn(svn_boolean_t *left_changes, 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, @@ -7066,9 +7518,6 @@ remove_node_txn(svn_boolean_t *left_changes, { 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! */ @@ -7078,15 +7527,6 @@ remove_node_txn(svn_boolean_t *left_changes, 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')) { @@ -7283,26 +7723,6 @@ remove_node_txn(svn_boolean_t *left_changes, 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, @@ -7317,9 +7737,6 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, 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, @@ -7338,8 +7755,7 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, 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, + conflict, work_items, cancel_func, cancel_baton, scratch_pool), wcroot); @@ -7371,7 +7787,7 @@ db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot, if (affected_rows == 0) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - "The node '%s' is not a committed directory", + _("The node '%s' is not a committed directory"), path_for_error_message(wcroot, local_relpath, scratch_pool)); @@ -8109,7 +8525,7 @@ 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, @@ -8344,6 +8760,45 @@ svn_wc__db_op_delete_many(svn_wc__db_t *db, scratch_pool)); } +/* Helper function for read_info() to provide better diagnostics than just + asserting. + + ### BH: Yes this code is ugly, and that is why I only introduce it in + ### read_info(). But we really need something to determine the root cause + ### of this problem to diagnose why TortoiseSVN users were seeing all those + ### assertions. + + Adds an error to the *err chain if invalid values are encountered. In that + case the value is set to the first value in the map, assuming that caller + will just return the combined error. + */ +static int +column_token_err(svn_error_t **err, + svn_sqlite__stmt_t *stmt, + int column, + const svn_token_map_t *map) +{ + svn_error_t *err2; + const char *word = svn_sqlite__column_text(stmt, column, NULL); + int value; + + /* svn_token__from_word_err() handles NULL for us */ + err2 = svn_token__from_word_err(&value, map, word); + + if (err2) + { + *err = svn_error_compose_create( + *err, + svn_error_createf( + SVN_ERR_WC_CORRUPT, err2, + _("Encountered invalid node state in column %d of " + "info query to working copy database"), + column)); + value = map[0].val; + } + + return value; +} /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */ @@ -8411,11 +8866,11 @@ read_info(svn_wc__db_status_t *status, 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); + node_kind = column_token_err(&err, stmt_info, 4, kind_map); if (status) { - *status = svn_sqlite__column_token(stmt_info, 3, presence_map); + *status = column_token_err(&err, stmt_info, 3, presence_map); if (op_depth != 0) /* WORKING */ err = svn_error_compose_create(err, @@ -8467,14 +8922,11 @@ read_info(svn_wc__db_status_t *status, if (depth) { if (node_kind != svn_node_dir) - { - *depth = svn_depth_unknown; - } + *depth = svn_depth_unknown; + else if (svn_sqlite__column_is_null(stmt_info, 11)) + *depth = svn_depth_unknown; else - { - *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map, - svn_depth_unknown); - } + *depth = column_token_err(&err, stmt_info, 11, depth_map); } if (checksum) { @@ -8666,10 +9118,8 @@ read_info(svn_wc__db_status_t *status, 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)); + err = svn_error_quick_wrapf(err, _("Error reading node '%s'"), + local_relpath); SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info))); @@ -8773,9 +9223,9 @@ svn_wc__db_read_info(svn_wc__db_status_t *status, have_base, have_more_work, have_work, wcroot, local_relpath, result_pool, scratch_pool), svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, result_pool), + wcroot, repos_id, result_pool), svn_wc__db_fetch_repos_info(original_root_url, original_uuid, - wcroot->sdb, original_repos_id, + wcroot, original_repos_id, result_pool), SVN_NO_ERROR, wcroot); @@ -8789,6 +9239,27 @@ is_wclocked(svn_boolean_t *locked, const char *dir_relpath, apr_pool_t *scratch_pool); +/* Helper for read_children_info and single variant */ +static svn_error_t * +find_conflict_descendants(svn_boolean_t *conflict_exists, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + + /* Only used on files, so certainly not wcroot*/ + assert(local_relpath[0] != '\0'); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_FIND_CONFLICT_DESCENDANT)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(conflict_exists, stmt)); + + return svn_error_trace(svn_sqlite__reset(stmt)); +} + /* 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 @@ -8796,13 +9267,16 @@ struct read_children_info_item_t struct svn_wc__db_info_t info; int op_depth; int nr_layers; + svn_boolean_t was_dir; }; +/* Implementation of svn_wc__db_read_children_info */ 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, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -8811,9 +9285,12 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, const char *repos_root_url = NULL; const char *repos_uuid = NULL; apr_int64_t last_repos_id = INVALID_REPOS_ID; + const char *last_repos_root_url = NULL; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_NODE_CHILDREN_INFO)); + (base_tree_only + ? STMT_SELECT_BASE_NODE_CHILDREN_INFO + : 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)); @@ -8828,7 +9305,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, int op_depth; svn_boolean_t new_child; - child_item = svn_hash_gets(nodes, name); + child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name)); if (child_item) new_child = FALSE; else @@ -8840,7 +9317,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, op_depth = svn_sqlite__column_int(stmt, 0); /* Do we have new or better information? */ - if (new_child || op_depth > child_item->op_depth) + if (new_child) { struct svn_wc__db_info_t *child = &child_item->info; child_item->op_depth = op_depth; @@ -8875,8 +9352,6 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, } 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 && @@ -8885,7 +9360,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, last_repos_root_url = repos_root_url; err = svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - wcroot->sdb, repos_id, + wcroot, repos_id, result_pool); if (err) SVN_ERR(svn_error_compose_create(err, @@ -8923,11 +9398,19 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, child->depth = svn_depth_unknown; else { + child->has_descendants = TRUE; + child_item->was_dir = TRUE; 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)); + { + err = is_wclocked(&child->locked, wcroot, child_relpath, + scratch_pool); + + if (err) + SVN_ERR(svn_error_compose_create(err, + svn_sqlite__reset(stmt))); + } } child->recorded_time = svn_sqlite__column_int64(stmt, 13); @@ -8959,6 +9442,17 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, if (new_child) svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child); } + else if (!child_item->was_dir + && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir) + { + child_item->was_dir = TRUE; + + err = find_conflict_descendants(&child_item->info.has_descendants, + wcroot, child_relpath, + scratch_pool); + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + } if (op_depth == 0) { @@ -8990,22 +9484,14 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, 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--; - } + shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth, + scratch_pool); moved_to->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, shadow_op_relpath, @@ -9028,54 +9514,58 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, 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) + if (!base_tree_only) { - 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); + 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)); - child_item = svn_hash_gets(nodes, name); - if (!child_item) + while (have_row) { - child_item = apr_pcalloc(result_pool, sizeof(*child_item)); - child_item->info.status = svn_wc__db_status_not_present; - } + 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 = &child_item->info; - child->changelist = svn_sqlite__column_text(stmt, 1, result_pool); + child->changelist = svn_sqlite__column_text(stmt, 1, result_pool); - child->props_mod = !svn_sqlite__column_is_null(stmt, 2); + 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; + 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)); - } + 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 */ + /* conflict */ + child->conflicted = !svn_sqlite__column_is_null(stmt, 3); - if (child->conflicted) - svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), ""); + if (child->conflicted) + svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), ""); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + } return SVN_NO_ERROR; } @@ -9085,6 +9575,7 @@ svn_wc__db_read_children_info(apr_hash_t **nodes, apr_hash_t **conflicts, svn_wc__db_t *db, const char *dir_abspath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9102,23 +9593,29 @@ svn_wc__db_read_children_info(apr_hash_t **nodes, SVN_WC__DB_WITH_TXN( read_children_info(wcroot, dir_relpath, *conflicts, *nodes, - result_pool, scratch_pool), + base_tree_only, 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); +/* Implementation of svn_wc__db_read_single_info. + ### This function is very similar to a lot of code inside + read_children_info, but that performs some tricks to re-use data between + different siblings. + + ### We read the same few NODES records a few times via different helper + functions, so this could be optimized bit, but everything is within + a sqlite transaction and all queries are backed by an index, so generally + everything (including the used indexes) should be in the sqlite page cache + after the first query. +*/ 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, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9127,27 +9624,43 @@ read_single_info(const struct svn_wc__db_info_t **info, const svn_checksum_t *checksum; const char *original_repos_relpath; svn_boolean_t have_work; + apr_hash_t *properties; 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)); + if (!base_tree_only) + 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)); + else + { + svn_boolean_t update_root; + + have_work = FALSE; + original_repos_relpath = NULL; + + SVN_ERR(svn_wc__db_base_get_info_internal( + &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath, + &repos_id, &mtb->changed_rev, &mtb->changed_date, + &mtb->changed_author, &mtb->depth, &checksum, NULL, + &mtb->lock, &mtb->had_props, &properties, &update_root, + wcroot, local_relpath, scratch_pool, scratch_pool)); + + mtb->have_base = TRUE; + mtb->file_external = (update_root && mtb->kind == svn_node_file); + } /* 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)); @@ -9160,22 +9673,16 @@ read_single_info(const struct svn_wc__db_info_t **info, 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); + const char *cur_relpath; 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--; - } + cur_relpath = svn_relpath_prefix(local_relpath, op_depth, + scratch_pool); + move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, cur_relpath, result_pool); @@ -9192,7 +9699,8 @@ read_single_info(const struct svn_wc__db_info_t **info, /* 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)) + if (!base_tree_only && mtb->have_base + && (have_work || mtb->kind == svn_node_file)) { svn_boolean_t update_root; svn_wc__db_lock_t **lock_arg = NULL; @@ -9225,18 +9733,20 @@ read_single_info(const struct svn_wc__db_info_t **info, #ifdef HAVE_SYMLINK if (mtb->kind == svn_node_file - && (mtb->had_props || mtb->props_mod)) + && (mtb->had_props || mtb->props_mod + || (base_tree_only && properties))) { - 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)); + if (!base_tree_only) + { + if (mtb->props_mod) + SVN_ERR(svn_wc__db_read_props_internal(&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)); } @@ -9246,11 +9756,17 @@ read_single_info(const struct svn_wc__db_info_t **info, 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)); + wcroot, repos_id, result_pool)); - if (mtb->kind == svn_node_dir) + if (!base_tree_only && mtb->kind == svn_node_dir) SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool)); + if (mtb->kind == svn_node_dir) + mtb->has_descendants = TRUE; + else + SVN_ERR(find_conflict_descendants(&mtb->has_descendants, + wcroot, local_relpath, scratch_pool)); + *info = mtb; return SVN_NO_ERROR; @@ -9260,6 +9776,7 @@ 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, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9274,6 +9791,7 @@ svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath, + base_tree_only, result_pool, scratch_pool), wcroot); @@ -9443,7 +9961,7 @@ svn_wc__db_read_pristine_info(svn_wc__db_status_t *status, } svn_error_t * -svn_wc__db_read_children_walker_info(apr_hash_t **nodes, +svn_wc__db_read_children_walker_info(const apr_array_header_t **items, svn_wc__db_t *db, const char *dir_abspath, apr_pool_t *result_pool, @@ -9453,6 +9971,7 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, const char *dir_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + apr_array_header_t *nodes; SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); @@ -9466,16 +9985,18 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, 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); + nodes = apr_array_make(result_pool, 16, + sizeof(struct svn_wc__db_walker_info_t *)); 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); + const char *name = svn_relpath_basename(child_relpath, result_pool); int op_depth = svn_sqlite__column_int(stmt, 1); svn_error_t *err; child = apr_palloc(result_pool, sizeof(*child)); + child->name = name; child->status = svn_sqlite__column_token(stmt, 2, presence_map); if (op_depth > 0) { @@ -9484,13 +10005,16 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, 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); + + APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } SVN_ERR(svn_sqlite__reset(stmt)); + *items = nodes; + return SVN_NO_ERROR; } @@ -9545,7 +10069,7 @@ svn_wc__db_read_node_install_info(const char **wcroot_abspath, if (have_row) { - if (!err && sha1_checksum) + if (sha1_checksum) err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool); if (!err && pristine_props) @@ -9574,68 +10098,48 @@ svn_wc__db_read_node_install_info(const char **wcroot_abspath, -/* The body of svn_wc__db_read_url(). +/* The body of svn_wc__db_read_repos_info(). */ 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) +db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + apr_int64_t *repos_id, + 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, + SVN_ERR(read_info(&status, NULL, revision, 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)); + NULL, NULL, NULL, + wcroot, local_relpath, result_pool, scratch_pool)); - if (repos_relpath == NULL) + if ((repos_relpath && !*repos_relpath) + || (repos_id && *repos_id == INVALID_REPOS_ID)) { if (status == svn_wc__db_status_added) { - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL, + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, local_relpath, - scratch_pool, scratch_pool)); + result_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)); + SVN_ERR(scan_deletion(&base_del_relpath, NULL, + &work_del_relpath, + NULL, wcroot, + local_relpath, + scratch_pool, + scratch_pool)); - repos_relpath = svn_relpath_join( - repos_relpath, - svn_dirent_skip_ancestor(base_del_relpath, - local_relpath), - scratch_pool); - } - else + if (work_del_relpath) { /* The parent of the WORKING delete, must be an addition */ const char *work_relpath = NULL; @@ -9648,34 +10152,57 @@ read_url_txn(const char **url, work_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool); - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, + 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, + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, svn_dirent_skip_ancestor(work_relpath, local_relpath), - scratch_pool); + result_pool); + } + else + { + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision, + repos_relpath, + repos_id, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + wcroot, + base_del_relpath, + scratch_pool, + scratch_pool)); + + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, + svn_dirent_skip_ancestor(base_del_relpath, + local_relpath), + result_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. */ + /* A BASE excluded would have had repository information, so + we have a working exclude, which must be below an addition */ + svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool); - SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, parent_relpath, + scratch_pool, scratch_pool)); - *url = svn_path_url_add_component2(url2, name, result_pool); + if (repos_relpath) + *repos_relpath = svn_relpath_join(*repos_relpath, name, + result_pool); return SVN_NO_ERROR; } @@ -9687,26 +10214,23 @@ read_url_txn(const char **url, } } - 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_read_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) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + apr_int64_t repos_id = INVALID_REPOS_ID; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -9715,9 +10239,17 @@ svn_wc__db_read_url(const char **url, 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); + SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath, + (repos_root_url || repos_uuid) + ? &repos_id : NULL, + wcroot, local_relpath, + result_pool, scratch_pool), + svn_wc__db_fetch_repos_info(repos_root_url, + repos_uuid, + wcroot, repos_id, + result_pool), + SVN_NO_ERROR, SVN_NO_ERROR, + wcroot); return SVN_NO_ERROR; } @@ -9861,12 +10393,12 @@ svn_wc__db_read_props_streamily(svn_wc__db_t *db, /* 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_error_t * +svn_wc__db_read_props_internal(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; @@ -9923,8 +10455,10 @@ svn_wc__db_read_props(apr_hash_t **props, 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), + SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot, + local_relpath, + result_pool, + scratch_pool), wcroot); return SVN_NO_ERROR; @@ -10155,7 +10689,7 @@ filter_unwanted_props(apr_hash_t *prop_hash, hi; hi = apr_hash_next(hi)) { - const char *ipropname = svn__apr_hash_index_key(hi); + const char *ipropname = apr_hash_this_key(hi); if (strcmp(ipropname, propname) != 0) svn_hash_sets(prop_hash, ipropname, NULL); @@ -10329,7 +10863,7 @@ db_read_inherited_props(apr_array_header_t **inherited_props, iprop_elt->prop_hash = node_props; /* Build the output array in depth-first order. */ - svn_sort__array_insert(&iprop_elt, iprops, 0); + svn_sort__array_insert(iprops, &iprop_elt, 0); } } } @@ -10365,7 +10899,7 @@ db_read_inherited_props(apr_array_header_t **inherited_props, /* 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); + svn_sort__array_insert(iprops, &cached_iprop, 0); } } @@ -10479,7 +11013,7 @@ get_children_with_cached_iprops(apr_hash_t **iprop_paths, hi; hi = apr_hash_next(hi)) { - const char *child_abspath = svn__apr_hash_index_key(hi); + const char *child_abspath = apr_hash_this_key(hi); const char *child_relpath; svn_node_kind_t child_kind; @@ -10557,8 +11091,34 @@ svn_wc__db_read_children_of_working_node(const apr_array_header_t **children, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_children2(children, wcroot, local_relpath, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_WORKING_CHILDREN, -1, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_wc__db_base_read_not_present_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 svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1, + result_pool, scratch_pool)); } /* Helper for svn_wc__db_node_check_replace(). @@ -10745,75 +11305,27 @@ svn_wc__db_read_children(const apr_array_header_t **children, VERIFY_USABLE_WCROOT(wcroot); return gather_children(children, wcroot, local_relpath, + STMT_SELECT_NODE_CHILDREN, -1, result_pool, scratch_pool); } -/* */ +/* Implementation of svn_wc__db_global_relocate */ 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; + local_dir_relpath = local_relpath; SVN_ERR(read_info(&status, NULL, NULL, NULL, &old_repos_id, @@ -10852,11 +11364,11 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, { const char *work_del_relpath; - SVN_ERR(scan_deletion_txn(NULL, NULL, - &work_del_relpath, NULL, - wcroot, local_dir_relpath, - scratch_pool, - scratch_pool)); + SVN_ERR(scan_deletion(NULL, NULL, + &work_del_relpath, NULL, + wcroot, local_dir_relpath, + scratch_pool, + scratch_pool)); if (work_del_relpath) { /* Deleted within a copy/move */ @@ -10884,15 +11396,62 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, scratch_pool, scratch_pool)); } - SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb, + SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot, old_repos_id, scratch_pool)); - SVN_ERR_ASSERT(repos_uuid); + SVN_ERR_ASSERT(repos_uuid); /* 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; + + 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)); + VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid, - have_base_node, old_repos_id, scratch_pool), + relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool), wcroot); + SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity, + scratch_pool)); + return SVN_NO_ERROR; } @@ -10979,6 +11538,48 @@ determine_commit_repos_info(apr_int64_t *repos_id, return svn_error_trace(svn_sqlite__reset(stmt)); } +static svn_error_t * +moved_descendant_collect(apr_hash_t **map, + 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; + + *map = NULL; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + 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)); + + /* Find all moved descendants. Key them on target, because that is + always unique */ + while (have_row) + { + const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool); + const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool); + + if (!*map) + *map = apr_hash_make(result_pool); + + svn_hash_sets(*map, to_relpath, src_relpath); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} + /* Helper for svn_wc__db_global_commit() Makes local_relpath and all its descendants at the same op-depth represent @@ -10990,49 +11591,25 @@ determine_commit_repos_info(apr_int64_t *repos_id, 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_hash_t *children, 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)); + if (!children) + return SVN_NO_ERROR; /* Then update them */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -11041,11 +11618,12 @@ moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, 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 *src_relpath = apr_hash_this_val(hi); + const char *to_relpath = apr_hash_this_key(hi); const char *new_repos_relpath; int to_op_depth = relpath_depth(to_relpath); int affected; + apr_hash_t *map; svn_pool_clear(iterpool); @@ -11072,9 +11650,11 @@ moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, 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, + SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth, + iterpool, iterpool)); + SVN_ERR(moved_descendant_commit(wcroot, to_relpath, repos_id, new_repos_relpath, revision, - iterpool)); + map, iterpool)); } svn_pool_destroy(iterpool); @@ -11133,7 +11713,6 @@ commit_node(svn_wc__db_wcroot_t *wcroot, 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, @@ -11155,6 +11734,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, const char *repos_relpath; int op_depth; svn_wc__db_status_t old_presence; + svn_boolean_t moved_here; /* If we are adding a file or directory, then we need to get repository information from the parent node since "this node" does @@ -11184,6 +11764,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, /* 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. */ + old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map); new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map); /* What will the new depth be? */ @@ -11202,26 +11783,35 @@ commit_node(svn_wc__db_wcroot_t *wcroot, 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. + if (old_presence != svn_wc__db_status_base_deleted) + { + /* 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); + 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); - if (keep_changelist && have_act) - changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool); + inherited_prop_blob.data = svn_sqlite__column_blob( + stmt_info, 16, + &inherited_prop_blob.len, + scratch_pool); - old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map); + if (keep_changelist && have_act) + changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool); + + moved_here = svn_sqlite__column_int(stmt_info, 15); + } + else + { + moved_here = FALSE; + changelist = NULL; + } /* ### other stuff? */ @@ -11232,6 +11822,24 @@ commit_node(svn_wc__db_wcroot_t *wcroot, { int affected_rows; + SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath)); + + /* First clear the moves that we are going to delete in a bit */ + { + apr_hash_t *old_moves; + apr_hash_index_t *hi; + SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0, + scratch_pool, scratch_pool)); + + if (old_moves) + for (hi = apr_hash_first(scratch_pool, old_moves); + hi; hi = apr_hash_next(hi)) + { + SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi), + scratch_pool)); + } + } + /* This removes all layers of this node and at the same time determines if we need to remove shadowed layers below our descendants. */ @@ -11264,26 +11872,40 @@ commit_node(svn_wc__db_wcroot_t *wcroot, 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)); + if (old_presence != svn_wc__db_status_base_deleted) + { + /* Collapse descendants of the current op_depth to layer 0, + this includes moved-from/to clearing */ + SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth, + repos_id, repos_relpath, new_revision, + scratch_pool)); + } + + if (old_presence != svn_wc__db_status_base_deleted) + { + apr_hash_t *moves = NULL; + + SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0, + scratch_pool, 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, + /* And make the recorded local moves represent moves of the node we + just committed. */ + SVN_ERR(moved_descendant_commit(wcroot, local_relpath, repos_id, repos_relpath, new_revision, - scratch_pool)); + moves, 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)); + if (moved_here) + { + /* 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)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } } - /* Update or add the BASE_NODE row with all the new information. */ if (*local_relpath == '\0') @@ -11292,46 +11914,64 @@ commit_node(svn_wc__db_wcroot_t *wcroot, 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) + if (old_presence != svn_wc__db_status_base_deleted) { - SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data, - inherited_prop_blob.len)); - } + new_presence = (old_presence == svn_wc__db_status_incomplete + ? svn_wc__db_status_incomplete + : svn_wc__db_status_normal); - SVN_ERR(svn_sqlite__step_done(stmt)); + 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)); + } - if (have_act) + SVN_ERR(svn_sqlite__step_done(stmt)); + } + else { - 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( + struct insert_base_baton_t ibb; + blank_ibb(&ibb); + + ibb.repos_id = repos_id; + ibb.status = svn_wc__db_status_not_present; + ibb.kind = new_kind; + ibb.repos_relpath = repos_relpath; + ibb.revision = new_revision; + + SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + + keep_changelist = FALSE; /* Nothing there */ + } + + 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", @@ -11351,23 +11991,21 @@ commit_node(svn_wc__db_wcroot_t *wcroot, } } - 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_boolean_t op_root = (op_depth > 0 + && (relpath_depth(local_relpath) == op_depth)); + /* If we are committing an add of a delete, we can assume we own + all locks at or below REPOS_RELPATH (or the server would have + denied the commit). As we must have passed these to the server + we can now safely remove them. + */ SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb, - STMT_DELETE_LOCK_RECURSIVELY)); + op_root + ? STMT_DELETE_LOCK_RECURSIVELY + : STMT_DELETE_LOCK)); SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath)); SVN_ERR(svn_sqlite__step_done(lock_stmt)); } @@ -11387,7 +12025,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, 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, @@ -11399,7 +12036,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, 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)); @@ -11408,7 +12044,7 @@ svn_wc__db_global_commit(svn_wc__db_t *db, 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, + new_checksum, new_dav_cache, keep_changelist, no_unlock, work_items, scratch_pool), wcroot); @@ -11543,6 +12179,9 @@ db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot, * 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. * + * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the + * values as stored currently in WCROOT for LOCAL_RELPATH. + * * 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 @@ -11555,6 +12194,10 @@ db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot, static svn_error_t * bump_node_revision(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_wc__db_status_t node_status, + svn_node_kind_t node_kind, + svn_revnum_t node_revision, + const char *node_repos_relpath, apr_int64_t new_repos_id, const char *new_repos_relpath, svn_revnum_t new_rev, @@ -11567,56 +12210,14 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, 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; + apr_hash_t *children; + apr_hash_index_t *hi; 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)) + if (new_repos_relpath != NULL + && strcmp(node_repos_relpath, new_repos_relpath)) set_repos_relpath = TRUE; if (wcroot_iprops) @@ -11626,7 +12227,7 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, if (iprops || set_repos_relpath - || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision)) + || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision)) { SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath, iprops, new_rev, @@ -11638,10 +12239,10 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, /* 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) + || node_kind != svn_node_dir + || node_status == svn_wc__db_status_server_excluded + || node_status == svn_wc__db_status_excluded + || node_status == svn_wc__db_status_not_present) return SVN_NO_ERROR; /* And now recurse over the children */ @@ -11653,25 +12254,62 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, 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++) + SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0, + scratch_pool, iterpool)); + for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi)) { - const char *child_basename = APR_ARRAY_IDX(children, i, const char *); + const char *child_basename = apr_hash_this_key(hi); + const struct svn_wc__db_base_info_t *child_info; const char *child_local_relpath; const char *child_repos_relpath = NULL; svn_pool_clear(iterpool); + child_info = apr_hash_this_val(hi); + + if (child_info->update_root && child_info->kind == svn_node_file) + continue; /* Skip file externals */ + + if (depth < svn_depth_immediates && child_info->kind == svn_node_dir) + continue; /* Skip directories */ + + child_local_relpath = svn_relpath_join(local_relpath, child_basename, + iterpool); + + /* Don't touch nodes that can't be touched via the exclude list */ + if (svn_hash_gets(exclude_relpaths, child_local_relpath)) + continue; + + /* 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 (child_info->status == svn_wc__db_status_not_present + || (child_info->status == svn_wc__db_status_server_excluded && + child_info->revnum != new_rev)) + { + svn_sqlite__stmt_t *stmt; + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_BASE_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + continue; + } + /* 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, + SVN_ERR(bump_node_revision(wcroot, child_local_relpath, + child_info->status, + child_info->kind, + child_info->revnum, + child_info->repos_relpath, + new_repos_id, child_repos_relpath, new_rev, depth_below_here, exclude_relpaths, wcroot_iprops, @@ -11699,6 +12337,7 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -11707,8 +12346,11 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, svn_node_kind_t kind; svn_error_t *err; apr_int64_t new_repos_id = INVALID_REPOS_ID; + svn_revnum_t revision; + const char *repos_relpath; - err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL, + err = svn_wc__db_base_get_info_internal(&status, &kind, &revision, + &repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, local_relpath, @@ -11738,13 +12380,16 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, new_repos_uuid, wcroot->sdb, scratch_pool)); - SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id, + SVN_ERR(bump_node_revision(wcroot, local_relpath, + status, kind, revision, repos_relpath, + new_repos_id, new_repos_relpath, new_revision, depth, exclude_relpaths, wcroot_iprops, TRUE /* is_root */, FALSE, db, scratch_pool)); + /* ### TODO: Use empty_update flag for change knowledge */ SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db, scratch_pool)); @@ -11765,6 +12410,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -11787,7 +12433,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, 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, + exclude_relpaths, wcroot_iprops, empty_update, notify_func, notify_baton, scratch_pool), wcroot); @@ -11864,6 +12510,7 @@ svn_wc__db_lock_add(svn_wc__db_t *db, static svn_error_t * lock_remove_txn(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_skel_t *work_items, apr_pool_t *scratch_pool) { const char *repos_relpath; @@ -11883,6 +12530,8 @@ lock_remove_txn(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + return SVN_NO_ERROR; } @@ -11890,6 +12539,7 @@ lock_remove_txn(svn_wc__db_wcroot_t *wcroot, svn_error_t * svn_wc__db_lock_remove(svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *work_items, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; @@ -11902,7 +12552,7 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - lock_remove_txn(wcroot, local_relpath, scratch_pool), + lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool), wcroot); /* There may be some entries, and the lock info is now out of date. */ @@ -11911,39 +12561,6 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, 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. @@ -12042,23 +12659,26 @@ get_moved_from_info(const char **moved_from_relpath, return SVN_NO_ERROR; } -/* The body of scan_addition(). - */ +/* 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_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) +scan_addition(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 = ""; @@ -12087,8 +12707,7 @@ scan_addition_txn(svn_wc__db_status_t *status, svn_boolean_t have_row; svn_wc__db_status_t presence; int op_depth; - const char *repos_prefix_path = ""; - int i; + const char *repos_prefix_path; /* ### is it faster to fetch fewer columns? */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -12137,17 +12756,10 @@ scan_addition_txn(svn_wc__db_status_t *status, /* 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); - } + op_root_relpath = svn_relpath_prefix(local_relpath, op_depth, + scratch_pool); + repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath, + local_relpath); if (op_root_relpath_p) *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath); @@ -12239,39 +12851,37 @@ scan_addition_txn(svn_wc__db_status_t *status, { const char *base_relpath; - while (TRUE) - { + while (TRUE) + { + const char *tmp; - SVN_ERR(svn_sqlite__reset(stmt)); + 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); - /* 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)); - 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; - if (! have_row) - break; + op_depth = svn_sqlite__column_int(stmt, 0); - op_depth = svn_sqlite__column_int(stmt, 0); + /* Skip to op_depth */ + tmp = op_root_relpath; - /* 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); + op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth, + scratch_pool); + repos_prefix_path = svn_relpath_join( + svn_relpath_skip_ancestor(op_root_relpath, tmp), + repos_prefix_path, scratch_pool); } - } SVN_ERR(svn_sqlite__reset(stmt)); @@ -12331,39 +12941,27 @@ scan_addition_txn(svn_wc__db_status_t *status, 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, +svn_error_t * +svn_wc__db_scan_addition_internal( + 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) { - 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; + return svn_error_trace( + scan_addition(status, op_root_relpath_p, repos_relpath, repos_id, + original_repos_relpath, original_repos_id, + original_revision, NULL, NULL, NULL, + wcroot, local_relpath, result_pool, scratch_pool)); } - svn_error_t * svn_wc__db_scan_addition(svn_wc__db_status_t *status, const char **op_root_abspath, @@ -12395,7 +12993,8 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(scan_addition(status, + SVN_WC__DB_WITH_TXN4( + scan_addition(status, op_root_abspath ? &op_root_relpath : NULL, @@ -12403,7 +13002,14 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, original_repos_relpath, original_repos_id_p, original_revision, NULL, NULL, NULL, - wcroot, local_relpath, result_pool, scratch_pool)); + wcroot, local_relpath, result_pool, scratch_pool), + svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot, + repos_id, result_pool), + svn_wc__db_fetch_repos_info(original_root_url, original_uuid, + wcroot, original_repos_id, + result_pool), + SVN_NO_ERROR, + wcroot); if (op_root_abspath) *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath, @@ -12411,12 +13017,6 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, /* 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; } @@ -12444,7 +13044,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(scan_addition(&status, + SVN_WC__DB_WITH_TXN( + scan_addition(&status, op_root_abspath ? &op_root_relpath : NULL, @@ -12460,7 +13061,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, moved_from_delete_abspath ? &moved_from_op_depth : NULL, - wcroot, local_relpath, scratch_pool, scratch_pool)); + wcroot, local_relpath, scratch_pool, scratch_pool), + wcroot); if (status != svn_wc__db_status_moved_here || !moved_from_relpath) return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, @@ -12484,12 +13086,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, /* 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); + const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath, + moved_from_op_depth, scratch_pool); *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp, scratch_pool); @@ -12498,26 +13096,25 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, return SVN_NO_ERROR; } -/* ### +/* ### Recursive helper for svn_wc__db_follow_moved_to() */ 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, +follow_moved_to(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + int op_depth, + apr_array_header_t **moved_tos, 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 shadowing_op_depth; + const char *ancestor_relpath; + const char *node_moved_to = NULL; int i; - SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path)); - + /* Obtain the depth of the node directly shadowing local_relpath + as it exists at OP_DEPTH, and perhaps moved to info */ 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, @@ -12525,57 +13122,35 @@ follow_moved_to(apr_array_header_t **moved_tos, SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (have_row) { - working_op_depth = svn_sqlite__column_int(stmt, 0); + shadowing_op_depth = svn_sqlite__column_int(stmt, 0); node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool); - if (!repos_path) + + if (node_moved_to) { - 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); + struct svn_wc__db_moved_to_t *moved_to; + + moved_to = apr_palloc(result_pool, sizeof(*moved_to)); + moved_to->op_depth = shadowing_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(svn_sqlite__reset(stmt)); - if (node_moved_to) + if (!have_row) { - 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)); + /* Node is not shadowed, so not moved */ + return SVN_NO_ERROR; } - - if (node_moved_to) + else 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; + /* Moved directly, so we have the final location */ + return SVN_NO_ERROR; } - - /* 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) + for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i) { const char *ancestor_moved_to; @@ -12584,56 +13159,30 @@ follow_moved_to(apr_array_header_t **moved_tos, 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); + shadowing_op_depth)); + SVN_ERR(svn_sqlite__step_row(stmt)); + 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; + node_moved_to + = svn_relpath_join(ancestor_moved_to, + svn_relpath_skip_ancestor(ancestor_relpath, + local_relpath), + result_pool); + moved_to = apr_palloc(result_pool, sizeof(*moved_to)); - moved_to->op_depth = working_op_depth; + moved_to->op_depth = shadowing_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)); + SVN_ERR(follow_moved_to(wcroot, node_moved_to, + relpath_depth(ancestor_moved_to), + moved_tos, result_pool, scratch_pool)); + break; } } @@ -12661,96 +13210,91 @@ svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos, 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)); + SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos, + result_pool, scratch_pool), + wcroot); /* ### 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_wc__db_scan_moved_to_internal(const char **move_src_relpath, + const char **move_dst_relpath, + const char **delete_relpath, + 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; int delete_op_depth; const char *relpath = local_relpath; + const char *dst_relpath; + + SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */ + + if (move_src_relpath) + *move_src_relpath = NULL; + if (move_dst_relpath) + *move_dst_relpath = NULL; + if (delete_relpath) + *delete_relpath = NULL; + + 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, relpath, op_depth)); - *move_dst_relpath = *move_dst_op_root_relpath = NULL; - *move_src_root_relpath = *move_src_op_root_relpath = NULL; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - do + if (!have_row) { + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, + svn_sqlite__reset(stmt), + _("Node '%s' is not shadowed"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + + delete_op_depth = svn_sqlite__column_int(stmt, 0); + dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + + SVN_ERR(svn_sqlite__reset(stmt)); + + while (!dst_relpath && have_row) + { + relpath = svn_relpath_dirname(relpath, scratch_pool); + + if (relpath_depth(relpath) < delete_op_depth) + break; + 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)); + STMT_SELECT_DEPTH_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, + delete_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); - } + dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool); + 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) + if (dst_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); + if (move_src_relpath) + *move_src_relpath = apr_pstrdup(result_pool, relpath); + + if (move_dst_relpath) + *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath); + + if (delete_relpath) + *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth, + result_pool); } return SVN_NO_ERROR; @@ -12763,7 +13307,7 @@ 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, + const char **delete_abspath, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -12771,47 +13315,54 @@ svn_wc__db_base_moved_to(const char **move_dst_abspath, { 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; + const char *dst_root_relpath; + const char *src_root_relpath, *delete_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), + SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath, + &dst_root_relpath, + &delete_relpath, + wcroot, local_relpath, + 0 /* BASE */, + 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; + *move_dst_abspath = + dst_root_relpath + ? svn_dirent_join(wcroot->abspath, + svn_dirent_join( + dst_root_relpath, + svn_relpath_skip_ancestor(src_root_relpath, + local_relpath), + scratch_pool), + 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; + *move_dst_op_root_abspath = + dst_root_relpath + ? svn_dirent_join(wcroot->abspath, dst_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; + *move_src_root_abspath = + src_root_relpath + ? svn_dirent_join(wcroot->abspath, 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; + if (delete_abspath) + *delete_abspath = + delete_relpath + ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool) + : NULL; return SVN_NO_ERROR; } @@ -12834,6 +13385,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, SDB_FILE, NULL, SVN_INVALID_REVNUM, svn_depth_unknown, TRUE /* exclusive */, + 0 /* timeout */, wc_db->state_pool, scratch_pool)); SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot, @@ -12841,7 +13393,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, 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. */ @@ -12850,191 +13401,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, 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, @@ -13110,28 +13476,15 @@ svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, } 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_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot, + const svn_skel_t *work_item, + 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)); + /* Add the work item(s) to the WORK_QUEUE. */ + return svn_error_trace(add_work_items(wcroot->sdb, work_item, + scratch_pool)); } - svn_error_t * svn_wc__db_wq_add(svn_wc__db_t *db, const char *wri_abspath, @@ -13244,8 +13597,8 @@ wq_record(svn_wc__db_wcroot_t *wcroot, 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_abspath = apr_hash_this_key(hi); + const svn_io_dirent2_t *dirent = apr_hash_this_val(hi); const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath); @@ -13458,7 +13811,7 @@ svn_wc__db_temp_get_all_access(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); + const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); /* This is highly redundant, 'cause the same WCROOT will appear many times in dir_data. */ @@ -13645,6 +13998,8 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files, svn_error_t * svn_wc__db_read_conflict(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -13658,14 +14013,16 @@ svn_wc__db_read_conflict(svn_skel_t **conflict, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot, - local_relpath, + return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props, + wcroot, local_relpath, result_pool, scratch_pool)); } svn_error_t * svn_wc__db_read_conflict_internal(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_pool_t *result_pool, @@ -13674,6 +14031,11 @@ svn_wc__db_read_conflict_internal(svn_skel_t **conflict, svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + if (kind) + *kind = svn_node_none; + if (props) + *props = NULL; + /* Check if we have a conflict in ACTUAL */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_ACTUAL_NODE)); @@ -13681,58 +14043,91 @@ svn_wc__db_read_conflict_internal(svn_skel_t **conflict, SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (! have_row) + 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; + apr_size_t cfl_len; + const void *cfl_data; - err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb, - STMT_SELECT_NODE_INFO); + /* 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 (err) - stmt_node = NULL; + if (cfl_data) + *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool); else - err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id, - local_relpath); + *conflict = NULL; - if (!err) - err = svn_sqlite__step(&have_row, stmt_node); + if (props) + { + svn_error_t *err; - if (stmt_node) - err = svn_error_compose_create(err, - svn_sqlite__reset(stmt_node)); + err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1, + result_pool, + scratch_pool)); - SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + } + else + *conflict = NULL; - if (have_row) + SVN_ERR(svn_sqlite__reset(stmt)); + + if (!have_row || kind || (props && !*props)) + { + svn_error_t *err = NULL; + svn_boolean_t have_info = 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_info, stmt)); + + if (have_info) { - *conflict = NULL; - return SVN_NO_ERROR; + if (kind) + { + svn_wc__db_status_t status; + int op_depth = svn_sqlite__column_int(stmt, 0); + + status = svn_sqlite__column_token(stmt, 3, presence_map); + + if (op_depth > 0) + err = convert_to_working_status(&status, status); + + if (!err && (status == svn_wc__db_status_normal + || status == svn_wc__db_status_added + || status == svn_wc__db_status_deleted + || status == svn_wc__db_status_incomplete)) + { + *kind = svn_sqlite__column_token(stmt, 4, kind_map); + } + } + + /* Need props, and no props in ACTUAL? */ + if (!err && (props && !*props)) + { + err = svn_sqlite__column_properties(props, stmt, 14, + result_pool, scratch_pool); + } } - return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - _("The node '%s' was not found."), + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + if (!have_row && !have_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)); + } } - { - 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)); - } + return SVN_NO_ERROR; } @@ -13749,6 +14144,7 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, const char *local_relpath; svn_sqlite__stmt_t *stmt_info; svn_boolean_t have_info; + svn_wc__db_status_t status; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -13780,12 +14176,27 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, } } + status = svn_sqlite__column_token(stmt_info, 3, presence_map); + + if (show_deleted && status == svn_wc__db_status_base_deleted) + { + /* Let's return the kind of what is really deleted insead of what + we have cached in the base-deleted record */ + + SVN_ERR(svn_sqlite__step(&have_info, stmt_info)); + + if (!have_info) + { + /* No lower layer deleted? Database inconsistency! */ + *kind = svn_node_none; + return svn_error_trace(svn_sqlite__reset(stmt_info)); + } + } + 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)); @@ -13821,38 +14232,6 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, 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, @@ -14017,7 +14396,7 @@ svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath, wcroot->abspath, svn_wc_get_adm_dir(scratch_pool), WCROOT_TEMPDIR_RELPATH, - NULL); + SVN_VA_NULL); return SVN_NO_ERROR; } @@ -14046,6 +14425,7 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int levels_to_lock, svn_boolean_t steal_lock, + svn_boolean_t enforce_empty_wq, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; @@ -14075,6 +14455,9 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, scratch_pool)); } + if (enforce_empty_wq) + SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb)); + /* 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)); @@ -14101,8 +14484,9 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, /* 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); + err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot, + lock_relpath, + TRUE, scratch_pool); if (err) SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); @@ -14190,7 +14574,7 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, err = svn_sqlite__insert(NULL, stmt); if (err) return svn_error_createf(SVN_ERR_WC_LOCKED, err, - _("Working copy '%s' locked"), + _("Failed to lock working copy '%s'."), path_for_error_message(wcroot, local_relpath, scratch_pool)); @@ -14249,7 +14633,7 @@ svn_wc__db_wclock_obtain(svn_wc__db_t *db, SVN_WC__DB_WITH_TXN( wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock, - scratch_pool), + db->enforce_empty_wq, scratch_pool), wcroot); return SVN_NO_ERROR; } @@ -14440,12 +14824,12 @@ svn_wc__db_wclock_release(svn_wc__db_t *db, /* 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) +svn_error_t * +svn_wc__db_wclock_owns_lock_internal(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; @@ -14512,8 +14896,8 @@ svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact, - scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath, + exact, scratch_pool)); return SVN_NO_ERROR; } @@ -14629,29 +15013,110 @@ svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db, return SVN_NO_ERROR; } +/* Helper for svn_wc__db_op_make_copy_internal */ +static svn_error_t * +db_move_moved_to(svn_wc__db_wcroot_t *wcroot, + const char *src1_relpath, + int src1_op_depth, + const char *src2_relpath, + int src2_op_depth, + const char *dst_relpath, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + int affected_rows; -/* 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: + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + src1_relpath, src1_op_depth)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); - 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 - + if (affected_rows == 1) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id, + src2_relpath, src2_op_depth, + dst_relpath)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + } + if (affected_rows != 1) + return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL); - 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. + return SVN_NO_ERROR; +} - 0 1 2 3 +static svn_error_t * +db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int new_shadow_layer, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + new_shadow_layer)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row) + { + int del_op_depth; + const char *src_relpath; + const char *dst_relpath; + svn_error_t *err; + + svn_pool_clear(iterpool); + + del_op_depth = svn_sqlite__column_int(stmt, 0); + src_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool); + + err = svn_error_trace( + db_move_moved_to( + wcroot, + src_relpath, del_op_depth, + src_relpath, new_shadow_layer, + dst_relpath, 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)); + + 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 @@ -14666,104 +15131,224 @@ svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db, 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_int64_t last_repos_id, + const char *last_repos_relpath, + svn_revnum_t last_revision, + int last_op_depth, + svn_boolean_t shadowed, + int root_shadow_depth, 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)); + svn_boolean_t have_row = FALSE; + svn_revnum_t revision; + apr_int64_t repos_id; + const char *repos_relpath; + svn_node_kind_t kind; + int op_depth = relpath_depth(local_relpath); - if (have_row) + if (last_op_depth != op_depth) { - 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__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)); SVN_ERR(svn_sqlite__reset(stmt)); + if (have_row) + shadowed = TRUE; + } - 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); + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision, + &repos_relpath, &repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool)); - /* 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 (last_repos_relpath + && repos_id == last_repos_id + && revision == last_revision) + { + const char *name = svn_relpath_skip_ancestor(last_repos_relpath, + repos_relpath); - if (working_status == svn_wc__db_status_base_deleted) - remove_working = TRUE; - } + if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0) + op_depth = last_op_depth; } - else - SVN_ERR(svn_sqlite__reset(stmt)); - if (remove_working) + /* Can we add a new copy node at the wanted op-depth? */ + if (!have_row || op_depth == last_op_depth) { - 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)); - } + int i; - if (add_working_base_deleted) - { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_DELETE_FROM_BASE)); + 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)); + + if (shadowed) + SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind, + op_depth, scratch_pool)); + + if (kind == svn_node_dir) + { + const apr_array_header_t *children; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(gather_children(&children, wcroot, local_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, 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, + repos_id, repos_relpath, revision, + op_depth, shadowed, root_shadow_depth, + scratch_pool)); + } + svn_pool_destroy(iterpool); + } } else { + /* Auch... we can't make a copy of whatever comes deeper, as this + op-depth is already filled by something else. Let's hope + the user doesn't mind. + + Luckily we know that the moves are already moved to the shadowing + layer, so we can just remove dangling base-deletes if there are + any. + */ + /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */ + SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath, + root_shadow_depth, + scratch_pool)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_WORKING_NODE_FROM_BASE_COPY)); + STMT_DELETE_WORKING_BASE_DELETE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + last_op_depth)); + SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + last_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)); + /* Insert a not-present node to mark that we don't know what exists here. - for (i = 0; i < children->nelts; i++) + We do this last (after recursing), to allow the move fix-up code to + see the original moves. */ + if (last_op_depth > 0 && last_op_depth != op_depth) { - const char *name = APR_ARRAY_IDX(children, i, const char *); - const char *copy_relpath; + insert_working_baton_t iwb; - svn_pool_clear(iterpool); + blank_iwb(&iwb); + iwb.presence = svn_wc__db_status_not_present; + iwb.op_depth = last_op_depth; - copy_relpath = svn_relpath_join(local_relpath, name, iterpool); + iwb.original_repos_id = repos_id; + iwb.original_repos_relpath = repos_relpath; + iwb.original_revnum = revision; + iwb.kind = kind; - SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL, - iterpool)); + SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool)); + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t move_move_info, + 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; + int op_depth = -1; + + /* 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)); + if (have_row) + op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (have_row) + { + if (op_depth == relpath_depth(local_relpath)) + 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 have a working layer, but not one at the op-depth of local-relpath, + so we can create a copy by just copying the lower layer */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_COPY_OP_DEPTH_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, + op_depth, relpath_depth(local_relpath))); + SVN_ERR(svn_sqlite__step_done(stmt)); } + else + { + int affected_rows; + + op_depth = relpath_depth(local_relpath); + /* 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)); + + /* Insert a shadowing layer */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_INSERT_DELETE_FROM_NODE_RECURSIVE)); - SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath, - iterpool), - svn_depth_empty, iterpool)); + /* As we are keeping whatever is below, move the*/ + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", + wcroot->wc_id, local_relpath, + 0, op_depth)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + SVN_ERR_ASSERT(affected_rows > 0); + + if (!move_move_info) + SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath, + op_depth, scratch_pool)); + + + SVN_ERR(make_copy_txn(wcroot, local_relpath, + INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM, + op_depth, FALSE, op_depth, + scratch_pool)); + } 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)); + conflicts, scratch_pool)); - svn_pool_destroy(iterpool); + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); return SVN_NO_ERROR; } @@ -14778,8 +15363,6 @@ svn_wc__db_op_make_copy(svn_wc__db_t *db, { 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)); @@ -14787,30 +15370,15 @@ svn_wc__db_op_make_copy(svn_wc__db_t *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), + svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE, + conflicts, work_items, + scratch_pool), wcroot); + SVN_ERR(flush_entries(wcroot, local_abspath, + svn_depth_infinity, scratch_pool)); + return SVN_NO_ERROR; } @@ -15017,7 +15585,7 @@ has_switched_subtrees(svn_boolean_t *is_switched, 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, + SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot, repos_id, scratch_pool)); url = svn_path_url_add_component2(repos_root_url, repos_relpath, scratch_pool); @@ -15108,17 +15676,14 @@ svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees, return SVN_NO_ERROR; } -/* Like svn_wc__db_has_local_mods(), +/* Like svn_wc__db_has_db_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) +has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; @@ -15130,9 +15695,6 @@ has_local_mods(svn_boolean_t *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. */ @@ -15142,96 +15704,6 @@ has_local_mods(svn_boolean_t *is_modified, /* 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; @@ -15239,12 +15711,10 @@ has_local_mods(svn_boolean_t *is_modified, 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_has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; @@ -15256,9 +15726,8 @@ svn_wc__db_has_local_mods(svn_boolean_t *is_modified, 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)); + return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath, + scratch_pool)); } @@ -15275,8 +15744,6 @@ revision_status_txn(svn_revnum_t *min_revision, 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; @@ -15296,16 +15763,10 @@ revision_status_txn(svn_revnum_t *min_revision, 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, @@ -15321,12 +15782,8 @@ revision_status_txn(svn_revnum_t *min_revision, } } - 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)); + /* Check for db mods. */ + SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool)); return SVN_NO_ERROR; } @@ -15342,8 +15799,6 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, 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; @@ -15360,7 +15815,7 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, 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, + trail_url, committed, scratch_pool), wcroot); return SVN_NO_ERROR; @@ -15406,7 +15861,7 @@ svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens, if (child_repos_id != last_repos_id) { svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url, - NULL, wcroot->sdb, + NULL, wcroot, child_repos_id, scratch_pool); @@ -15529,6 +15984,69 @@ svn_wc__db_verify(svn_wc__db_t *db, return SVN_NO_ERROR; } + +svn_error_t * +svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + svn_error_t *err = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row) + { + const char *local_relpath; + int op_depth = svn_sqlite__column_int(stmt, 1); + int id = svn_sqlite__column_int(stmt, 2); + const char *msg; + + svn_pool_clear(iterpool); + + local_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + msg = svn_sqlite__column_text(stmt, 3, scratch_pool); + + err = callback(baton, wcroot->abspath, local_relpath, op_depth, + id, msg, iterpool); + + if (err) + break; + + 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))); +} + +svn_error_t * +svn_wc__db_verify_db_full(svn_wc__db_t *db, + const char *wri_abspath, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + 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); + + return svn_error_trace( + svn_wc__db_verify_db_full_internal(wcroot, callback, baton, + scratch_pool)); +} + svn_error_t * svn_wc__db_bump_format(int *result_format, svn_boolean_t *bumped_format, @@ -15549,6 +16067,7 @@ svn_wc__db_bump_format(int *result_format, err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE, svn_sqlite__mode_readwrite, TRUE, /* exclusive */ + 0, /* default timeout */ NULL, /* my statements */ scratch_pool, scratch_pool); if (err) @@ -15608,3 +16127,504 @@ svn_wc__db_vacuum(svn_wc__db_t *db, return SVN_NO_ERROR; } + +/* Item queued with svn_wc__db_commit_queue_add */ +typedef struct commit_queue_item_t +{ + const char *local_relpath; + svn_boolean_t recurse; /* Use legacy recursion */ + svn_boolean_t committed; /* Process the node as committed */ + svn_boolean_t remove_lock; /* Remove existing lock on node */ + svn_boolean_t remove_changelist; /* Remove changelist on node */ + + /* The pristine text checksum. NULL if the old value should be kept + and for directories */ + const svn_checksum_t *new_sha1_checksum; + + apr_hash_t *new_dav_cache; /* New DAV cache for the node */ +} commit_queue_item_t; + +/* The queue definition for vn_wc__db_create_commit_queue, + svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */ +struct svn_wc__db_commit_queue_t +{ + svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */ + apr_array_header_t *items; /* List of commit_queue_item_t* */ + svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */ +}; + +/* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the + working copy specified with WRI_ABSPATH */ +svn_error_t * +svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue, + 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_wc__db_commit_queue_t *q; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + wri_abspath, result_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + q = apr_pcalloc(result_pool, sizeof(*q)); + + SVN_ERR_ASSERT(wcroot->sdb); + + q->wcroot = wcroot; + q->items = apr_array_make(result_pool, 64, + sizeof(commit_queue_item_t*)); + q->have_recurse = FALSE; + + *queue = q; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue, + const char *local_abspath, + svn_boolean_t recurse, + svn_boolean_t is_commited, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *new_dav_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + commit_queue_item_t *cqi; + const char *local_relpath; + + local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath, + local_abspath); + + if (! local_relpath) + return svn_error_createf( + SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The path '%s' is not in the working copy '%s'"), + svn_dirent_local_style(local_abspath, scratch_pool), + svn_dirent_local_style(queue->wcroot->abspath, scratch_pool)); + + cqi = apr_pcalloc(result_pool, sizeof(*cqi)); + cqi->local_relpath = local_relpath; + cqi->recurse = recurse; + cqi->committed = is_commited; + cqi->remove_lock = remove_lock; + cqi->remove_changelist = remove_changelist; + cqi->new_sha1_checksum = new_sha1_checksum; + cqi->new_dav_cache = new_dav_cache; + + queue->have_recurse |= recurse; + + APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi; + return SVN_NO_ERROR; +} + +/*** Finishing updates and commits. ***/ + +/* Post process an item that is committed in the repository. Collapse layers into + * BASE. Queue work items that will finish a commit of the file or directory + * LOCAL_ABSPATH in DB: + */ +static svn_error_t * +process_committed_leaf(svn_wc__db_t *db, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + 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 remove_lock, + svn_boolean_t remove_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; + + { + const char *lock_relpath; + svn_boolean_t locked; + + if (kind == svn_node_dir) + lock_relpath = local_relpath; + else + lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool); + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, + lock_relpath, FALSE, + scratch_pool)); + + if (!locked) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty, + scratch_pool)); + } + + 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 + || status == svn_wc__db_status_deleted); + + if (kind != svn_node_dir + && status != svn_wc__db_status_deleted) + { + /* 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_internal( + 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, + wcroot, local_relpath, + scratch_pool, scratch_pool)); + } + } + + SVN_ERR(svn_wc__wq_build_file_commit(&work_item, + db, svn_dirent_join(wcroot->abspath, + local_relpath, + scratch_pool), + prop_mods, + scratch_pool, scratch_pool)); + } + + /* The new text base will be found in the pristine store by its checksum. */ + SVN_ERR(commit_node(wcroot, local_relpath, + new_revnum, new_changed_rev, + new_changed_date, new_changed_author, + checksum, + new_dav_cache, + !remove_changelist, + !remove_lock, + work_item, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/** Internal helper for svn_wc_process_committed_queue2(). + * Bump a commit item, collapsing local changes with the new repository + * information to a new BASE node. + * + * @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 all the new dav properties for LOCAL_RELPATH. + * + * If @a remove_lock is set, release any user locks on @a + * local_abspath; otherwise keep them during processing. + * + * If @a remove_changelist is set, clear any changeset assignments + * from @a local_abspath; otherwise, keep such assignments. + * + * If @a new_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. (Part of the legacy recurse handling) + */ +static svn_error_t * +process_committed_internal(svn_wc__db_t *db, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + 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 remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *items_by_relpath, + 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_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &old_checksum, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, &prop_mods, NULL, NULL, NULL, + wcroot, local_relpath, + 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, wcroot, local_relpath, !top_of_recurse, + status, kind, prop_mods, old_checksum, + new_revnum, new_date, rev_author, + new_dav_cache, + remove_lock, remove_changelist, + new_sha1_checksum, + scratch_pool)); + + /* Only check for recursion on nodes that have children */ + if (kind != svn_node_dir + || 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(gather_children(&children, wcroot, local_relpath, + STMT_SELECT_NODE_CHILDREN, -1, + 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_relpath; + const commit_queue_item_t *cqi; + + svn_pool_clear(iterpool); + + this_relpath = svn_dirent_join(local_relpath, name, iterpool); + + new_sha1_checksum = NULL; + cqi = svn_hash_gets(items_by_relpath, this_relpath); + + if (cqi != NULL) + new_sha1_checksum = cqi->new_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(process_committed_internal( + db, wcroot, this_relpath, + TRUE /* recurse */, + FALSE /* top_of_recurse */, + new_revnum, new_date, + rev_author, + NULL /* new_dav_cache */, + FALSE /* remove_lock */, + remove_changelist, + new_sha1_checksum, + items_by_relpath, + iterpool)); + } + + svn_pool_destroy(iterpool); + } + + 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(const apr_array_header_t *all_items, + const commit_queue_item_t *item, + apr_pool_t *scratch_pool) +{ + const char *local_relpath = item->local_relpath; + int i; + + for (i = 0; i < all_items->nelts; i++) + { + const commit_queue_item_t *qi + = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *); + + if (qi == item) + continue; + + if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath, + local_relpath)) + { + return TRUE; + } + } + + return FALSE; +} + +/* Compare function for svn_sort__array */ +static int +compare_queue_items(const void *v1, + const void *v2) +{ + const commit_queue_item_t *cqi1 + = *(const commit_queue_item_t **)v1; + const commit_queue_item_t *cqi2 + = *(const commit_queue_item_t **)v2; + + return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath); +} + +/* Internal, locked version of svn_wc__db_process_commit_queue */ +static svn_error_t * +db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + apr_pool_t *scratch_pool) +{ + apr_hash_t *items_by_relpath = NULL; + int j; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + svn_sort__array(queue->items, compare_queue_items); + + if (queue->have_recurse) + { + items_by_relpath = apr_hash_make(scratch_pool); + + for (j = 0; j < queue->items->nelts; j++) + { + commit_queue_item_t *cqi + = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *); + + svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi); + } + } + + for (j = 0; j < queue->items->nelts; j++) + { + commit_queue_item_t *cqi + = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *); + + 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_recurse && have_recursive_parent(queue->items, cqi, + iterpool)) + continue; + + if (!cqi->committed) + { + if (cqi->remove_lock) + { + svn_skel_t *work_item; + + SVN_ERR(svn_wc__wq_build_sync_file_flags( + &work_item, + db, + svn_dirent_join( + queue->wcroot->abspath, + cqi->local_relpath, + iterpool), + iterpool, iterpool)); + + lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item, + iterpool); + } + if (cqi->remove_changelist) + SVN_ERR(svn_wc__db_op_set_changelist(db, + svn_dirent_join( + queue->wcroot->abspath, + cqi->local_relpath, + iterpool), + NULL, NULL, + svn_depth_empty, + NULL, NULL, /* notify */ + NULL, NULL, /* cancel */ + iterpool)); + } + else + { + SVN_ERR(process_committed_internal( + db, queue->wcroot, cqi->local_relpath, + cqi->recurse, + TRUE /* top_of_recurse */, + new_revnum, new_date, new_author, + cqi->new_dav_cache, + cqi->remove_lock, + cqi->remove_changelist, + cqi->new_sha1_checksum, + items_by_relpath, + iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + apr_pool_t *scratch_pool) +{ + SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue, + new_revnum, new_date, + new_author, scratch_pool), + queue->wcroot); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db.h b/contrib/subversion/subversion/libsvn_wc/wc_db.h index 0fcce5e0e..a947be04e 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db.h +++ b/contrib/subversion/subversion/libsvn_wc/wc_db.h @@ -20,7 +20,7 @@ * ==================================================================== * @endcopyright * - * @file svn_wc_db.h + * @file wc_db.h * @brief The Subversion Working Copy Library - Metadata/Base-Text Support * * Requires: @@ -412,9 +412,6 @@ svn_wc__db_get_wcroot(const char **wcroot_abspath, 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 @@ -424,6 +421,9 @@ svn_wc__db_get_wcroot(const char **wcroot_abspath, svn_prop_inherited_item_t * structures that is set as the base node's inherited_properties. + 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. @@ -444,10 +444,10 @@ svn_wc__db_base_add_directory(svn_wc__db_t *db, 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 *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -676,13 +676,15 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, const svn_skel_t *work_items, apr_pool_t *scratch_pool); +/* Remove a node and all its descendants from the BASE tree. This can + be done in two modes: -/* 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. + * Remove everything, scheduling wq operations to clean up + the working copy. (KEEP_WORKING = FALSE) - The node to remove is indicated by LOCAL_ABSPATH from the local - filesystem. + * Bump things to WORKING, so the BASE layer is free, but the working + copy unmodified, except that everything that was visible from + BASE is now a copy of what it used to be. (KEEP_WORKING = TRUE) This operation *installs* workqueue operations to update the local filesystem after the database operation. @@ -693,21 +695,11 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, 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 MARK_NOT_PRESENT or MARK_EXCLUDED is TRUE, install a marker + of the specified type at the root of the now removed tree, with + either the specified revision (or in case of SVN_INVALID_REVNUM) + the original revision. If CONFLICT and/or WORK_ITEMS are passed they are installed as part of the operation, after the work items inserted by the operation @@ -716,10 +708,10 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, 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_boolean_t keep_working, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -953,33 +945,46 @@ svn_wc__db_pristine_read(svn_stream_t **contents, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Baton for svn_wc__db_pristine_install */ +typedef struct svn_wc__db_install_data_t + svn_wc__db_install_data_t; -/* 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. +/* Open a writable stream to a temporary text base, ready for installing + into the pristine store. Set *STREAM to the opened stream. The temporary + file will have an arbitrary unique name. Return as *INSTALL_DATA a baton + for eiter installing or removing the 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. + Arrange that, on stream closure, *MD5_CHECKSUM and *SHA1_CHECKSUM will be + set to the MD-5 and SHA-1 checksums respectively of that file. + MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted. - Allocate *TEMP_DIR_ABSPATH in RESULT_POOL. */ + Allocate the new stream, path and checksums 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); - +svn_wc__db_pristine_prepare_install(svn_stream_t **stream, + svn_wc__db_install_data_t **install_data, + svn_checksum_t **sha1_checksum, + svn_checksum_t **md5_checksum, + 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. */ +/* Install the file created via svn_wc__db_pristine_prepare_install() 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, +svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data, const svn_checksum_t *sha1_checksum, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool); +/* Removes the temporary data created by svn_wc__db_pristine_prepare_install + when the pristine won't be installed. */ +svn_error_t * +svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, + 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 @@ -1248,6 +1253,53 @@ svn_wc__db_committable_externals_below(apr_array_header_t **externals, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Opaque struct for svn_wc__db_create_commit_queue, svn_wc__db_commit_queue_add, + svn_wc__db_process_commit_queue */ +typedef struct svn_wc__db_commit_queue_t svn_wc__db_commit_queue_t; + +/* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the + working copy specified with WRI_ABSPATH */ +svn_error_t * +svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue, + svn_wc__db_t *db, + const char *wri_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Adds the specified path to the commit queue with the related information. + + See svn_wc_queue_committed4() for argument documentation. + + Note that this function currently DOESN'T copy the passed values to + RESULT_POOL, but expects them to be valid until processing. Otherwise the + only users memory requirements would +- double. + */ +svn_error_t * +svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue, + const char *local_abspath, + svn_boolean_t recurse, + svn_boolean_t is_commited, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *new_dav_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Process the items in QUEUE in a single transaction. Commit workqueue items + for items that need post processing. + + Implementation detail of svn_wc_process_committed_queue2(). + */ +svn_error_t * +svn_wc__db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + 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. @@ -1616,6 +1668,9 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, * * At present only depth=empty and depth=infinity are supported. * + * If @a clear_changelists is FALSE then changelist information is kept, + * otherwise it is cleared. + * * This function populates the revert list that can be queried to * determine what was reverted. */ @@ -1623,6 +1678,7 @@ svn_error_t * svn_wc__db_op_revert(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t clear_changelists, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -1658,7 +1714,7 @@ typedef struct svn_wc__db_revert_list_copied_child_info_t { * 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_revert_list_read_copied_children(apr_array_header_t **children, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -1963,6 +2019,7 @@ struct svn_wc__db_info_t { svn_boolean_t moved_here; /* Only on op-roots. */ svn_boolean_t file_external; + svn_boolean_t has_descendants; /* Is dir, or has tc descendants */ }; /* Return in *NODES a hash mapping name->struct svn_wc__db_info_t for @@ -1972,27 +2029,36 @@ struct svn_wc__db_info_t { 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. + + If BASE_TREE_ONLY is set, only information about the BASE tree + is returned. */ 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, + svn_boolean_t base_tree_only, 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. */ + element. + + If BASE_TREE_ONLY is set, only information about the BASE tree + is returned. */ 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, + svn_boolean_t base_tree_only, 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 { + const char *name; svn_wc__db_status_t status; svn_node_kind_t kind; }; @@ -2053,11 +2119,10 @@ svn_wc__db_read_node_install_info(const char **wcroot_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. */ +/* Return in *ITEMS an array of struct svn_wc__db_walker_info_t* for + the direct children of DIR_ABSPATH. */ svn_error_t * -svn_wc__db_read_children_walker_info(apr_hash_t **nodes, +svn_wc__db_read_children_walker_info(const apr_array_header_t **items, svn_wc__db_t *db, const char *dir_abspath, apr_pool_t *result_pool, @@ -2065,15 +2130,26 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, /** - * Set *URL to the corresponding url for LOCAL_ABSPATH. - * If the node is added, return the url it will have in the repository. + * Set *revision, *repos_relpath, *repos_root_url, *repos_uuid to + * the intended/commit location of LOCAL_ABSPATH. These arguments may be + * NULL if they are not needed. + * + * If the node is deleted, return the url it would have in the repository + * if it wouldn't be deleted. If the node is added return the url it will + * have in the repository, once committed. + * + * If the node is not added and has an existing repository location, set + * revision to its existing revision, otherwise to SVN_INVALID_REVNUM. */ 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_read_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); /* Set *PROPS to the properties of the node LOCAL_ABSPATH in the ACTUAL @@ -2114,16 +2190,16 @@ svn_wc__db_read_props_streamily(svn_wc__db_t *db, 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() +/* Set *PROPS to the base properties of the node at LOCAL_ABSPATH. *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. + If the base node is in a state that cannot have properties (such as + not-present or locally added without copy-from), return an error. + Allocate *PROPS and its keys and values in RESULT_POOL. + + See also svn_wc_get_pristine_props(). */ svn_error_t * svn_wc__db_read_pristine_props(apr_hash_t **props, @@ -2146,7 +2222,7 @@ svn_wc__db_read_pristine_props(apr_hash_t **props, * 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 + * If ACTUAL_PROPS is not NULL, then set *ACTUAL_PROPS to ALL the actual * properties stored on LOCAL_ABSPATH. * * Allocate @a *iprops in @a result_pool. Use @a scratch_pool @@ -2237,6 +2313,14 @@ svn_wc__db_read_children_of_working_node(const apr_array_header_t **children, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__db_base_read_not_present_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); + /* 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 @@ -2279,7 +2363,10 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **markers, apr_pool_t *scratch_pool); /* Read the conflict information recorded on LOCAL_ABSPATH in *CONFLICT, - an editable conflict skel. + an editable conflict skel. If kind is not NULL, also read the node kind + in *KIND. (SHOW_HIDDEN: false, SHOW_DELETED: true). If props is not NULL + read the actual properties in this value if they exist. (Set to NULL in case + the node is deleted, etc.) If the node exists, but does not have a conflict set *CONFLICT to NULL, otherwise return a SVN_ERR_WC_PATH_NOT_FOUND error. @@ -2288,6 +2375,8 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **markers, SCRATCH_POOL */ svn_error_t * svn_wc__db_read_conflict(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -2323,16 +2412,6 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, 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 @@ -2444,11 +2523,6 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, 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 * @@ -2459,7 +2533,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, 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, @@ -2540,6 +2613,9 @@ svn_wc__db_global_update(svn_wc__db_t *db, for pathnames contained in EXCLUDE_RELPATHS are not touched by this function. These pathnames should be paths relative to the wcroot. + If EMPTY_UPDATE is TRUE then no nodes at or below LOCAL_ABSPATH have been + affected by the update/switch yet. + 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 @@ -2556,6 +2632,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool); @@ -2605,10 +2682,12 @@ svn_wc__db_lock_add(svn_wc__db_t *db, apr_pool_t *scratch_pool); -/* Remove any lock for LOCAL_ABSPATH in DB. */ +/* Remove any lock for LOCAL_ABSPATH in DB and install WORK_ITEMS + (if not NULL) in DB */ svn_error_t * svn_wc__db_lock_remove(svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -2619,30 +2698,6 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, @{ */ -/* 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 @@ -2707,7 +2762,8 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, 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 LOCAL_ABSPATH is not moved here return an + * 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. @@ -2879,34 +2935,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, 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, @@ -2921,20 +2949,6 @@ svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, 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. * @@ -3075,10 +3089,6 @@ svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, 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 @@ -3096,9 +3106,6 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, 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, @@ -3192,9 +3199,21 @@ svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db, 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. */ +/* When local_abspath has no WORKING layer, copy the base tree at + LOCAL_ABSPATH into the working tree as copy, leaving any subtree + additions and copies as-is. This may introduce multiple layers if + the tree is mixed revision. + + When local_abspath has a WORKING node, but is not an op-root, copy + all descendants at the same op-depth to the op-depth of local_abspath, + thereby turning this node in a copy of what was already there. + + Fails with a SVN_ERR_WC_PATH_UNEXPECTED_STATUS error if LOCAL_RELPATH + is already an op-root (as in that case it can't be copied as that + would overwrite what is already there). + + After this operation the copied layer (E.g. BASE) can be removed, without + the WORKING nodes chaning. Typical usecase: tree conflict handling */ svn_error_t * svn_wc__db_op_make_copy(svn_wc__db_t *db, const char *local_abspath, @@ -3249,7 +3268,8 @@ svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants, * * 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_MODIFIED whether the working copy has local modifications + * recorded for it in DB. * * 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 @@ -3270,8 +3290,6 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, 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 @@ -3332,16 +3350,13 @@ svn_wc__db_get_excluded_subtrees(apr_hash_t **server_excluded_subtrees, /* 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(). */ + * This function does not check the working copy state, but is a lot more + * efficient than a full status walk. */ 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_has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); /* Verify the consistency of metadata concerning the WC that contains @@ -3369,27 +3384,31 @@ svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos, 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. */ +/* Update a moved-away tree conflict victim LOCAL_ABSPATH, deleted in + DELETE_OP_ABSPATH with changes from the original location. */ 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, + const char *local_abspath, + const char *delete_op_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_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 + * DELETE_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, + const char **delete_abspath, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -3407,39 +3426,24 @@ svn_wc__db_vacuum(svn_wc__db_t *db, 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); +svn_wc__db_op_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); -/* 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. */ +/* Breaks all moves of nodes that exist at or below LOCAL_ABSPATH as + shadowed (read: deleted) by the opration rooted at + delete_op_root_abspath. + */ 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); +svn_wc__db_op_break_moved_away(svn_wc__db_t *db, + const char *local_abspath, + const char *delete_op_root_abspath, + svn_boolean_t mark_tc_resolved, + 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 @@ -3452,6 +3456,28 @@ svn_wc__required_lock_for_resolve(const char **required_abspath, apr_pool_t *scratch_pool); /* @} */ +typedef svn_error_t * (*svn_wc__db_verify_cb_t)(void *baton, + const char *wc_abspath, + const char *local_relpath, + int op_depth, + int id, + const char *description, + apr_pool_t *scratch_pool); + +/* Checks the database for FULL-correctness according to the spec. + + Note that typical 1.7-1.9 databases WILL PRODUCE warnings. + + This is mainly useful for WC-NG developers, as there will be + warnings without the database being corrupt +*/ +svn_error_t * +svn_wc__db_verify_db_full(svn_wc__db_t *db, + const char *wri_abspath, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db_pristine.c b/contrib/subversion/subversion/libsvn_wc/wc_db_pristine.c index d9dc8f396..9118d7068 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db_pristine.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db_pristine.c @@ -26,8 +26,11 @@ #define SVN_WC__I_AM_WC_DB #include "svn_pools.h" +#include "svn_io.h" #include "svn_dirent_uri.h" +#include "private/svn_io_private.h" + #include "wc.h" #include "wc_db.h" #include "wc-queries.h" @@ -67,7 +70,7 @@ get_pristine_fname(const char **pristine_abspath, wcroot_abspath, svn_wc_get_adm_dir(scratch_pool), PRISTINE_STORAGE_RELPATH, - NULL); + SVN_VA_NULL); /* We should have a valid checksum and (thus) a valid digest. */ SVN_ERR_ASSERT(hexdigest != NULL); @@ -78,14 +81,14 @@ get_pristine_fname(const char **pristine_abspath, subdir[2] = '\0'; hexdigest = apr_pstrcat(scratch_pool, hexdigest, PRISTINE_STORAGE_EXT, - (char *)NULL); + SVN_VA_NULL); /* The file is located at DIR/.svn/pristine/XX/XXYYZZ...svn-base */ *pristine_abspath = svn_dirent_join_many(result_pool, base_dir_abspath, subdir, hexdigest, - NULL); + SVN_VA_NULL); return SVN_NO_ERROR; } @@ -194,10 +197,20 @@ pristine_read_txn(svn_stream_t **contents, } /* Open the file as a readable stream. It will remain readable even when - * deleted from disk; APR guarantees that on Windows as well as Unix. */ + * deleted from disk; APR guarantees that on Windows as well as Unix. + * + * We also don't enable APR_BUFFERED on this file to maximize throughput + * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers + * where needed in streams, there is no point in having another layer of + * buffers. */ if (contents) - SVN_ERR(svn_stream_open_readonly(contents, pristine_abspath, - result_pool, scratch_pool)); + { + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, + APR_OS_DEFAULT, result_pool)); + *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + } + return SVN_NO_ERROR; } @@ -253,31 +266,9 @@ pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot, { return svn_dirent_join_many(result_pool, wcroot->abspath, svn_wc_get_adm_dir(scratch_pool), - PRISTINE_TEMPDIR_RELPATH, (char *)NULL); + PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL); } -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) -{ - 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 = pristine_get_tempdir(wcroot, result_pool, scratch_pool); - return SVN_NO_ERROR; -} - - /* Install the pristine text described by BATON into the pristine store of * SDB. If it is already stored then just delete the new file * BATON->tempfile_abspath. @@ -290,7 +281,7 @@ svn_wc__db_pristine_get_tempdir(const char **temp_dir_abspath, static svn_error_t * pristine_install_txn(svn_sqlite__db_t *sdb, /* The path to the source file that is to be moved into place. */ - const char *tempfile_abspath, + svn_stream_t *install_stream, /* The target path for the file (within the pristine store). */ const char *pristine_abspath, /* The pristine text's SHA-1 checksum. */ @@ -299,10 +290,8 @@ pristine_install_txn(svn_sqlite__db_t *sdb, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { - apr_finfo_t finfo; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - svn_error_t *err; /* If this pristine text is already present in the store, just keep it: * delete the new one and return. */ @@ -310,6 +299,7 @@ pristine_install_txn(svn_sqlite__db_t *sdb, SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); + if (have_row) { #ifdef SVN_DEBUG @@ -317,8 +307,10 @@ pristine_install_txn(svn_sqlite__db_t *sdb, * ### We could check much more. */ { apr_finfo_t finfo1, finfo2; - SVN_ERR(svn_io_stat(&finfo1, tempfile_abspath, APR_FINFO_SIZE, - scratch_pool)); + + SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE, + scratch_pool)); + SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE, scratch_pool)); if (finfo1.size != finfo2.size) @@ -333,84 +325,94 @@ pristine_install_txn(svn_sqlite__db_t *sdb, #endif /* Remove the temp file: it's already there */ - SVN_ERR(svn_io_remove_file2(tempfile_abspath, - FALSE /* ignore_enoent */, scratch_pool)); + SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); return SVN_NO_ERROR; } /* Move the file to its target location. (If it is already there, it is * an orphan file and it doesn't matter if we overwrite it.) */ - err = svn_io_file_rename(tempfile_abspath, pristine_abspath, - scratch_pool); + { + apr_finfo_t finfo; + SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, APR_FINFO_SIZE, + scratch_pool)); + SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, + TRUE, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_INSERT_PRISTINE)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + + SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool)); + } - /* Maybe the directory doesn't exist yet? */ - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_t *err2; + return SVN_NO_ERROR; +} - err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath, - scratch_pool), - APR_OS_DEFAULT, scratch_pool); +struct svn_wc__db_install_data_t +{ + svn_wc__db_wcroot_t *wcroot; + svn_stream_t *inner_stream; +}; - if (err2) - /* Creating directory didn't work: Return all errors */ - return svn_error_trace(svn_error_compose_create(err, err2)); - else - /* We could create a directory: retry install */ - svn_error_clear(err); +svn_error_t * +svn_wc__db_pristine_prepare_install(svn_stream_t **stream, + svn_wc__db_install_data_t **install_data, + svn_checksum_t **sha1_checksum, + svn_checksum_t **md5_checksum, + 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; + const char *temp_dir_abspath; - SVN_ERR(svn_io_file_rename(tempfile_abspath, pristine_abspath, - scratch_pool)); - } - else - SVN_ERR(err); + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); - SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE, - 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(svn_sqlite__get_statement(&stmt, sdb, - STMT_INSERT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); - SVN_ERR(svn_sqlite__insert(NULL, stmt)); + temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool); + + *install_data = apr_pcalloc(result_pool, sizeof(**install_data)); + (*install_data)->wcroot = wcroot; + + SVN_ERR_W(svn_stream__create_for_install(stream, + temp_dir_abspath, + result_pool, scratch_pool), + _("Unable to create pristine install stream")); + + (*install_data)->inner_stream = *stream; + + if (md5_checksum) + *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, + svn_checksum_md5, FALSE, result_pool); + if (sha1_checksum) + *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, + svn_checksum_sha1, FALSE, result_pool); return SVN_NO_ERROR; } - svn_error_t * -svn_wc__db_pristine_install(svn_wc__db_t *db, - const char *tempfile_abspath, +svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data, const svn_checksum_t *sha1_checksum, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - const char *wri_abspath; + svn_wc__db_wcroot_t *wcroot = install_data->wcroot; const char *pristine_abspath; - SVN_ERR_ASSERT(svn_dirent_is_absolute(tempfile_abspath)); SVN_ERR_ASSERT(sha1_checksum != NULL); SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1); SVN_ERR_ASSERT(md5_checksum != NULL); SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5); - /* ### this logic assumes that TEMPFILE_ABSPATH follows this pattern: - ### WCROOT_ABSPATH/COMPONENT/COMPONENT/TEMPFNAME - ### if we change this (see PRISTINE_TEMPDIR_RELPATH), then this - ### logic should change. */ - wri_abspath = svn_dirent_dirname( - svn_dirent_dirname( - svn_dirent_dirname(tempfile_abspath, scratch_pool), - scratch_pool), - 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(get_pristine_fname(&pristine_abspath, wcroot->abspath, sha1_checksum, scratch_pool, scratch_pool)); @@ -419,7 +421,7 @@ svn_wc__db_pristine_install(svn_wc__db_t *db, * at the disk, to ensure no concurrent pristine install/delete txn. */ SVN_SQLITE__WITH_IMMEDIATE_TXN( pristine_install_txn(wcroot->sdb, - tempfile_abspath, pristine_abspath, + install_data->inner_stream, pristine_abspath, sha1_checksum, md5_checksum, scratch_pool), wcroot->sdb); @@ -427,6 +429,14 @@ svn_wc__db_pristine_install(svn_wc__db_t *db, return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_stream__install_delete(install_data->inner_stream, + scratch_pool)); +} + svn_error_t * svn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum, @@ -823,12 +833,44 @@ svn_wc__db_pristine_remove(svn_wc__db_t *db, } +/* Remove all unreferenced pristines in the WC DB in WCROOT. + * + * Look for pristine texts whose 'refcount' in the DB is zero, and remove + * them from the 'pristine' table and from disk. + * + * TODO: At least check that any zero refcount is really correct, before + * using it. See dev@ email thread "Pristine text missing - cleanup + * doesn't work", . + * + * TODO: Ideas for possible extra clean-up operations: + * + * * Check and correct all the refcounts. Identify any rows missing + * from the 'pristine' table. (Create a temporary index for speed + * if necessary?) + * + * * Check the checksums. (Very expensive to check them all, so find + * a way to not check them all.) + * + * * Check for pristine files missing from disk but referenced in the + * 'pristine' table. + * + * * Repair any pristine files missing from disk and/or rows missing + * from the 'pristine' table and/or bad checksums. Generally + * requires contacting the server, so requires support at a higher + * level than this function. + * + * * Identify any pristine text files on disk that are not referenced + * in the DB, and delete them. + * + * TODO: Provide feedback about any errors found and any corrections made. + */ static svn_error_t * pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_error_t *err = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); /* Find each unreferenced pristine in the DB and remove it. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -838,16 +880,20 @@ pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot, svn_boolean_t have_row; const svn_checksum_t *sha1_checksum; + svn_pool_clear(iterpool); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (! have_row) break; SVN_ERR(svn_sqlite__column_checksum(&sha1_checksum, stmt, 0, - scratch_pool)); + iterpool)); err = pristine_remove_if_unreferenced(wcroot, sha1_checksum, - scratch_pool); + iterpool); } + svn_pool_destroy(iterpool); + return svn_error_trace( svn_error_compose_create(err, svn_sqlite__reset(stmt))); } diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db_private.h b/contrib/subversion/subversion/libsvn_wc/wc_db_private.h index a4bf98f67..a9d7b0417 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db_private.h +++ b/contrib/subversion/subversion/libsvn_wc/wc_db_private.h @@ -42,12 +42,16 @@ struct svn_wc__db_t { 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? */ + /* Should we ensure the WORK_QUEUE is empty when a DB is locked + * for writing? */ svn_boolean_t enforce_empty_wq; /* Should we open Sqlite databases EXCLUSIVE */ svn_boolean_t exclusive; + /* Busy timeout in ms., 0 for the libsvn_subr default. */ + apr_int32_t timeout; + /* 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; @@ -122,7 +126,6 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, 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); @@ -145,6 +148,9 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Return an error if the work queue in SDB is non-empty. */ +svn_error_t * +svn_wc__db_verify_no_work(svn_sqlite__db_t *sdb); /* Assert that the given WCROOT is usable. NOTE: the expression is multiply-evaluated!! */ @@ -192,7 +198,7 @@ svn_wc__db_util_fetch_wc_id(apr_int64_t *wc_id, /* 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(). + * SMODE, EXCLUSIVE and TIMEOUT are 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 @@ -205,10 +211,18 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, const char *sdb_fname, svn_sqlite__mode_t smode, svn_boolean_t exclusive, + apr_int32_t timeout, const char *const *my_statements, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Like svn_wc__db_wq_add() but taking WCROOT */ +svn_error_t * +svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot, + const svn_skel_t *work_item, + 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 * @@ -299,14 +313,40 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, 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, +svn_error_t * +svn_wc__db_scan_addition_internal( + 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, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +svn_error_t * +svn_wc__db_scan_deletion_internal( + 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); + + +/* Look up REPOS_ID in WCROOT->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, + svn_wc__db_wcroot_t *wcroot, apr_int64_t repos_id, apr_pool_t *result_pool); @@ -314,6 +354,8 @@ svn_wc__db_fetch_repos_info(const char **repos_root_url, DB+LOCAL_ABSPATH, and outputting relpaths instead of abspaths. */ svn_error_t * svn_wc__db_read_conflict_internal(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_pool_t *result_pool, @@ -330,23 +372,6 @@ svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot, /* 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 @@ -369,79 +394,91 @@ svn_wc__db_with_txn(svn_wc__db_wcroot_t *wcroot, #define SVN_WC__DB_WITH_TXN4(expr1, expr2, expr3, expr4, wcroot) \ SVN_SQLITE__WITH_LOCK4(expr1, expr2, expr3, expr4, (wcroot)->sdb) +/* Update the single op-depth layer in the move destination subtree + rooted at DST_RELPATH to make it match the move source subtree + rooted at SRC_RELPATH. */ +svn_error_t * +svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_op_relpath, + int src_op_depth, + const char *dst_op_relpath, + svn_skel_t *conflict, + svn_skel_t *work_items, + apr_pool_t *scratch_pool); -/* Return CHILDREN mapping const char * names to svn_node_kind_t * for the - children of LOCAL_RELPATH at OP_DEPTH. */ +/* Like svn_wc__db_op_make_copy but with wcroot, local_relpath */ svn_error_t * -svn_wc__db_get_children_op_depth(apr_hash_t **children, - svn_wc__db_wcroot_t *wcroot, +svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, - apr_pool_t *result_pool, + svn_boolean_t move_move_info, + const svn_skel_t *conflicts, + const svn_skel_t *work_items, apr_pool_t *scratch_pool); -/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH. +/* Extract the moved-to information for LOCAL_RELPATH as it existed + at OP-DEPTH. The output paths are optional and set to NULL + if there is no move, otherwise: - ### 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 ...? + *MOVE_SRC_RELPATH: the path that was moved (LOCAL_RELPATH or one + of its ancestors) - Given a wc: + *MOVE_DST_RELPATH: The path *MOVE_SRC_RELPATH was moved to. - 0 1 2 3 4 - normal - A normal - A/B normal normal - A/B/C not-pres normal - A/B/C/D normal + *DELETE_RELPATH: The path at which LOCAL_RELPATH was removed ( + *MOVE_SRC_RELPATH or one of its ancestors) - 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. + Given a path A/B/C with A/B moved to X and A deleted then for A/B/C: - 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: + MOVE_SRC_RELPATH is A/B + MOVE_DST_RELPATH is X + DELETE_RELPATH is A - 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 + X/C can be calculated if necessesary, like with the other + scan functions. - 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. + This function returns SVN_ERR_WC_PATH_NOT_FOUND if LOCAL_RELPATH didn't + exist at OP_DEPTH, or when it is not shadowed. - 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. - */ + ### 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_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_wc__db_scan_moved_to_internal(const char **move_src_relpath, + const char **move_dst_relpath, + const char **delete_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* Like svn_wc__db_op_set_props, but updates ACTUAL_NODE directly without + comparing with the pristine properties, etc. +*/ svn_error_t * -svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, +svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, + apr_hash_t *props, + svn_boolean_t clear_recorded_info, 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); +svn_wc__db_read_props_internal(apr_hash_t **props, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead + of DB+LOCAL_ABSPATH. */ +svn_error_t * +svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t exact, + 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 @@ -455,12 +492,38 @@ svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool); /* Unbreak the move from LOCAL_RELPATH on op-depth in WCROOT, by making - the destination a normal copy */ + the destination DST_RELPATH a normal copy. SRC_OP_DEPTH is the op-depth + where the move_to information is stored */ 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_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + const char *dst_relpath, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool); + +svn_error_t * +svn_wc__db_op_mark_resolved_internal(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); + +/* op_depth is the depth at which the node is added. */ +svn_error_t * +svn_wc__db_op_raise_moved_away_internal( + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + svn_wc__db_t *db, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + apr_pool_t *scratch_pool); svn_error_t * svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, @@ -470,4 +533,10 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, void *notify_baton, apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool); + #endif /* WC_DB_PRIVATE_H */ diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c b/contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c index 7f4f85399..46cbeae36 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c @@ -46,7 +46,7 @@ * layer. The destination may have additional higher op-depths * representing adds, deletes, moves within the move destination. [2] * - * After the intial move an update has modified the NODES in the move + * After the initial move an update has modified the NODES in the move * source and may have introduced a tree-conflict since the source and * destination trees are no longer equivalent. The source is a * different revision and may have text, property and tree changes @@ -88,9 +88,9 @@ #include "svn_sorts.h" #include "private/svn_skel.h" +#include "private/svn_sorts_private.h" #include "private/svn_sqlite.h" #include "private/svn_wc_private.h" -#include "private/svn_editor.h" #include "wc.h" #include "props.h" @@ -100,6 +100,80 @@ #include "workqueue.h" #include "token-map.h" +/* Helper functions */ +/* 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); +} + +/* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */ +static svn_error_t * +verify_write_lock(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath, + FALSE, scratch_pool)); + if (!locked) + { + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* In our merge conflicts we record the move_op_src path, which is essentially + the depth at which what was moved is marked deleted. The problem is that + this depth is not guaranteed to be stable, because somebody might just + remove another ancestor, or revert one. + + To work around this problem we locate the layer below this path, and use + that to pinpoint whatever is moved. + + For a path SRC_RELPATH that was deleted by an operation rooted at + DELETE_OP_DEPTH find the op-depth at which the node was originally added. + */ +static svn_error_t * +find_src_op_depth(int *src_op_depth, + svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + 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_HIGHEST_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + src_relpath, delete_op_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + *src_op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + if (!have_row) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is not deleted"), + path_for_error_message(wcroot, src_relpath, + scratch_pool)); + + return SVN_NO_ERROR; +} + /* * Receiver code. * @@ -113,20 +187,34 @@ * be made at the move destination. */ -struct tc_editor_baton { +typedef struct update_move_baton_t { svn_wc__db_t *db; svn_wc__db_wcroot_t *wcroot; - const char *move_root_dst_relpath; - /* The most recent conflict raised during this drive. We rely on the - non-Ev2, depth-first, drive for this to make sense. */ - const char *conflict_root_relpath; + int src_op_depth; + int dst_op_depth; svn_wc_operation_t operation; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; - apr_pool_t *result_pool; /* For things that live as long as the baton. */ -}; + + svn_cancel_func_t cancel_func; + void *cancel_baton; +} update_move_baton_t; + +/* Per node flags for tree conflict collection */ +typedef struct node_move_baton_t +{ + svn_boolean_t skip; + svn_boolean_t shadowed; + svn_boolean_t edited; + + const char *src_relpath; + const char *dst_relpath; + + update_move_baton_t *umb; + struct node_move_baton_t *pb; +} node_move_baton_t; /* * Notifications are delayed until the entire update-move transaction @@ -135,24 +223,52 @@ struct tc_editor_baton { * and spooling notifications out of that table after the transaction. */ -/* Add an entry to the notification list. */ +/* Add an entry to the notification list, and at the same time install + a conflict and/or work items. */ static svn_error_t * update_move_list_add(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_wc__db_t *db, svn_wc_notify_action_t action, svn_node_kind_t kind, svn_wc_notify_state_t content_state, - svn_wc_notify_state_t prop_state) - + svn_wc_notify_state_t prop_state, + svn_skel_t *conflict, + svn_skel_t *work_item, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + if (conflict) + { + svn_boolean_t tree_conflict; + + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, + &tree_conflict, + db, wcroot->abspath, conflict, + scratch_pool, scratch_pool)); + if (tree_conflict) + { + action = svn_wc_notify_tree_conflict; + content_state = svn_wc_notify_state_inapplicable; + prop_state = svn_wc_notify_state_inapplicable; + } + } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_UPDATE_MOVE_LIST)); - SVN_ERR(svn_sqlite__bindf(stmt, "sdddd", local_relpath, - action, kind, content_state, prop_state)); + SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath, + action, kind_map_none, kind, + content_state, prop_state)); SVN_ERR(svn_sqlite__step_done(stmt)); + if (conflict) + SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict, + scratch_pool)); + + if (work_item) + SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool)); + return SVN_NO_ERROR; } @@ -192,7 +308,7 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, local_relpath, iterpool), action, iterpool); - notify->kind = svn_sqlite__column_int(stmt, 2); + notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none); notify->content_state = svn_sqlite__column_int(stmt, 3); notify->prop_state = svn_sqlite__column_int(stmt, 4); notify->old_revision = old_revision; @@ -212,23 +328,25 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, return SVN_NO_ERROR; } -/* Mark a tree-conflict on LOCAL_RELPATH if such a tree-conflict does - not already exist. */ +/* Create a tree-conflict for recording on LOCAL_RELPATH if such + a tree-conflict does not already exist. */ static svn_error_t * -mark_tree_conflict(const char *local_relpath, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - const svn_wc_conflict_version_t *old_version, - const svn_wc_conflict_version_t *new_version, - const char *move_root_dst_relpath, - svn_wc_operation_t operation, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_reason_t reason, - svn_wc_conflict_action_t action, - const char *move_src_op_root_relpath, - apr_pool_t *scratch_pool) +create_tree_conflict(svn_skel_t **conflict_p, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + const char *dst_op_root_relpath, + svn_wc__db_t *db, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + svn_wc_operation_t operation, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + const char *old_repos_relpath, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err; svn_skel_t *conflict; @@ -250,14 +368,18 @@ mark_tree_conflict(const char *local_relpath, : NULL; if (!new_repos_relpath) - new_repos_relpath - = svn_relpath_join(new_version->path_in_repos, - svn_relpath_skip_ancestor(move_root_dst_relpath, - local_relpath), - scratch_pool); - - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, local_relpath, - scratch_pool, scratch_pool); + { + const char *child_relpath = svn_relpath_skip_ancestor( + dst_op_root_relpath, + local_relpath); + SVN_ERR_ASSERT(child_relpath != NULL); + new_repos_relpath = svn_relpath_join(new_version->path_in_repos, + child_relpath, scratch_pool); + } + + err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + result_pool, scratch_pool); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); else if (err) @@ -280,7 +402,7 @@ mark_tree_conflict(const char *local_relpath, && conflict_operation != svn_wc_operation_switch) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, local_relpath, scratch_pool)); if (tree_conflicted) @@ -302,17 +424,19 @@ mark_tree_conflict(const char *local_relpath, && strcmp(move_src_op_root_relpath, svn_dirent_skip_ancestor(wcroot->abspath, existing_abspath)))) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, + local_relpath, scratch_pool)); /* Already a suitable tree-conflict. */ + *conflict_p = conflict; return SVN_NO_ERROR; } } else - conflict = svn_wc__conflict_skel_create(scratch_pool); + conflict = svn_wc__conflict_skel_create(result_pool); SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( conflict, db, @@ -321,19 +445,13 @@ mark_tree_conflict(const char *local_relpath, reason, action, move_src_op_root_abspath, - scratch_pool, + result_pool, scratch_pool)); - if (reason != svn_wc_conflict_reason_unversioned - && old_repos_relpath != NULL /* no local additions */) - { - conflict_old_version = svn_wc_conflict_version_create2( + conflict_old_version = svn_wc_conflict_version_create2( old_version->repos_url, old_version->repos_uuid, old_repos_relpath, old_version->peg_rev, old_kind, scratch_pool); - } - else - conflict_old_version = NULL; conflict_new_version = svn_wc_conflict_version_create2( new_version->repos_url, new_version->repos_uuid, @@ -344,312 +462,368 @@ mark_tree_conflict(const char *local_relpath, { SVN_ERR(svn_wc__conflict_skel_set_op_update( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } else { assert(operation == svn_wc_operation_switch); SVN_ERR(svn_wc__conflict_skel_set_op_switch( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, - svn_wc_notify_tree_conflict, - new_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + *conflict_p = conflict; return SVN_NO_ERROR; } -/* If LOCAL_RELPATH is a child of the most recently raised - tree-conflict or is shadowed then set *IS_CONFLICTED to TRUE and - raise a tree-conflict on the root of the obstruction if such a - tree-conflict does not already exist. KIND is the kind of the - incoming LOCAL_RELPATH. This relies on the non-Ev2, depth-first, - drive. */ static svn_error_t * -check_tree_conflict(svn_boolean_t *is_conflicted, - struct tc_editor_baton *b, +create_node_tree_conflict(svn_skel_t **conflict_p, + node_move_baton_t *nmb, + const char *dst_local_relpath, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *umb = nmb->umb; + const char *dst_repos_relpath; + const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool); + + dst_repos_relpath = + svn_relpath_join(nmb->umb->old_version->path_in_repos, + svn_relpath_skip_ancestor(dst_root_relpath, + nmb->dst_relpath), + scratch_pool); + + + + return svn_error_trace( + create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath, + svn_relpath_prefix(dst_local_relpath, + umb->dst_op_depth, + scratch_pool), + umb->db, + umb->old_version, umb->new_version, + umb->operation, old_kind, new_kind, + dst_repos_relpath, + reason, action, move_src_op_root_relpath, + result_pool, scratch_pool)); +} + +/* Checks if a specific local path is shadowed as seen from the move root. + Helper for update_moved_away_node() */ +static svn_error_t * +check_node_shadowed(svn_boolean_t *shadowed, + svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_action_t action, + int move_root_dst_op_depth, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int dst_op_depth = relpath_depth(b->move_root_dst_relpath); - int op_depth; - const char *conflict_root_relpath = local_relpath; - const char *move_dst_relpath, *dummy1; - const char *dummy2, *move_src_op_root_relpath; - if (b->conflict_root_relpath) - { - if (svn_relpath_skip_ancestor(b->conflict_root_relpath, local_relpath)) - { - *is_conflicted = TRUE; - return SVN_NO_ERROR; - } - b->conflict_root_relpath = NULL; - } + 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__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, local_relpath, - dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) - op_depth = svn_sqlite__column_int(stmt, 0); + { + int op_depth = svn_sqlite__column_int(stmt, 0); + + *shadowed = (op_depth > move_root_dst_op_depth); + } + else + *shadowed = FALSE; SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) + return SVN_NO_ERROR; +} + +/* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is + the ROOT OF THE OBSTRUCTION if such a tree-conflict does not + already exist. KIND is the kind of the incoming LOCAL_RELPATH. */ +static svn_error_t * +mark_tc_on_op_root(node_move_baton_t *nmb, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_action_t action, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *b = nmb->umb; + const char *move_dst_relpath; + svn_skel_t *conflict; + + SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed); + + nmb->skip = TRUE; + + if (old_kind == svn_node_none) + move_dst_relpath = NULL; + else + SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL, + b->wcroot, nmb->dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath, + old_kind, new_kind, + (move_dst_relpath + ? svn_wc_conflict_reason_moved_away + : svn_wc_conflict_reason_deleted), + action, move_dst_relpath + ? nmb->dst_relpath + : NULL, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_node_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + if (nmb->edited) + return SVN_NO_ERROR; + + if (nmb->pb) { - *is_conflicted = FALSE; - return SVN_NO_ERROR; + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; } - *is_conflicted = TRUE; + nmb->edited = TRUE; + + if (nmb->skip) + return SVN_NO_ERROR; - while (relpath_depth(conflict_root_relpath) > op_depth) + if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed)) { - conflict_root_relpath = svn_relpath_dirname(conflict_root_relpath, - scratch_pool); - old_kind = new_kind = svn_node_dir; - if (old_repos_relpath) - old_repos_relpath = svn_relpath_dirname(old_repos_relpath, - scratch_pool); - action = svn_wc_conflict_action_edit; + svn_node_kind_t dst_kind, src_kind; + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->src_relpath, + nmb->umb->src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(mark_tc_on_op_root(nmb, + dst_kind, src_kind, + svn_wc_conflict_action_edit, + scratch_pool)); } - SVN_ERR(svn_wc__db_op_depth_moved_to(&move_dst_relpath, - &dummy1, - &dummy2, - &move_src_op_root_relpath, - dst_op_depth, - b->wcroot, conflict_root_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR(mark_tree_conflict(conflict_root_relpath, - b->wcroot, b->db, b->old_version, b->new_version, - b->move_root_dst_relpath, b->operation, - old_kind, new_kind, - old_repos_relpath, - (move_dst_relpath - ? svn_wc_conflict_reason_moved_away - : svn_wc_conflict_reason_deleted), - action, move_src_op_root_relpath, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, conflict_root_relpath); + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_parent_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + SVN_ERR_ASSERT(nmb && nmb->pb); + + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_directory(void *baton, +tc_editor_add_directory(node_move_baton_t *nmb, const char *relpath, - const apr_array_header_t *children, + svn_node_kind_t old_kind, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_node_kind_t old_kind; - svn_skel_t *work_item; - svn_wc_notify_action_t action = svn_wc_notify_update_add; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + const char *local_abspath; + svn_node_kind_t wc_kind; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_dir, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else + wc_kind = svn_node_none; + + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + + if (wc_kind == svn_node_none) { - SVN_ERR(err); - old_kind = move_dst_kind; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; - - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); + if (!nmb->shadowed && wc_kind == old_kind) + wc_kind = svn_node_none; /* Node will be gone once we install */ - switch (old_kind) + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ { - case svn_node_file: - default: - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - action = svn_wc_notify_tree_conflict; - is_conflicted = TRUE; - break; - - case svn_node_none: - SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, abspath, + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_dir, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath, scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - /* Fall through */ - case svn_node_dir: - break; } - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - action, - svn_node_dir, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, + svn_node_dir, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_file(void *baton, +tc_editor_add_file(node_move_baton_t *nmb, const char *relpath, + svn_node_kind_t old_kind, const svn_checksum_t *checksum, - svn_stream_t *contents, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_node_kind_t old_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_skel_t *work_item; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + svn_node_kind_t wc_kind; + const char *local_abspath; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_file, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else - { - SVN_ERR(err); - old_kind = move_dst_kind; - } - - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_file, move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; + wc_kind = svn_node_none; - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - if (old_kind != svn_node_none) + if (wc_kind == svn_node_none) { - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - return SVN_NO_ERROR; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Update working file. */ - SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, - svn_dirent_join(b->wcroot->abspath, - relpath, - scratch_pool), + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ + { + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_file, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, NULL, - FALSE /* FIXME: use_commit_times? */, - TRUE /* record_file_info */, scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + /* Update working file. */ + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, + svn_dirent_join(b->wcroot->abspath, + relpath, + scratch_pool), + NULL, + FALSE /*FIXME: use_commit_times?*/, + TRUE /* record_file_info */, + scratch_pool, scratch_pool)); + } - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_add, + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, svn_node_file, svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } -static svn_error_t * -tc_editor_add_symlink(void *baton, - const char *relpath, - const char *target, - apr_hash_t *props, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_add_absent(void *baton, - const char *relpath, - svn_node_kind_t kind, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - /* All the info we need about one version of a working node. */ typedef struct working_node_version_t { @@ -669,6 +843,7 @@ create_conflict_markers(svn_skel_t **work_items, const working_node_version_t *old_version, const working_node_version_t *new_version, svn_node_kind_t kind, + svn_boolean_t set_operation, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -689,19 +864,22 @@ create_conflict_markers(svn_skel_t **work_items, = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool); original_version->path_in_repos = repos_relpath; - if (operation == svn_wc_operation_update) - { - SVN_ERR(svn_wc__conflict_skel_set_op_update( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); - } - else + if (set_operation) { - SVN_ERR(svn_wc__conflict_skel_set_op_switch( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); + if (operation == svn_wc_operation_update) + { + SVN_ERR(svn_wc__conflict_skel_set_op_update( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } + else + { + SVN_ERR(svn_wc__conflict_skel_set_op_switch( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } } /* According to this func's doc string, it is "Currently only used for @@ -720,8 +898,8 @@ update_working_props(svn_wc_notify_state_t *prop_state, svn_skel_t **conflict_skel, apr_array_header_t **propchanges, apr_hash_t **actual_props, - svn_wc__db_t *db, - const char *local_abspath, + update_move_baton_t *b, + const char *local_relpath, const struct working_node_version_t *old_version, const struct working_node_version_t *new_version, apr_pool_t *result_pool, @@ -736,139 +914,123 @@ update_working_props(svn_wc_notify_state_t *prop_state, * merge-left version, and the current props of the * moved-here working file as the merge-right version. */ - SVN_ERR(svn_wc__db_read_props(actual_props, - db, local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_props_internal(actual_props, + b->wcroot, local_relpath, + result_pool, scratch_pool)); SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props, result_pool)); SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props, - db, local_abspath, + b->db, svn_dirent_join(b->wcroot->abspath, + local_relpath, + scratch_pool), old_version->props, old_version->props, *actual_props, *propchanges, result_pool, scratch_pool)); - /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props - relies on NODES row having been updated first which we don't do - at present. So this extra property diff has the same effect. + /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal + relies on NODES row being updated via a different route . - ### Perhaps we should update NODES first (but after - ### svn_wc__db_read_props above)? */ + This extra property diff makes sure we clear the actual row when + the final result is unchanged properties. */ SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props, scratch_pool)); if (!new_propchanges->nelts) new_actual_props = NULL; - /* Install the new actual props. Don't set the conflict_skel yet, because - we might need to add a text conflict to it as well. */ - SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, - new_actual_props, - svn_wc__has_magic_property(*propchanges), - NULL/*conflict_skel*/, NULL/*work_items*/, - scratch_pool)); + /* Install the new actual props. */ + SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath, + new_actual_props, + svn_wc__has_magic_property( + *propchanges), + scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_alter_directory(void *baton, +tc_editor_alter_directory(node_move_baton_t *nmb, const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - const apr_array_header_t *children, + apr_hash_t *old_props, apr_hash_t *new_props, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; + update_move_baton_t *b = nmb->umb; working_node_version_t old_version, new_version; - svn_wc__db_status_t status; - svn_boolean_t is_conflicted; - - SVN_ERR_ASSERT(expected_move_dst_revision == b->old_version->peg_rev); - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + svn_skel_t *work_items = NULL; + svn_skel_t *conflict_skel = NULL; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, + scratch_pool); + svn_wc_notify_state_t prop_state; + apr_hash_t *actual_props; + apr_array_header_t *propchanges; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - /* There might be not-present nodes of a different revision as the same - depth as a copy. This is commonly caused by copying/moving mixed revision - directories */ - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_dir); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) return SVN_NO_ERROR; + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_dir) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_dir, svn_node_dir, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + old_version.location_and_kind = b->old_version; new_version.location_and_kind = b->new_version; + old_version.checksum = NULL; /* not a file */ + old_version.props = old_props; new_version.checksum = NULL; /* not a file */ - new_version.props = new_props ? new_props : old_version.props; + new_version.props = new_props; - if (new_props) + SVN_ERR(update_working_props(&prop_state, &conflict_skel, + &propchanges, &actual_props, + b, dst_relpath, + &old_version, &new_version, + scratch_pool, scratch_pool)); + + if (prop_state == svn_wc_notify_state_conflicted) { - const char *dst_abspath = svn_dirent_join(b->wcroot->abspath, - dst_relpath, - scratch_pool); - svn_wc_notify_state_t prop_state; - svn_skel_t *conflict_skel = NULL; - apr_hash_t *actual_props; - apr_array_header_t *propchanges; - - SVN_ERR(update_working_props(&prop_state, &conflict_skel, - &propchanges, &actual_props, - b->db, dst_abspath, - &old_version, &new_version, - scratch_pool, scratch_pool)); - - if (conflict_skel) - { - svn_skel_t *work_items; - - SVN_ERR(create_conflict_markers(&work_items, dst_abspath, - b->db, move_dst_repos_relpath, - conflict_skel, b->operation, - &old_version, &new_version, - svn_node_dir, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_mark_conflict_internal(b->wcroot, dst_relpath, - conflict_skel, - scratch_pool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_items, - scratch_pool)); - } + const char *move_dst_repos_relpath; - SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, - svn_wc_notify_update_update, - svn_node_dir, - svn_wc_notify_state_inapplicable, - prop_state)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_items, local_abspath, + b->db, move_dst_repos_relpath, + conflict_skel, b->operation, + &old_version, &new_version, + svn_node_dir, !obstructed, + scratch_pool, scratch_pool)); } + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, + svn_wc_notify_update_update, + svn_node_dir, + svn_wc_notify_state_inapplicable, + prop_state, + conflict_skel, work_items, scratch_pool)); + return SVN_NO_ERROR; } - -/* Merge the difference between OLD_VERSION and NEW_VERSION into +/* Edit the file found at the move destination, which is initially at + * the old state. Merge the changes into the "working"/"actual" file. + * + * Merge the difference between OLD_VERSION and NEW_VERSION into * the working file at LOCAL_RELPATH. * * The term 'old' refers to the pre-update state, which is the state of @@ -883,17 +1045,18 @@ tc_editor_alter_directory(void *baton, * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL. * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * -update_working_file(const char *local_relpath, - const char *repos_relpath, - svn_wc_operation_t operation, - const working_node_version_t *old_version, - const working_node_version_t *new_version, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - apr_pool_t *scratch_pool) +tc_editor_alter_file(node_move_baton_t *nmb, + const char *dst_relpath, + const svn_checksum_t *old_checksum, + const svn_checksum_t *new_checksum, + apr_hash_t *old_props, + apr_hash_t *new_props, + apr_pool_t *scratch_pool) { - const char *local_abspath = svn_dirent_join(wcroot->abspath, - local_relpath, + update_move_baton_t *b = nmb->umb; + working_node_version_t old_version, new_version; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, + dst_relpath, scratch_pool); const char *old_pristine_abspath; const char *new_pristine_abspath; @@ -903,23 +1066,51 @@ update_working_file(const char *local_relpath, enum svn_wc_merge_outcome_t merge_outcome; svn_wc_notify_state_t prop_state, content_state; svn_skel_t *work_item, *work_items = NULL; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; + + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; + + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_file) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_file, svn_node_file, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + + old_version.location_and_kind = b->old_version; + new_version.location_and_kind = b->new_version; + old_version.checksum = old_checksum; + old_version.props = old_props; + new_version.checksum = new_checksum; + new_version.props = new_props; + + /* ### TODO: Only do this when there is no higher WORKING layer */ SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges, - &actual_props, db, local_abspath, - old_version, new_version, + &actual_props, b, dst_relpath, + &old_version, &new_version, scratch_pool, scratch_pool)); - if (!svn_checksum_match(new_version->checksum, old_version->checksum)) + if (!obstructed + && !svn_checksum_match(new_version.checksum, old_version.checksum)) { svn_boolean_t is_locally_modified; SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, - db, local_abspath, + b->db, local_abspath, FALSE /* exact_comparison */, scratch_pool)); if (!is_locally_modified) { - SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, local_abspath, NULL, FALSE /* FIXME: use_commit_times? */, @@ -939,15 +1130,15 @@ update_working_file(const char *local_relpath, * moved-here working file as the merge-right version. */ SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, - db, wcroot->abspath, - old_version->checksum, + b->db, b->wcroot->abspath, + old_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - db, wcroot->abspath, - new_version->checksum, + b->db, b->wcroot->abspath, + new_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, - &merge_outcome, db, + &merge_outcome, b->db, old_pristine_abspath, new_pristine_abspath, local_abspath, @@ -958,7 +1149,7 @@ update_working_file(const char *local_relpath, NULL, /* diff3-cmd */ NULL, /* merge options */ propchanges, - NULL, NULL, /* cancel_func + baton */ + b->cancel_func, b->cancel_baton, scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); @@ -976,207 +1167,113 @@ update_working_file(const char *local_relpath, * too. */ if (conflict_skel) { - SVN_ERR(create_conflict_markers(&work_item, local_abspath, db, - repos_relpath, conflict_skel, - operation, old_version, new_version, - svn_node_file, - scratch_pool, scratch_pool)); + const char *move_dst_repos_relpath; - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict_skel, - scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db, + move_dst_repos_relpath, conflict_skel, + b->operation, &old_version, &new_version, + svn_node_file, !obstructed, + scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); } - SVN_ERR(svn_wc__db_wq_add(db, wcroot->abspath, work_items, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, svn_wc_notify_update_update, svn_node_file, content_state, - prop_state)); - - return SVN_NO_ERROR; -} - - -/* Edit the file found at the move destination, which is initially at - * the old state. Merge the changes into the "working"/"actual" file. - */ -static svn_error_t * -tc_editor_alter_file(void *baton, - const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - apr_hash_t *new_props, - const svn_checksum_t *new_checksum, - svn_stream_t *new_contents, - apr_pool_t *scratch_pool) -{ - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; - working_node_version_t old_version, new_version; - svn_boolean_t is_conflicted; - svn_wc__db_status_t status; - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); - - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_file); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; - - old_version.location_and_kind = b->old_version; - new_version.location_and_kind = b->new_version; - - /* If new checksum is null that means no change; similarly props. */ - new_version.checksum = new_checksum ? new_checksum : old_version.checksum; - new_version.props = new_props ? new_props : old_version.props; - - /* Update file and prop contents if the update has changed them. */ - if (!svn_checksum_match(new_checksum, old_version.checksum) || new_props) - { - SVN_ERR(update_working_file(dst_relpath, move_dst_repos_relpath, - b->operation, &old_version, &new_version, - b->wcroot, b->db, - scratch_pool)); - } + prop_state, + conflict_skel, work_items, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_alter_symlink(void *baton, - const char *relpath, - svn_revnum_t revision, - apr_hash_t *props, - const char *target, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_delete(void *baton, +tc_editor_delete(node_move_baton_t *nmb, const char *relpath, - svn_revnum_t revision, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; + update_move_baton_t *b = nmb->umb; svn_sqlite__stmt_t *stmt; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - svn_boolean_t must_delete_working_nodes = FALSE; - const char *local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, - scratch_pool); - const char *parent_relpath = svn_relpath_dirname(relpath, scratch_pool); - int op_depth_below; - svn_boolean_t have_row; + const char *local_abspath; + svn_boolean_t is_modified, is_all_deletes; + svn_skel_t *work_items = NULL; + svn_skel_t *conflict = NULL; - SVN_ERR(svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; /* Check before retracting delete to catch delete-delete conflicts. This catches conflicts on the node itself; deleted children are caught as local modifications below.*/ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - move_dst_kind, - svn_node_unknown, - move_dst_repos_relpath, - svn_wc_conflict_action_delete, - scratch_pool)); + if (nmb->shadowed) + { + SVN_ERR(mark_tc_on_op_root(nmb, + old_kind, new_kind, + svn_wc_conflict_action_delete, + scratch_pool)); + return SVN_NO_ERROR; + } - if (!is_conflicted) + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, + nmb->umb->db, local_abspath, FALSE, + NULL, NULL, scratch_pool)); + if (is_modified) { - svn_boolean_t is_modified, is_all_deletes; + svn_wc_conflict_reason_t reason; - SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, b->db, - local_abspath, - NULL, NULL, scratch_pool)); - if (is_modified) - { - svn_wc_conflict_reason_t reason; + /* No conflict means no NODES rows at the relpath op-depth + so it's easy to convert the modified tree into a copy. - if (!is_all_deletes) - { - /* No conflict means no NODES rows at the relpath op-depth - so it's easy to convert the modified tree into a copy. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, relpath_depth(relpath))); - SVN_ERR(svn_sqlite__step_done(stmt)); - - reason = svn_wc_conflict_reason_edited; - } - else - { + Note the following assumptions for relpath: + * it is not shadowed + * it is not the/an op-root. (or we can't make us a copy) + */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH_ABOVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE, + NULL, NULL, scratch_pool)); - reason = svn_wc_conflict_reason_deleted; - must_delete_working_nodes = TRUE; - } - is_conflicted = TRUE; - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, - move_dst_kind, - svn_node_none, - move_dst_repos_relpath, reason, - svn_wc_conflict_action_delete, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - } - } + reason = svn_wc_conflict_reason_edited; - if (!is_conflicted || must_delete_working_nodes) + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, new_kind, reason, + (new_kind == svn_node_none) + ? svn_wc_conflict_action_delete + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else { apr_pool_t *iterpool = svn_pool_create(scratch_pool); - svn_skel_t *work_item; - svn_node_kind_t del_kind; const char *del_abspath; + svn_boolean_t have_row; + /* Get all descendants of the node in reverse order (so children are + handled before their parents, but not strictly depth first) */ SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_CHILDREN_OP_DEPTH)); + STMT_SELECT_DESCENDANTS_OP_DEPTH_RV)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); + b->dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { svn_error_t *err; + svn_skel_t *work_item; + svn_node_kind_t del_kind; svn_pool_clear(iterpool); @@ -1194,8 +1291,7 @@ tc_editor_delete(void *baton, b->wcroot->abspath, del_abspath, iterpool, iterpool); if (!err) - err = svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool); + err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); @@ -1203,139 +1299,34 @@ tc_editor_delete(void *baton, } SVN_ERR(svn_sqlite__reset(stmt)); - SVN_ERR(svn_wc__db_depth_get_info(NULL, &del_kind, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, - b->wcroot, relpath, op_depth, - iterpool, iterpool)); - if (del_kind == svn_node_dir) - SVN_ERR(svn_wc__wq_build_dir_remove(&work_item, b->db, + if (old_kind == svn_node_dir) + SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, FALSE /* recursive */, - iterpool, iterpool)); + scratch_pool, iterpool)); else - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db, + SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, - iterpool, iterpool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool)); - - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_delete, - del_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - svn_pool_destroy(iterpool); - } - - /* Deleting the ROWS is valid so long as we update the parent before - committing the transaction. The removed rows could have been - replacing a lower layer in which case we need to add base-deleted - rows. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, parent_relpath, - op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - op_depth_below = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (have_row) - { - /* Remove non-shadowing nodes. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_NO_LOWER_LAYER)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, op_depth_below)); - SVN_ERR(svn_sqlite__step_done(stmt)); + scratch_pool, iterpool)); - /* Convert remaining shadowing nodes to presence='base-deleted'. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_REPLACE_WITH_BASE_DELETED)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - else - { - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + svn_pool_destroy(iterpool); } - /* Retract any base-delete. */ - SVN_ERR(svn_wc__db_retract_parent_delete(b->wcroot, relpath, op_depth, - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -tc_editor_copy(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) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_move(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) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_rotate(void *baton, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_complete(void *baton, - apr_pool_t *scratch_pool) -{ - return SVN_NO_ERROR; -} + /* Only notify if add_file/add_dir is not going to notify */ + if (conflict || (new_kind == svn_node_none)) + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + svn_wc_notify_update_delete, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_items, scratch_pool)); + else if (work_items) + SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items, + scratch_pool)); -static svn_error_t * -tc_editor_abort(void *baton, - apr_pool_t *scratch_pool) -{ return SVN_NO_ERROR; } -/* The editor callback table implementing the receiver. */ -static const svn_editor_cb_many_t editor_ops = { - tc_editor_add_directory, - tc_editor_add_file, - tc_editor_add_symlink, - tc_editor_add_absent, - tc_editor_alter_directory, - tc_editor_alter_file, - tc_editor_alter_symlink, - tc_editor_delete, - tc_editor_copy, - tc_editor_move, - tc_editor_rotate, - tc_editor_complete, - tc_editor_abort -}; - - /* * Driver code. * @@ -1352,73 +1343,11 @@ static const svn_editor_cb_many_t editor_ops = { * single-revision. */ -/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION - * to reflect the tree conflict on the victim SRC_ABSPATH in DB. - * - * If SRC_ABSPATH is not a tree-conflict victim, return an error. - */ -static svn_error_t * -get_tc_info(svn_wc_operation_t *operation, - svn_wc_conflict_reason_t *local_change, - svn_wc_conflict_action_t *incoming_change, - const char **move_src_op_root_abspath, - svn_wc_conflict_version_t **old_version, - svn_wc_conflict_version_t **new_version, - svn_wc__db_t *db, - const char *src_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const apr_array_header_t *locations; - svn_boolean_t tree_conflicted; - svn_skel_t *conflict_skel; - - /* Check for tree conflict on src. */ - SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, - src_abspath, - scratch_pool, scratch_pool)); - if (!conflict_skel) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not in conflict"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - - SVN_ERR(svn_wc__conflict_read_info(operation, &locations, - NULL, NULL, &tree_conflicted, - db, src_abspath, - conflict_skel, result_pool, - scratch_pool)); - if ((*operation != svn_wc_operation_update - && *operation != svn_wc_operation_switch) - || !tree_conflicted) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not a tree-conflict victim"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - if (locations) - { - SVN_ERR_ASSERT(locations->nelts >= 2); - *old_version = APR_ARRAY_IDX(locations, 0, - svn_wc_conflict_version_t *); - *new_version = APR_ARRAY_IDX(locations, 1, - svn_wc_conflict_version_t *); - } - - SVN_ERR(svn_wc__conflict_read_tree_conflict(local_change, - incoming_change, - move_src_op_root_abspath, - db, src_abspath, - conflict_skel, scratch_pool, - scratch_pool)); - - return SVN_NO_ERROR; -} - /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at OP_DEPTH provided the row exists. Return *KIND of svn_node_none if - the row does not exist. *CHILDREN is a sorted array of basenames of - type 'const char *', rather than a hash, to allow the driver to - process children in a defined order. */ + the row does not exist, or only describes a delete of a lower op-depth. + *CHILDREN is a sorted array of basenames of type 'const char *', rather + than a hash, to allow the driver to process children in a defined order. */ static svn_error_t * get_info(apr_hash_t **props, const svn_checksum_t **checksum, @@ -1430,64 +1359,68 @@ get_info(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *hash_children; - apr_array_header_t *sorted_children; + svn_wc__db_status_t status; + const char *repos_relpath; + svn_node_kind_t db_kind; svn_error_t *err; - int i; - err = svn_wc__db_depth_get_info(NULL, kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, checksum, NULL, NULL, props, + err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL, + NULL, NULL, NULL, NULL, checksum, NULL, + NULL, props, wcroot, local_relpath, op_depth, result_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + + /* If there is no node at this depth, or only a node that describes a delete + of a lower layer we report this node as not existing. */ + if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + || (!err && status != svn_wc__db_status_added + && status != svn_wc__db_status_normal)) { svn_error_clear(err); - *kind = svn_node_none; + + if (kind) + *kind = svn_node_none; + if (checksum) + *checksum = NULL; + if (props) + *props = NULL; + if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; } else SVN_ERR(err); + if (kind) + *kind = db_kind; - SVN_ERR(svn_wc__db_get_children_op_depth(&hash_children, wcroot, - local_relpath, op_depth, - scratch_pool, scratch_pool)); - - sorted_children = svn_sort__hash(hash_children, - svn_sort_compare_items_lexically, - scratch_pool); - - *children = apr_array_make(result_pool, sorted_children->nelts, - sizeof(const char *)); - for (i = 0; i < sorted_children->nelts; ++i) - APR_ARRAY_PUSH(*children, const char *) - = apr_pstrdup(result_pool, APR_ARRAY_IDX(sorted_children, i, - svn_sort__item_t).key); - - return SVN_NO_ERROR; -} - -/* Return TRUE if SRC_CHILDREN and DST_CHILDREN represent the same - children, FALSE otherwise. SRC_CHILDREN and DST_CHILDREN are - sorted arrays of basenames of type 'const char *'. */ -static svn_boolean_t -children_match(apr_array_header_t *src_children, - apr_array_header_t *dst_children) { int i; + if (children && db_kind == svn_node_dir) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; - if (src_children->nelts != dst_children->nelts) - return FALSE; + *children = apr_array_make(result_pool, 16, sizeof(const char *)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS)); + 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); - for(i = 0; i < src_children->nelts; ++i) - { - const char *src_child = - APR_ARRAY_IDX(src_children, i, const char *); - const char *dst_child = - APR_ARRAY_IDX(dst_children, i, const char *); + APR_ARRAY_PUSH(*children, const char *) + = svn_relpath_basename(child_relpath, result_pool); - if (strcmp(src_child, dst_child)) - return FALSE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); } + else if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); - return TRUE; + return SVN_NO_ERROR; } /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties, @@ -1516,97 +1449,77 @@ props_match(svn_boolean_t *match, /* ### Drive TC_EDITOR so as to ... */ static svn_error_t * -update_moved_away_node(svn_editor_t *tc_editor, +update_moved_away_node(node_move_baton_t *nmb, + svn_wc__db_wcroot_t *wcroot, const char *src_relpath, const char *dst_relpath, - int src_op_depth, - const char *move_root_dst_relpath, - svn_revnum_t move_root_dst_revision, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { + update_move_baton_t *b = nmb->umb; svn_node_kind_t src_kind, dst_kind; const svn_checksum_t *src_checksum, *dst_checksum; apr_hash_t *src_props, *dst_props; apr_array_header_t *src_children, *dst_children; - int dst_op_depth = relpath_depth(move_root_dst_relpath); + + if (b->cancel_func) + SVN_ERR(b->cancel_func(b->cancel_baton)); SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind, - src_relpath, src_op_depth, + src_relpath, b->src_op_depth, wcroot, scratch_pool, scratch_pool)); SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind, - dst_relpath, dst_op_depth, + dst_relpath, b->dst_op_depth, wcroot, scratch_pool, scratch_pool)); if (src_kind == svn_node_none || (dst_kind != svn_node_none && src_kind != dst_kind)) { - SVN_ERR(svn_editor_delete(tc_editor, dst_relpath, - move_root_dst_revision)); + SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind, + scratch_pool)); } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind != svn_node_none && src_kind != dst_kind) { if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_add_file(tc_editor, dst_relpath, - src_checksum, contents, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind, + src_checksum, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - SVN_ERR(svn_editor_add_directory(tc_editor, dst_relpath, - src_children, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind, + src_props, scratch_pool)); } } else if (src_kind != svn_node_none) { - svn_boolean_t match; - apr_hash_t *props; - - SVN_ERR(props_match(&match, src_props, dst_props, scratch_pool)); - props = match ? NULL: src_props; + svn_boolean_t props_equal; + SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool)); if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - if (svn_checksum_match(src_checksum, dst_checksum)) - src_checksum = NULL; - - if (src_checksum) - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - else - contents = NULL; - - if (props || src_checksum) - SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath, - move_root_dst_revision, - props, src_checksum, contents)); + if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum)) + SVN_ERR(tc_editor_alter_file(nmb, dst_relpath, + dst_checksum, src_checksum, + dst_props, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - apr_array_header_t *children - = children_match(src_children, dst_children) ? NULL : src_children; - - if (props || children) - SVN_ERR(svn_editor_alter_directory(tc_editor, dst_relpath, - move_root_dst_revision, - children, props)); + if (!props_equal) + SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath, + dst_props, src_props, + scratch_pool)); } } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind == svn_node_dir) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -1615,8 +1528,12 @@ update_moved_away_node(svn_editor_t *tc_editor, while (i < src_children->nelts || j < dst_children->nelts) { const char *child_name; - const char *src_child_relpath, *dst_child_relpath; svn_boolean_t src_only = FALSE, dst_only = FALSE; + node_move_baton_t cnmb = { 0 }; + + cnmb.pb = nmb; + cnmb.umb = nmb->umb; + cnmb.shadowed = nmb->shadowed; svn_pool_clear(iterpool); if (i >= src_children->nelts) @@ -1645,193 +1562,85 @@ update_moved_away_node(svn_editor_t *tc_editor, child_name = dst_only ? dst_name : src_name; } - src_child_relpath = svn_relpath_join(src_relpath, child_name, - iterpool); - dst_child_relpath = svn_relpath_join(dst_relpath, child_name, - iterpool); + cnmb.src_relpath = svn_relpath_join(src_relpath, child_name, + iterpool); + cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name, + iterpool); - SVN_ERR(update_moved_away_node(tc_editor, src_child_relpath, - dst_child_relpath, src_op_depth, - move_root_dst_relpath, - move_root_dst_revision, - db, wcroot, scratch_pool)); + if (!cnmb.shadowed) + SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot, + cnmb.dst_relpath, b->dst_op_depth, + iterpool)); + + SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath, + cnmb.dst_relpath, iterpool)); if (!dst_only) ++i; if (!src_only) ++j; + + if (nmb->skip) /* Does parent now want a skip? */ + break; } } return SVN_NO_ERROR; } -/* Update the single op-depth layer in the move destination subtree - rooted at DST_RELPATH to make it match the move source subtree - rooted at SRC_RELPATH. */ static svn_error_t * -replace_moved_layer(const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc__db_wcroot_t *wcroot, - apr_pool_t *scratch_pool) +suitable_for_move(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 dst_op_depth = relpath_depth(dst_relpath); + svn_revnum_t revision; + const char *repos_relpath; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); - /* Replace entire subtree at one op-depth. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOCAL_RELPATH_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - src_relpath, src_op_depth)); + 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) + return svn_error_trace(svn_sqlite__reset(stmt)); + + revision = svn_sqlite__column_revnum(stmt, 4); + repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + + SVN_ERR(svn_sqlite__reset(stmt)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_REPOS_PATH_REVISION)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { - svn_error_t *err; - svn_sqlite__stmt_t *stmt2; - const char *src_cp_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *dst_cp_relpath - = svn_relpath_join(dst_relpath, - svn_relpath_skip_ancestor(src_relpath, - src_cp_relpath), - scratch_pool); - - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_COPY_NODE_MOVE); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, - src_cp_relpath, src_op_depth, - dst_cp_relpath, dst_op_depth, - svn_relpath_dirname(dst_cp_relpath, - scratch_pool)); - if (!err) - err = svn_sqlite__step_done(stmt2); - if (err) - return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2); + const char *relpath = svn_sqlite__column_text(stmt, 0, NULL); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - SVN_ERR(svn_sqlite__reset(stmt)); + svn_pool_clear(iterpool); - return SVN_NO_ERROR; -} - -/* Transfer changes from the move source to the move destination. - * - * Drive the editor TC_EDITOR with the difference between DST_RELPATH - * (at its own op-depth) and SRC_RELPATH (at op-depth zero). - * - * Then update the single op-depth layer in the move destination subtree - * rooted at DST_RELPATH to make it match the move source subtree - * rooted at SRC_RELPATH. - * - * ### And the other params? - */ -static svn_error_t * -drive_tree_conflict_editor(svn_editor_t *tc_editor, - const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - /* - * Refuse to auto-resolve unsupported tree conflicts. - */ - /* ### Only handle conflicts created by update/switch operations for now. */ - if (operation != svn_wc_operation_update && - operation != svn_wc_operation_switch) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot auto-resolve tree-conflict on '%s'"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, - src_relpath, scratch_pool), - scratch_pool)); - - /* We walk the move source (i.e. the post-update tree), comparing each node - * with the equivalent node at the move destination and applying the update - * to nodes at the move destination. */ - SVN_ERR(update_moved_away_node(tc_editor, src_relpath, dst_relpath, - src_op_depth, - dst_relpath, old_version->peg_rev, - db, wcroot, scratch_pool)); - - SVN_ERR(replace_moved_layer(src_relpath, dst_relpath, src_op_depth, - wcroot, scratch_pool)); - - SVN_ERR(svn_editor_complete(tc_editor)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -suitable_for_move(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_revnum_t revision; - const char *repos_relpath; - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - - 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) - { - revision = svn_sqlite__column_revnum(stmt, 4); - repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - } - SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) - return SVN_NO_ERROR; /* Return an error? */ - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_REPOS_PATH_REVISION)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2); - const char *relpath = svn_sqlite__column_text(stmt, 0, NULL); - - svn_pool_clear(iterpool); - - relpath = svn_relpath_skip_ancestor(local_relpath, relpath); - relpath = svn_relpath_join(repos_relpath, relpath, iterpool); + relpath = svn_relpath_skip_ancestor(local_relpath, relpath); + relpath = svn_relpath_join(repos_relpath, relpath, iterpool); if (revision != node_revision) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "%s' is a mixed-revision working copy"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL))) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "'%s' is a switched subtree"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, + local_relpath, + scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -1845,96 +1654,113 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see. */ static svn_error_t * -update_moved_away_conflict_victim(svn_wc__db_t *db, +update_moved_away_conflict_victim(svn_revnum_t *old_rev, + svn_revnum_t *new_rev, + svn_wc__db_t *db, svn_wc__db_wcroot_t *wcroot, - const char *victim_relpath, + const char *local_relpath, + const char *delete_relpath, svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - const char *move_src_op_root_relpath, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - svn_editor_t *tc_editor; - struct tc_editor_baton *tc_editor_baton; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - const char *dummy1, *dummy2, *dummy3; - int src_op_depth; - const char *move_root_dst_abspath; + update_move_baton_t umb = { NULL }; + const char *src_relpath, *dst_relpath; + svn_wc_conflict_version_t old_version; + svn_wc_conflict_version_t new_version; + apr_int64_t repos_id; + node_move_baton_t nmb = { 0 }; - /* ### assumes wc write lock already held */ + SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath)); /* Construct editor baton. */ - tc_editor_baton = apr_pcalloc(scratch_pool, sizeof(*tc_editor_baton)); - SVN_ERR(svn_wc__db_op_depth_moved_to( - &dummy1, &tc_editor_baton->move_root_dst_relpath, &dummy2, &dummy3, - relpath_depth(move_src_op_root_relpath) - 1, - wcroot, victim_relpath, scratch_pool, scratch_pool)); - if (tc_editor_baton->move_root_dst_relpath == NULL) + + SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot, + local_relpath, relpath_depth(delete_relpath), + scratch_pool)); + + SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL, + wcroot, local_relpath, + umb.src_op_depth, + scratch_pool, scratch_pool)); + + if (dst_relpath == NULL) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("The node '%s' has not been moved away"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); - move_root_dst_abspath - = svn_dirent_join(wcroot->abspath, tc_editor_baton->move_root_dst_relpath, - scratch_pool); - SVN_ERR(svn_wc__write_check(db, move_root_dst_abspath, scratch_pool)); + umb.dst_op_depth = relpath_depth(dst_relpath); - tc_editor_baton->operation = operation; - tc_editor_baton->old_version= old_version; - tc_editor_baton->new_version= new_version; - tc_editor_baton->db = db; - tc_editor_baton->wcroot = wcroot; - tc_editor_baton->result_pool = scratch_pool; + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool)); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - move_src_op_root_relpath, - relpath_depth(move_src_op_root_relpath))); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - src_op_depth = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not deleted"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); - if (src_op_depth == 0) - SVN_ERR(suitable_for_move(wcroot, victim_relpath, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind, + &new_version.peg_rev, + &new_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, src_relpath, umb.src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url, + &new_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind, + &old_version.peg_rev, + &old_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, dst_relpath, umb.dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url, + &old_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + *old_rev = old_version.peg_rev; + *new_rev = new_version.peg_rev; + + umb.operation = operation; + umb.old_version= &old_version; + umb.new_version= &new_version; + umb.db = db; + umb.wcroot = wcroot; + umb.cancel_func = cancel_func; + umb.cancel_baton = cancel_baton; + + if (umb.src_op_depth == 0) + SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool)); /* Create a new, and empty, list for notification information. */ SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); - /* Create the editor... */ - SVN_ERR(svn_editor_create(&tc_editor, tc_editor_baton, - cancel_func, cancel_baton, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_setcb_many(tc_editor, &editor_ops, scratch_pool)); - - /* ... and drive it. */ - SVN_ERR(drive_tree_conflict_editor(tc_editor, - victim_relpath, - tc_editor_baton->move_root_dst_relpath, - src_op_depth, - operation, - local_change, incoming_change, - tc_editor_baton->old_version, - tc_editor_baton->new_version, - db, wcroot, - cancel_func, cancel_baton, - scratch_pool)); + + /* Drive the editor... */ + + nmb.umb = &umb; + nmb.src_relpath = src_relpath; + nmb.dst_relpath = dst_relpath; + /* nmb.shadowed = FALSE; */ + /* nmb.edited = FALSE; */ + /* nmb.skip_children = FALSE; */ + + /* We walk the move source (i.e. the post-update tree), comparing each node + * with the equivalent node at the move destination and applying the update + * to nodes at the move destination. */ + SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath, + umb.src_op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); return SVN_NO_ERROR; } @@ -1942,58 +1768,43 @@ update_moved_away_conflict_victim(svn_wc__db_t *db, 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, + const char *local_abspath, + const char *delete_op_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, 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_revnum_t old_rev, new_rev; const char *local_relpath; - svn_wc_operation_t operation; - svn_wc_conflict_reason_t local_change; - svn_wc_conflict_action_t incoming_change; - svn_wc_conflict_version_t *old_version; - svn_wc_conflict_version_t *new_version; - const char *move_src_op_root_abspath, *move_src_op_root_relpath; + const char *delete_relpath; /* ### Check for mixed-rev src or dst? */ - SVN_ERR(get_tc_info(&operation, &local_change, &incoming_change, - &move_src_op_root_abspath, - &old_version, &new_version, - db, victim_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__write_check(db, move_src_op_root_abspath, scratch_pool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, victim_abspath, + db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - move_src_op_root_relpath - = svn_dirent_skip_ancestor(wcroot->abspath, move_src_op_root_abspath); + delete_relpath + = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath); SVN_WC__DB_WITH_TXN( update_moved_away_conflict_victim( - db, wcroot, local_relpath, - operation, local_change, incoming_change, - move_src_op_root_relpath, - old_version, new_version, + &old_rev, &new_rev, + db, wcroot, local_relpath, delete_relpath, + operation, action, reason, cancel_func, cancel_baton, scratch_pool), wcroot); /* Send all queued up notifications. */ - SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev - : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev - : SVN_INVALID_REVNUM), + SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, notify_func, notify_baton, scratch_pool)); if (notify_func) @@ -2008,7 +1819,7 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, notify->kind = svn_node_none; notify->content_state = svn_wc_notify_state_inapplicable; notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = new_version->peg_rev; + notify->revision = new_rev; notify_func(notify_baton, notify, scratch_pool); } @@ -2017,11 +1828,12 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, } /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire - BASE tree at LOCAL_RELPATH, to FALSE otherwise. */ + tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */ static svn_error_t * depth_sufficient_to_bump(svn_boolean_t *can_bump, - const char *local_relpath, svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, svn_depth_t depth, apr_pool_t *scratch_pool) { @@ -2038,21 +1850,21 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, 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, 0)); + local_relpath, op_depth)); break; case svn_depth_files: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_NON_FILE_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; case svn_depth_immediates: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_GRANDCHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; default: SVN_ERR_MALFUNCTION(); @@ -2068,6 +1880,7 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, static svn_error_t * bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, const char *move_src_root_relpath, + int src_op_depth, const char *move_src_op_root_relpath, const char *move_dst_op_root_relpath, svn_wc__db_t *db, @@ -2084,18 +1897,29 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, svn_node_kind_t new_kind; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; + svn_skel_t *conflict; + + /* Verify precondition: We are allowed to set a tree conflict here. */ + SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool)); /* Read new (post-update) information from the new move source BASE node. */ - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev, - &new_repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, move_src_op_root_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev, + &new_repos_relpath, &repos_id, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, move_src_op_root_relpath, + src_op_depth, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - wcroot->sdb, repos_id, scratch_pool)); + wcroot, repos_id, scratch_pool)); + + /* Read old (pre-update) information from the move destination node. - /* Read old (pre-update) information from the move destination node. */ + This potentially touches nodes that aren't locked by us, but that is not + a problem because we have a SQLite write lock here, and all sqlite + operations that affect move stability use a sqlite lock as well. + (And affecting the move itself requires a write lock on the node that + we do own the lock for: the move source) + */ SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev, &old_repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -2103,6 +1927,21 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, relpath_depth(move_dst_op_root_relpath), scratch_pool, scratch_pool)); + if (strcmp(move_src_root_relpath, move_src_op_root_relpath)) + { + /* We have information for the op-root, but need it for the node that + we are putting the tree conflict on. Luckily we know that we have + a clean BASE */ + + const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath, + move_src_root_relpath); + + old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath, + scratch_pool); + new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath, + scratch_pool); + } + old_version = svn_wc_conflict_version_create2( repos_root_url, repos_uuid, old_repos_relpath, old_rev, old_kind, scratch_pool); @@ -2110,38 +1949,202 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, repos_root_url, repos_uuid, new_repos_relpath, new_rev, new_kind, scratch_pool); - SVN_ERR(mark_tree_conflict(move_src_root_relpath, - wcroot, db, old_version, new_version, - move_dst_op_root_relpath, - svn_wc_operation_update, - old_kind, new_kind, - old_repos_relpath, - svn_wc_conflict_reason_moved_away, - svn_wc_conflict_action_edit, - move_src_op_root_relpath, - scratch_pool)); + SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath, + move_dst_op_root_relpath, + db, old_version, new_version, + svn_wc_operation_update, + old_kind, new_kind, + old_repos_relpath, + svn_wc_conflict_reason_moved_away, + svn_wc_conflict_action_edit, + move_src_op_root_relpath, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets + * *SKIP to TRUE if the node should be skipped, otherwise to FALSE. + * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH. + */ +static svn_error_t * +check_bump_layer(svn_boolean_t *skip, + svn_depth_t *src_depth, + const char *bump_root, + svn_depth_t bump_depth, + const char *src_relpath, + svn_node_kind_t src_kind, + apr_pool_t *scratch_pool) +{ + const char *relpath; + + *skip = FALSE; + *src_depth = bump_depth; + + relpath = svn_relpath_skip_ancestor(bump_root, src_relpath); + + if (!relpath) + *skip = TRUE; + + if (bump_depth == svn_depth_infinity) + return SVN_NO_ERROR; + + if (relpath && *relpath == '\0') + return SVN_NO_ERROR; + + switch (bump_depth) + { + case svn_depth_empty: + *skip = TRUE; + break; + + case svn_depth_files: + if (src_kind != svn_node_file) + { + *skip = TRUE; + break; + } + /* Fallthrough */ + case svn_depth_immediates: + if (!relpath || relpath_depth(relpath) > 1) + *skip = TRUE; + + *src_depth = svn_depth_empty; + break; + default: + SVN_ERR_MALFUNCTION(); + } + + return SVN_NO_ERROR; +} + +/* The guts of bump_moved_away: Determines if a move can be bumped to match + * the move origin and if so performs this bump. + */ +static svn_error_t * +bump_moved_layer(svn_boolean_t *recurse, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + const char *src_relpath, + int src_del_depth, + svn_depth_t src_depth, + const char *dst_relpath, + svn_wc__db_t *db, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + svn_skel_t *conflict; + svn_boolean_t can_bump; + const char *src_root_relpath; + + SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool)); + + *recurse = FALSE; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_HAS_LAYER_BETWEEN)); + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, + op_depth, src_del_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (have_row) + return SVN_NO_ERROR; + + if (op_depth == 0) + SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath, + op_depth, src_depth, scratch_pool)); + else + /* Having chosen to bump an entire BASE tree move we + always have sufficient depth to bump subtree moves. */ + can_bump = TRUE; + + /* Are we allowed to bump */ + if (can_bump) + { + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, + dst_relpath, + FALSE, scratch_pool)); + + if (!locked) + can_bump = FALSE; + } + + src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth, + scratch_pool); + + if (!can_bump) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth, + src_root_relpath, dst_relpath, + db, scratch_pool)); + + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, src_root_relpath, + scratch_pool, scratch_pool)); + + /* ### TODO: check this is the right sort of tree-conflict? */ + if (!conflict) + { + /* ### TODO: verify moved_here? */ + + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, + src_relpath, op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); + + *recurse = TRUE; + } return SVN_NO_ERROR; } -/* Bump LOCAL_RELPATH, and all the children of LOCAL_RELPATH, that are - moved-to at op-depth greater than OP_DEPTH. SRC_DONE is a hash - with keys that are 'const char *' relpaths that have already been - bumped. Any bumped paths are added to SRC_DONE. */ +/* Internal storage for bump_moved_away() */ +struct bump_pair_t +{ + const char *src_relpath; + const char *dst_relpath; + int src_del_op_depth; + svn_node_kind_t src_kind; +}; + +/* Bump moves of LOCAL_RELPATH and all its descendants that were + originally below LOCAL_RELPATH at op-depth OP_DEPTH. + */ static svn_error_t * bump_moved_away(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int op_depth, - apr_hash_t *src_done, svn_depth_t depth, 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_pool_t *iterpool; + int i; + apr_array_header_t *pairs = apr_array_make(scratch_pool, 32, + sizeof(struct bump_pair_t*)); + /* Build an array, as we can't execute the same Sqlite query recursively */ iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -2151,126 +2154,48 @@ bump_moved_away(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - svn_sqlite__stmt_t *stmt2; - const char *src_relpath, *dst_relpath; - int src_op_depth = svn_sqlite__column_int(stmt, 2); - svn_error_t *err; - svn_skel_t *conflict; - svn_depth_t src_depth = depth; + struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp)); - svn_pool_clear(iterpool); - - src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2); + bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (depth != svn_depth_infinity) - { - svn_boolean_t skip_this_src = FALSE; - svn_node_kind_t src_kind; + APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp; - if (strcmp(src_relpath, local_relpath)) - { - switch (depth) - { - case svn_depth_empty: - skip_this_src = TRUE; - break; - case svn_depth_files: - src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (src_kind != svn_node_file) - { - skip_this_src = TRUE; - break; - } - /* Fallthrough */ - case svn_depth_immediates: - if (strcmp(svn_relpath_dirname(src_relpath, scratch_pool), - local_relpath)) - skip_this_src = TRUE; - src_depth = svn_depth_empty; - break; - default: - SVN_ERR_MALFUNCTION(); - } - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } - if (skip_this_src) - { - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - } + SVN_ERR(svn_sqlite__reset(stmt)); - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_HAS_LAYER_BETWEEN); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdd", wcroot->wc_id, local_relpath, - op_depth, src_op_depth); - if (!err) - err = svn_sqlite__step(&have_row, stmt2); - if (!err) - err = svn_sqlite__reset(stmt2); - if (!err && !have_row) - { - svn_boolean_t can_bump; - const char *src_root_relpath = src_relpath; + for (i = 0; i < pairs->nelts; i++) + { + struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *); + svn_boolean_t skip; + svn_depth_t src_wc_depth; - if (op_depth == 0) - err = depth_sufficient_to_bump(&can_bump, src_relpath, wcroot, - src_depth, scratch_pool); - else - /* Having chosen to bump an entire BASE tree move we - always have sufficient depth to bump subtree moves. */ - can_bump = TRUE; + svn_pool_clear(iterpool); - if (!err) - { - if (!can_bump) - { - err = bump_mark_tree_conflict(wcroot, src_relpath, - src_root_relpath, dst_relpath, - db, scratch_pool); - if (err) - return svn_error_compose_create(err, - svn_sqlite__reset(stmt)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - while (relpath_depth(src_root_relpath) > src_op_depth) - src_root_relpath = svn_relpath_dirname(src_root_relpath, - iterpool); + SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth, + bp->src_relpath, bp->src_kind, iterpool)); - if (!svn_hash_gets(src_done, src_relpath)) - { - svn_hash_sets(src_done, - apr_pstrdup(result_pool, src_relpath), ""); - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, - src_root_relpath, - iterpool, iterpool); - /* ### TODO: check this is the right sort of tree-conflict? */ - if (!err && !conflict) - { - /* ### TODO: verify moved_here? */ - err = replace_moved_layer(src_relpath, dst_relpath, - op_depth, wcroot, iterpool); - - if (!err) - err = bump_moved_away(wcroot, dst_relpath, - relpath_depth(dst_relpath), - src_done, depth, db, - result_pool, iterpool); - } - } - } + if (!skip) + { + svn_boolean_t recurse; + + SVN_ERR(bump_moved_layer(&recurse, wcroot, + local_relpath, op_depth, + bp->src_relpath, bp->src_del_op_depth, + src_wc_depth, bp->dst_relpath, + db, iterpool)); + + if (recurse) + SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath, + relpath_depth(bp->dst_relpath), + depth, db, 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_pool_destroy(iterpool); @@ -2284,84 +2209,216 @@ svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, svn_wc__db_t *db, apr_pool_t *scratch_pool) { - apr_hash_t *src_done; - SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); if (local_relpath[0] != '\0') { - const char *dummy1, *move_dst_op_root_relpath; - const char *move_src_root_relpath, *move_src_op_root_relpath; + const char *move_dst_op_root_relpath; + const char *move_src_root_relpath, *delete_relpath; + svn_error_t *err; /* Is the root of the update moved away? (Impossible for the wcroot) */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &move_src_root_relpath, - &move_src_op_root_relpath, 0, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - if (move_src_root_relpath) + err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath, + &move_dst_op_root_relpath, + &delete_relpath, + wcroot, local_relpath, + 0 /* BASE */, + 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 (move_src_root_relpath) { if (strcmp(move_src_root_relpath, local_relpath)) { - SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath, - move_src_op_root_relpath, - move_dst_op_root_relpath, - db, scratch_pool)); + /* An ancestor of the path that was updated is moved away. + + If we have a lock on that ancestor, we can mark a tree + conflict on it, if we don't we ignore this case. A future + update of the ancestor will handle this. */ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal( + &locked, wcroot, + move_src_root_relpath, + FALSE, scratch_pool)); + + if (locked) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, + move_src_root_relpath, 0, + delete_relpath, + move_dst_op_root_relpath, + db, scratch_pool)); + } return SVN_NO_ERROR; } } } - src_done = apr_hash_make(scratch_pool); - SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, src_done, depth, db, - scratch_pool, scratch_pool)); + SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool)); return SVN_NO_ERROR; } +/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION + * to reflect the tree conflict on the victim SRC_ABSPATH in DB. + * + * If SRC_ABSPATH is not a tree-conflict victim, return an error. + */ static svn_error_t * -resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_wc_operation_t operation, - svn_wc_conflict_action_t action, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - apr_pool_t *scratch_pool) +fetch_conflict_details(int *src_op_depth, + svn_wc_operation_t *operation, + svn_wc_conflict_action_t *action, + svn_wc_conflict_version_t **left_version, + svn_wc_conflict_version_t **right_version, + svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + const svn_skel_t *conflict_skel, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const apr_array_header_t *locations; + svn_boolean_t text_conflicted; + svn_boolean_t prop_conflicted; + svn_boolean_t tree_conflicted; + const char *move_src_op_root_abspath; + svn_wc_conflict_reason_t reason; + const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, + scratch_pool); + + if (!conflict_skel) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not in conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_info(operation, &locations, + &text_conflicted, &prop_conflicted, + &tree_conflicted, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (text_conflicted || prop_conflicted || !tree_conflicted) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not a valid tree-conflict victim"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, + action, + &move_src_op_root_abspath, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (reason == svn_wc_conflict_reason_moved_away) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is already a moved away tree-conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + if (left_version) + { + if (locations && locations->nelts > 0) + *left_version = APR_ARRAY_IDX(locations, 0, + svn_wc_conflict_version_t *); + else + *left_version = NULL; + } + + if (right_version) + { + if (locations && locations->nelts > 1) + *right_version = APR_ARRAY_IDX(locations, 1, + svn_wc_conflict_version_t *); + else + *right_version = NULL; + } + + { + int del_depth = relpath_depth(local_relpath); + + if (move_src_op_root_abspath) + del_depth = relpath_depth( + svn_dirent_skip_ancestor(wcroot->abspath, + move_src_op_root_abspath)); + + SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_raise_moved_away_internal( + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int src_op_depth, + svn_wc__db_t *db, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int op_depth = relpath_depth(local_relpath); apr_pool_t *iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_MOVED_PAIR)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - const char *moved_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *move_root_dst_relpath = svn_sqlite__column_text(stmt, 1, - NULL); - const char *moved_dst_repos_relpath = svn_sqlite__column_text(stmt, 2, - NULL); + svn_error_t *err; + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); + svn_skel_t *conflict; svn_pool_clear(iterpool); - SVN_ERR(mark_tree_conflict(moved_relpath, - wcroot, db, old_version, new_version, - move_root_dst_relpath, operation, - svn_node_dir /* ### ? */, - svn_node_dir /* ### ? */, - moved_dst_repos_relpath, + SVN_ERR_ASSERT(src_repos_relpath != NULL); + + err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath, + db, old_version, new_version, operation, + src_kind /* ### old kind */, + src_kind /* ### new kind */, + src_repos_relpath, svn_wc_conflict_reason_moved_away, - action, local_relpath, - iterpool)); + action, + svn_relpath_prefix(src_relpath, + delete_op_depth, + iterpool), + iterpool, iterpool); + + if (!err) + err = update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_tree_conflict, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -2373,41 +2430,52 @@ resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, } 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) +svn_wc__db_op_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) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; svn_wc_operation_t operation; - svn_wc_conflict_reason_t reason; svn_wc_conflict_action_t action; - svn_wc_conflict_version_t *old_version, *new_version; + svn_wc_conflict_version_t *left_version, *right_version; + int move_src_op_depth; + svn_skel_t *conflict; 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(get_tc_info(&operation, &reason, &action, NULL, - &old_version, &new_version, - db, local_abspath, scratch_pool, scratch_pool)); - - SVN_WC__DB_WITH_TXN( - resolve_delete_raise_moved_away(wcroot, local_relpath, - db, operation, action, - old_version, new_version, - scratch_pool), + SVN_WC__DB_WITH_TXN4( + svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool), + fetch_conflict_details(&move_src_op_depth, + &operation, &action, + &left_version, &right_version, + wcroot, db, local_relpath, conflict, + scratch_pool, scratch_pool), + svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool), + svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath, + move_src_op_depth, + db, operation, action, + left_version, right_version, + scratch_pool), wcroot); + /* These version numbers are valid for update/switch notifications + only! */ SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev + (left_version + ? left_version->peg_rev : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev + (right_version + ? right_version->peg_rev : SVN_INVALID_REVNUM), notify_func, notify_baton, scratch_pool)); @@ -2416,159 +2484,109 @@ svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, } static svn_error_t * -break_move(svn_wc__db_wcroot_t *wcroot, - const char *src_relpath, - int src_op_depth, - const char *dst_relpath, - int dst_op_depth, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *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, src_relpath, - src_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - /* This statement clears moved_here. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, - dst_relpath, dst_op_depth, dst_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - return SVN_NO_ERROR; -} - -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) -{ - const char *dummy1, *move_dst_op_root_relpath; - const char *dummy2, *move_src_op_root_relpath; - - /* We want to include the passed op-depth, but the function does a > check */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &dummy2, - &move_src_op_root_relpath, - op_depth - 1, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR_ASSERT(move_src_op_root_relpath != NULL - && move_dst_op_root_relpath != NULL); - - SVN_ERR(break_move(wcroot, local_relpath, - relpath_depth(move_src_op_root_relpath), - move_dst_op_root_relpath, - relpath_depth(move_dst_op_root_relpath), - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool) +break_moved_away(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + int parent_src_op_depth, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; apr_pool_t *iterpool; + svn_error_t *err = NULL; SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_PAIR2)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + parent_src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); iterpool = svn_pool_create(scratch_pool); while (have_row) { - const char *src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - const char *dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); - int src_op_depth = svn_sqlite__column_int(stmt, 2); + int src_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); svn_pool_clear(iterpool); - SVN_ERR(break_move(wcroot, src_relpath, src_op_depth, dst_relpath, - relpath_depth(dst_relpath), iterpool)); - SVN_ERR(update_move_list_add(wcroot, src_relpath, - svn_wc_notify_move_broken, - svn_node_unknown, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - svn_pool_destroy(iterpool); + err = verify_write_lock(wcroot, src_relpath, iterpool); - SVN_ERR(svn_sqlite__reset(stmt)); + if (!err) + err = verify_write_lock(wcroot, dst_relpath, iterpool); - return SVN_NO_ERROR; -} + if (err) + break; -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) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; + err = svn_error_trace( + svn_wc__db_op_break_move_internal(wcroot, + src_relpath, src_op_depth, + dst_relpath, NULL, iterpool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, local_abspath, - scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); + if (err) + break; - SVN_WC__DB_WITH_TXN( - svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath, - relpath_depth(local_relpath), - scratch_pool), - wcroot); + err = svn_error_trace( + update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_move_broken, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + NULL, NULL, scratch_pool)); - if (notify_func) - { - svn_wc_notify_t *notify; + if (err) + break; - notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, - local_relpath, - scratch_pool), - svn_wc_notify_move_broken, - scratch_pool); - notify->kind = svn_node_unknown; - notify->content_state = svn_wc_notify_state_inapplicable; - notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = SVN_INVALID_REVNUM; - notify_func(notify_baton, notify, scratch_pool); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); } + svn_pool_destroy(iterpool); - return SVN_NO_ERROR; + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } 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) +svn_wc__db_op_break_moved_away(svn_wc__db_t *db, + const char *local_abspath, + const char *del_op_root_abspath, + svn_boolean_t mark_tc_resolved, + 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; + const char *del_relpath; + int src_op_depth; 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( - break_moved_away_children_internal(wcroot, local_relpath, scratch_pool), + if (del_op_root_abspath) + del_relpath = svn_dirent_skip_ancestor(wcroot->abspath, + del_op_root_abspath); + else + del_relpath = NULL; + + + SVN_WC__DB_WITH_TXN4( + find_src_op_depth(&src_op_depth, wcroot, local_relpath, + del_relpath ? relpath_depth(del_relpath) + : relpath_depth(local_relpath), + scratch_pool), + break_moved_away(wcroot, db, local_relpath, src_op_depth, + scratch_pool), + mark_tc_resolved + ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool) + : SVN_NO_ERROR, + SVN_NO_ERROR, wcroot); SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db_util.c b/contrib/subversion/subversion/libsvn_wc/wc_db_util.c index a6616e470..074feffcf 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db_util.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db_util.c @@ -83,7 +83,7 @@ static svn_error_t * relpath_depth_sqlite(svn_sqlite__context_t *sctx, int argc, svn_sqlite__value_t *values[], - apr_pool_t *scratch_pool) + void *baton) { const char *path = NULL; apr_int64_t depth; @@ -115,6 +115,7 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, const char *sdb_fname, svn_sqlite__mode_t smode, svn_boolean_t exclusive, + apr_int32_t timeout, const char *const *my_statements, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -139,74 +140,15 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, SVN_ERR(svn_sqlite__open(sdb, sdb_abspath, smode, my_statements ? my_statements : statements, - 0, NULL, result_pool, scratch_pool)); + 0, NULL, timeout, result_pool, scratch_pool)); if (exclusive) SVN_ERR(svn_sqlite__exec_statements(*sdb, STMT_PRAGMA_LOCKING_MODE)); SVN_ERR(svn_sqlite__create_scalar_function(*sdb, "relpath_depth", 1, + TRUE /* deterministic */, relpath_depth_sqlite, NULL)); return SVN_NO_ERROR; } - -/* Some helpful transaction helpers. - - Instead of directly using SQLite transactions, these wrappers - relieve the consumer from the need to wrap the wcroot and - local_relpath, which are almost always used within the transaction. - - This also means if we later want to implement some wc_db-specific txn - handling, we have a convenient place to do it. - */ - -/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */ -typedef svn_error_t *(*db_txn_callback_t)(void *baton, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool); - -/* Baton for use with run_txn() and with_db_txn(). */ -struct txn_baton_t -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - - db_txn_callback_t cb_func; - void *cb_baton; -}; - - -/* Unwrap the sqlite transaction into a wc_db txn. - Implements svn_sqlite__transaction_callback_t. */ -static svn_error_t * -run_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) -{ - struct txn_baton_t *tb = baton; - - return svn_error_trace( - tb->cb_func(tb->cb_baton, tb->wcroot, tb->local_relpath, 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) -{ - struct txn_baton_t tb; - - tb.wcroot = wcroot; - tb.local_relpath = local_relpath; - tb.cb_func = cb_func; - tb.cb_baton = cb_baton; - - return svn_error_trace( - svn_sqlite__with_lock(wcroot->sdb, run_txn, &tb, scratch_pool)); -} diff --git a/contrib/subversion/subversion/libsvn_wc/wc_db_wcroot.c b/contrib/subversion/subversion/libsvn_wc/wc_db_wcroot.c index a111073b3..1cfca3d1c 100644 --- a/contrib/subversion/subversion/libsvn_wc/wc_db_wcroot.c +++ b/contrib/subversion/subversion/libsvn_wc/wc_db_wcroot.c @@ -28,6 +28,7 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_path.h" +#include "svn_pools.h" #include "svn_version.h" #include "wc.h" @@ -42,7 +43,7 @@ #define UNKNOWN_WC_ID ((apr_int64_t) -1) #define FORMAT_FROM_SDB (-1) - +/* #define VERIFY_ON_CLOSE */ /* 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. */ @@ -145,9 +146,8 @@ get_path_kind(svn_node_kind_t *kind, } -/* 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_error_t * +svn_wc__db_verify_no_work(svn_sqlite__db_t *sdb) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -163,6 +163,27 @@ verify_no_work(svn_sqlite__db_t *sdb) return SVN_NO_ERROR; } +#if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG) +/* Implements svn_wc__db_verify_cb_t */ +static svn_error_t * +verify_db_cb(void *baton, + const char *wc_abspath, + const char *local_relpath, + int op_depth, + int id, + const char *msg, + apr_pool_t *scratch_pool) +{ + if (op_depth >= 0) + SVN_DBG(("DB-VRFY: %s: %s (%d): SV%04d %s", + wc_abspath, local_relpath, op_depth, id, msg)); + else + SVN_DBG(("DB-VRFY: %s: %s: SV%04d %s", + wc_abspath, local_relpath, id, msg)); + + return SVN_NO_ERROR; +} +#endif /* */ static apr_status_t @@ -173,6 +194,18 @@ close_wcroot(void *data) SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL); +#if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG) + if (getenv("SVN_CMDLINE_VERIFY_SQL_AT_CLOSE")) + { + apr_pool_t *scratch_pool = svn_pool_create(NULL); + + svn_error_clear(svn_wc__db_verify_db_full_internal( + wcroot, verify_db_cb, NULL, scratch_pool)); + + svn_pool_destroy(scratch_pool); + } +#endif + err = svn_sqlite__close(wcroot->sdb); wcroot->sdb = NULL; if (err) @@ -207,6 +240,7 @@ svn_wc__db_open(svn_wc__db_t **db, { svn_error_t *err; svn_boolean_t sqlite_exclusive = FALSE; + apr_int64_t timeout; err = svn_config_get_bool(config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, @@ -218,6 +252,15 @@ svn_wc__db_open(svn_wc__db_t **db, } else (*db)->exclusive = sqlite_exclusive; + + err = svn_config_get_int64(config, &timeout, + SVN_CONFIG_SECTION_WORKING_COPY, + SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT, + 0); + if (err || timeout < 0 || timeout > APR_INT32_MAX) + svn_error_clear(err); + else + (*db)->timeout = (apr_int32_t)timeout; } return SVN_NO_ERROR; @@ -236,8 +279,8 @@ svn_wc__db_close(svn_wc__db_t *db) 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); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); + const char *local_abspath = apr_hash_this_key(hi); if (wcroot->sdb) svn_hash_sets(roots, wcroot->abspath, wcroot); @@ -258,7 +301,6 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, 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) { @@ -293,11 +335,11 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, } /* 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))) + suspect and, thus, we cannot upgrade this database. */ + if (format >= SVN_WC__HAS_WORK_QUEUE && + format < SVN_WC__VERSION && verify_format) { - svn_error_t *err = verify_no_work(sdb); + svn_error_t *err = svn_wc__db_verify_no_work(sdb); if (err) { /* Special message for attempts to upgrade a 1.7-dev wc with @@ -354,7 +396,7 @@ svn_wc__db_close_many_wcroots(apr_hash_t *roots, 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); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); apr_status_t result; result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot); @@ -594,7 +636,7 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, as the filesystem allows. */ err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE, svn_sqlite__mode_readwrite, - db->exclusive, NULL, + db->exclusive, db->timeout, NULL, db->state_pool, scratch_pool); if (err == NULL) { @@ -711,11 +753,9 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, 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_quick_wrapf( + err, _("Missing a row in WCROOT for '%s'."), + svn_dirent_local_style(original_abspath, scratch_pool)); return svn_error_trace(err); } @@ -729,7 +769,7 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, ? symlink_wcroot_abspath : local_abspath), sdb, wc_id, format, - db->verify_format, db->enforce_empty_wq, + db->verify_format, db->state_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) && @@ -804,7 +844,7 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, ? symlink_wcroot_abspath : local_abspath), NULL, UNKNOWN_WC_ID, wc_format, - db->verify_format, db->enforce_empty_wq, + db->verify_format, db->state_pool, scratch_pool)); } @@ -959,10 +999,10 @@ svn_wc__db_drop_root(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); if (wcroot == root_wcroot) - svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL); + svn_hash_sets(db->dir_data, apr_hash_this_key(hi), NULL); } result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot); diff --git a/contrib/subversion/subversion/libsvn_wc/workqueue.c b/contrib/subversion/subversion/libsvn_wc/workqueue.c index b034d7d12..18736cc6f 100644 --- a/contrib/subversion/subversion/libsvn_wc/workqueue.c +++ b/contrib/subversion/subversion/libsvn_wc/workqueue.c @@ -23,6 +23,7 @@ #include +#include "svn_private_config.h" #include "svn_types.h" #include "svn_pools.h" #include "svn_dirent_uri.h" @@ -37,7 +38,7 @@ #include "conflicts.h" #include "translate.h" -#include "svn_private_config.h" +#include "private/svn_io_private.h" #include "private/svn_skel.h" @@ -142,8 +143,7 @@ run_base_remove(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_base_remove(db, local_abspath, FALSE /* keep_as_working */, - TRUE /* queue_deletes */, - FALSE /* remove_locks */, + SVN_IS_VALID_REVNUM(not_present_rev), FALSE, not_present_rev, NULL, NULL, scratch_pool)); @@ -395,8 +395,6 @@ run_postupgrade(work_item_baton_t *wqb, const char *entries_path; const char *format_path; const char *wcroot_abspath; - const char *adm_path; - const char *temp_path; svn_error_t *err; err = svn_wc__wipe_postupgrade(wri_abspath, FALSE, @@ -410,7 +408,6 @@ run_postupgrade(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath, scratch_pool, scratch_pool)); - adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool); entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES, scratch_pool); format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT, @@ -421,15 +418,13 @@ run_postupgrade(work_item_baton_t *wqb, ### The order may matter for some sufficiently old clients.. but ### this code only runs during upgrade after the files had been ### removed earlier during the upgrade. */ - SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING, + SVN_ERR(svn_io_write_atomic(format_path, SVN_WC__NON_ENTRIES_STRING, sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, - svn_io_file_del_none, scratch_pool)); - SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool)); + NULL, scratch_pool)); - SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING, + SVN_ERR(svn_io_write_atomic(entries_path, SVN_WC__NON_ENTRIES_STRING, sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, - svn_io_file_del_none, scratch_pool)); - SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool)); + NULL, scratch_pool)); return SVN_NO_ERROR; } @@ -474,7 +469,6 @@ run_file_install(work_item_baton_t *wqb, apr_hash_t *keywords; const char *temp_dir_abspath; svn_stream_t *dst_stream; - const char *dst_abspath; apr_int64_t val; const char *wcroot_abspath; const char *source_abspath; @@ -577,10 +571,8 @@ run_file_install(work_item_baton_t *wqb, /* Translate to a temporary file. We don't want the user seeing a partial file, nor let them muck with it while we translate. We may also need to get its TRANSLATED_SIZE before the user can monkey it. */ - SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath, - temp_dir_abspath, - svn_io_file_del_none, - scratch_pool, scratch_pool)); + SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath, + scratch_pool, scratch_pool)); /* Copy from the source to the dest, translating as we go. This will also close both streams. */ @@ -589,35 +581,11 @@ run_file_install(work_item_baton_t *wqb, scratch_pool)); /* All done. Move the file into place. */ - - { - svn_error_t *err; - - err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool); - - /* With a single db we might want to install files in a missing directory. - Simply trying this scenario on error won't do any harm and at least - one user reported this problem on IRC. */ - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_t *err2; - - err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath, - scratch_pool), - scratch_pool); - - if (err2) - /* Creating directory didn't work: Return all errors */ - return svn_error_trace(svn_error_compose_create(err, err2)); - else - /* We could create a directory: retry install */ - svn_error_clear(err); - - SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool)); - } - else - SVN_ERR(err); - } + /* With a single db we might want to install files in a missing directory. + Simply trying this scenario on error won't do any harm and at least + one user reported this problem on IRC. */ + SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath, + TRUE /* make_parents*/, scratch_pool)); /* Tweak the on-disk file according to its properties. */ #ifndef WIN32 @@ -1047,8 +1015,8 @@ svn_error_t * svn_wc__wq_build_dir_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - apr_pool_t *scratch_pool, - apr_pool_t *result_pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *local_relpath; @@ -1138,7 +1106,7 @@ run_prej_install(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, local_relpath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath, @@ -1147,14 +1115,15 @@ run_prej_install(work_item_baton_t *wqb, scratch_pool, scratch_pool)); if (arg1->next != NULL) - prop_conflict_skel = arg1->next; + prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */ else - SVN_ERR_MALFUNCTION(); /* ### wc_db can't provide it ... yet. */ + prop_conflict_skel = NULL; /* Read from DB */ /* Construct a property reject file in the temporary area. */ SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath, db, local_abspath, prop_conflict_skel, + cancel_func, cancel_baton, scratch_pool, scratch_pool)); /* ... and atomically move it into place. */ @@ -1170,21 +1139,21 @@ svn_error_t * svn_wc__wq_build_prej_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - svn_skel_t *conflict_skel, + /*svn_skel_t *conflict_skel,*/ apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *local_relpath; *work_item = svn_skel__make_empty_list(result_pool); - /* ### gotta have this, today */ - SVN_ERR_ASSERT(conflict_skel != NULL); - SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, local_abspath, result_pool, scratch_pool)); - if (conflict_skel != NULL) - svn_skel__prepend(conflict_skel, *work_item); + /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel + here: + if (conflict_skel != NULL) + svn_skel__prepend(conflict_skel, *work_item); + */ svn_skel__prepend_str(local_relpath, *work_item, result_pool); svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool); @@ -1317,7 +1286,7 @@ run_set_text_conflict_markers(work_item_baton_t *wqb, /* Check if we should combine with a property conflict... */ svn_skel_t *conflicts; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); if (! conflicts) @@ -1383,7 +1352,8 @@ run_set_property_conflict_marker(work_item_baton_t *wqb, svn_skel_t *conflicts; apr_hash_t *prop_names; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); if (! conflicts) @@ -1520,8 +1490,11 @@ svn_wc__wq_run(svn_wc__db_t *db, { static int count = 0; const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE"); + int count_env_val; + + SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var)); - if (count_env_var && ++count == atoi(count_env_var)) + if (count_env_var && ++count == count_env_val) return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel"); } #endif diff --git a/contrib/subversion/subversion/libsvn_wc/workqueue.h b/contrib/subversion/subversion/libsvn_wc/workqueue.h index 0617a60ed..630de07fd 100644 --- a/contrib/subversion/subversion/libsvn_wc/workqueue.h +++ b/contrib/subversion/subversion/libsvn_wc/workqueue.h @@ -73,7 +73,11 @@ extern "C" { These will be combined as appropriate, and returned in one of the above three styles. - The resulting list will be ordered: WORK_ITEM1 first, then WORK_ITEM2 */ + The resulting list will be ordered: WORK_ITEM1 first, then WORK_ITEM2. + + The result contains a shallow copy of the inputs. Allocate any + additional storage needed in RESULT_POOL. + */ svn_skel_t * svn_wc__wq_merge(svn_skel_t *work_item1, svn_skel_t *work_item2, @@ -177,22 +181,12 @@ svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item, /* Set *WORK_ITEM to a new work item that will install a property reject - file for LOCAL_ABSPATH into the working copy. The property conflicts will - be taken from CONFLICT_SKEL. - - ### Caution: Links CONFLICT_SKEL into the *WORK_ITEM, which involves - modifying *CONFLICT_SKEL. - - ### TODO: Make CONFLICT_SKEL 'const' and dup it into RESULT_POOL. - - ### TODO: If CONFLICT_SKEL is NULL, take property conflicts from wc_db - for the given DB/LOCAL_ABSPATH. + file for LOCAL_ABSPATH into the working copy. */ svn_error_t * svn_wc__wq_build_prej_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - svn_skel_t *conflict_skel, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -221,8 +215,8 @@ svn_error_t * svn_wc__wq_build_dir_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - apr_pool_t *scratch_pool, - apr_pool_t *result_pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); svn_error_t * svn_wc__wq_build_postupgrade(svn_skel_t **work_item, diff --git a/contrib/subversion/subversion/svn/add-cmd.c b/contrib/subversion/subversion/svn/add-cmd.c index 44f73c7bc..f0791fb02 100644 --- a/contrib/subversion/subversion/svn/add-cmd.c +++ b/contrib/subversion/subversion/svn/add-cmd.c @@ -83,7 +83,7 @@ svn_cl__add(apr_getopt_t *os, errors, opt_state->quiet, SVN_ERR_ENTRY_EXISTS, SVN_ERR_WC_PATH_NOT_FOUND, - SVN_NO_ERROR)); + 0)); } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/svn/auth-cmd.c b/contrib/subversion/subversion/svn/auth-cmd.c new file mode 100644 index 000000000..68ca067e7 --- /dev/null +++ b/contrib/subversion/subversion/svn/auth-cmd.c @@ -0,0 +1,483 @@ +/* + * auth-cmd.c: Subversion auth creds cache administration + * + * ==================================================================== + * 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 +#include +#include +#include + +#include "svn_private_config.h" + +#include "svn_private_config.h" +#include "svn_pools.h" +#include "svn_error.h" +#include "svn_opt.h" +#include "svn_dirent_uri.h" +#include "svn_hash.h" +#include "svn_utf.h" +#include "svn_cmdline.h" +#include "svn_config.h" +#include "svn_auth.h" +#include "svn_sorts.h" +#include "svn_base64.h" +#include "svn_x509.h" +#include "svn_time.h" + +#include "private/svn_cmdline_private.h" +#include "private/svn_token.h" +#include "private/svn_sorts_private.h" + +#include "cl.h" + +/* The separator between credentials . */ +#define SEP_STRING \ + "------------------------------------------------------------------------\n" + +static svn_error_t * +show_cert_failures(const char *failure_string, + apr_pool_t *scratch_pool) +{ + unsigned int failures; + + SVN_ERR(svn_cstring_atoui(&failures, failure_string)); + + if (0 == (failures & (SVN_AUTH_SSL_NOTYETVALID | SVN_AUTH_SSL_EXPIRED | + SVN_AUTH_SSL_CNMISMATCH | SVN_AUTH_SSL_UNKNOWNCA | + SVN_AUTH_SSL_OTHER))) + return SVN_NO_ERROR; + + SVN_ERR(svn_cmdline_printf( + scratch_pool, _("Automatic certificate validity check failed " + "because:\n"))); + + if (failures & SVN_AUTH_SSL_NOTYETVALID) + SVN_ERR(svn_cmdline_printf( + scratch_pool, _(" The certificate is not yet valid.\n"))); + + if (failures & SVN_AUTH_SSL_EXPIRED) + SVN_ERR(svn_cmdline_printf( + scratch_pool, _(" The certificate has expired.\n"))); + + if (failures & SVN_AUTH_SSL_CNMISMATCH) + SVN_ERR(svn_cmdline_printf( + scratch_pool, _(" The certificate's Common Name (hostname) " + "does not match the remote hostname.\n"))); + + if (failures & SVN_AUTH_SSL_UNKNOWNCA) + SVN_ERR(svn_cmdline_printf( + scratch_pool, _(" The certificate issuer is unknown.\n"))); + + if (failures & SVN_AUTH_SSL_OTHER) + SVN_ERR(svn_cmdline_printf( + scratch_pool, _(" Unknown verification failure.\n"))); + + return SVN_NO_ERROR; +} + + +/* decodes from format we store certs in for auth creds and + * turns parsing errors into warnings if PRINT_WARNING is TRUE + * and ignores them otherwise. returns NULL if it couldn't + * parse a cert for any reason. */ +static svn_x509_certinfo_t * +parse_certificate(const svn_string_t *ascii_cert, + svn_boolean_t print_warning, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_x509_certinfo_t *certinfo; + const svn_string_t *der_cert; + svn_error_t *err; + + /* Convert header-less PEM to DER by undoing base64 encoding. */ + der_cert = svn_base64_decode_string(ascii_cert, scratch_pool); + + err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len, + result_pool, scratch_pool); + if (err) + { + /* Just display X.509 parsing errors as warnings and continue */ + if (print_warning) + svn_handle_warning2(stderr, err, "svn: "); + svn_error_clear(err); + return NULL; + } + + return certinfo; +} + + +struct walk_credentials_baton_t +{ + int matches; + svn_boolean_t list; + svn_boolean_t delete; + svn_boolean_t show_passwords; + apr_array_header_t *patterns; +}; + +static svn_boolean_t +match_pattern(const char *pattern, const char *value, + svn_boolean_t caseblind, apr_pool_t *scratch_pool) +{ + const char *p = apr_psprintf(scratch_pool, "*%s*", pattern); + int flags = (caseblind ? APR_FNM_CASE_BLIND : 0); + + return (apr_fnmatch(p, value, flags) == APR_SUCCESS); +} + +static svn_boolean_t +match_certificate(svn_x509_certinfo_t **certinfo, + const char *pattern, + const svn_string_t *ascii_cert, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *value; + const svn_checksum_t *checksum; + const apr_array_header_t *hostnames; + int i; + + *certinfo = parse_certificate(ascii_cert, FALSE, result_pool, scratch_pool); + if (*certinfo == NULL) + return FALSE; + + value = svn_x509_certinfo_get_subject(*certinfo, scratch_pool); + if (match_pattern(pattern, value, FALSE, scratch_pool)) + return TRUE; + + value = svn_x509_certinfo_get_issuer(*certinfo, scratch_pool); + if (match_pattern(pattern, value, FALSE, scratch_pool)) + return TRUE; + + checksum = svn_x509_certinfo_get_digest(*certinfo); + value = svn_checksum_to_cstring_display(checksum, scratch_pool); + if (match_pattern(pattern, value, TRUE, scratch_pool)) + return TRUE; + + hostnames = svn_x509_certinfo_get_hostnames(*certinfo); + if (hostnames) + { + for (i = 0; i < hostnames->nelts; i++) + { + const char *hostname = APR_ARRAY_IDX(hostnames, i, const char *); + if (match_pattern(pattern, hostname, TRUE, scratch_pool)) + return TRUE; + } + } + + return FALSE; +} + + +static svn_error_t * +match_credential(svn_boolean_t *match, + svn_x509_certinfo_t **certinfo, + const char *cred_kind, + const char *realmstring, + apr_array_header_t *patterns, + apr_array_header_t *cred_items, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + *match = FALSE; + + for (i = 0; i < patterns->nelts; i++) + { + const char *pattern = APR_ARRAY_IDX(patterns, i, const char *); + int j; + + *match = match_pattern(pattern, cred_kind, FALSE, iterpool); + if (!*match) + *match = match_pattern(pattern, realmstring, FALSE, iterpool); + if (!*match) + { + svn_pool_clear(iterpool); + for (j = 0; j < cred_items->nelts; j++) + { + svn_sort__item_t item; + const char *key; + svn_string_t *value; + + item = APR_ARRAY_IDX(cred_items, j, svn_sort__item_t); + key = item.key; + value = item.value; + if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0 || + strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0) + continue; /* don't match secrets */ + else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0) + *match = match_certificate(certinfo, pattern, value, + result_pool, iterpool); + else + *match = match_pattern(pattern, value->data, FALSE, iterpool); + + if (*match) + break; + } + } + if (!*match) + break; + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +show_cert(svn_x509_certinfo_t *certinfo, const svn_string_t *pem_cert, + apr_pool_t *scratch_pool) +{ + const apr_array_header_t *hostnames; + + if (certinfo == NULL) + certinfo = parse_certificate(pem_cert, TRUE, scratch_pool, scratch_pool); + if (certinfo == NULL) + return SVN_NO_ERROR; + + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Subject: %s\n"), + svn_x509_certinfo_get_subject(certinfo, scratch_pool))); + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid from: %s\n"), + svn_time_to_human_cstring( + svn_x509_certinfo_get_valid_from(certinfo), + scratch_pool))); + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Valid until: %s\n"), + svn_time_to_human_cstring( + svn_x509_certinfo_get_valid_to(certinfo), + scratch_pool))); + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"), + svn_x509_certinfo_get_issuer(certinfo, scratch_pool))); + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"), + svn_checksum_to_cstring_display( + svn_x509_certinfo_get_digest(certinfo), + scratch_pool))); + + hostnames = svn_x509_certinfo_get_hostnames(certinfo); + if (hostnames && !apr_is_empty_array(hostnames)) + { + int i; + svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool); + for (i = 0; i < hostnames->nelts; ++i) + { + const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*); + if (i > 0) + svn_stringbuf_appendbytes(buf, ", ", 2); + svn_stringbuf_appendbytes(buf, hostname, strlen(hostname)); + } + SVN_ERR(svn_cmdline_printf(scratch_pool, _("Hostnames: %s\n"), + buf->data)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +list_credential(const char *cred_kind, + const char *realmstring, + apr_array_header_t *cred_items, + svn_boolean_t show_passwords, + svn_x509_certinfo_t *certinfo, + apr_pool_t *scratch_pool) +{ + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_cmdline_printf(scratch_pool, SEP_STRING)); + SVN_ERR(svn_cmdline_printf(scratch_pool, + _("Credential kind: %s\n"), cred_kind)); + SVN_ERR(svn_cmdline_printf(scratch_pool, + _("Authentication realm: %s\n"), realmstring)); + + for (i = 0; i < cred_items->nelts; i++) + { + svn_sort__item_t item; + const char *key; + svn_string_t *value; + + svn_pool_clear(iterpool); + item = APR_ARRAY_IDX(cred_items, i, svn_sort__item_t); + key = item.key; + value = item.value; + if (strcmp(value->data, realmstring) == 0) + continue; /* realm string was already shown above */ + else if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0) + { + if (show_passwords) + SVN_ERR(svn_cmdline_printf(iterpool, + _("Password: %s\n"), value->data)); + else + SVN_ERR(svn_cmdline_printf(iterpool, _("Password: [not shown]\n"))); + } + else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0) + { + if (show_passwords) + SVN_ERR(svn_cmdline_printf(iterpool, + _("Passphrase: %s\n"), value->data)); + else + SVN_ERR(svn_cmdline_printf(iterpool, + _("Passphrase: [not shown]\n"))); + } + else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0) + SVN_ERR(svn_cmdline_printf(iterpool, _("Password cache: %s\n"), + value->data)); + else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0) + SVN_ERR(svn_cmdline_printf(iterpool, _("Username: %s\n"), value->data)); + else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0) + SVN_ERR(show_cert(certinfo, value, iterpool)); + else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0) + SVN_ERR(show_cert_failures(value->data, iterpool)); + else + SVN_ERR(svn_cmdline_printf(iterpool, "%s: %s\n", key, value->data)); + } + svn_pool_destroy(iterpool); + + SVN_ERR(svn_cmdline_printf(scratch_pool, "\n")); + return SVN_NO_ERROR; +} + +/* This implements `svn_config_auth_walk_func_t` */ +static svn_error_t * +walk_credentials(svn_boolean_t *delete_cred, + void *baton, + const char *cred_kind, + const char *realmstring, + apr_hash_t *cred_hash, + apr_pool_t *scratch_pool) +{ + struct walk_credentials_baton_t *b = baton; + apr_array_header_t *sorted_cred_items; + svn_x509_certinfo_t *certinfo = NULL; + + *delete_cred = FALSE; + + sorted_cred_items = svn_sort__hash(cred_hash, + svn_sort_compare_items_lexically, + scratch_pool); + if (b->patterns->nelts > 0) + { + svn_boolean_t match; + + SVN_ERR(match_credential(&match, &certinfo, cred_kind, realmstring, + b->patterns, sorted_cred_items, + scratch_pool, scratch_pool)); + if (!match) + return SVN_NO_ERROR; + } + + b->matches++; + + if (b->list) + SVN_ERR(list_credential(cred_kind, realmstring, sorted_cred_items, + b->show_passwords, certinfo, scratch_pool)); + if (b->delete) + { + *delete_cred = TRUE; + SVN_ERR(svn_cmdline_printf(scratch_pool, + _("Deleting %s credential for realm '%s'\n"), + cred_kind, realmstring)); + } + + return SVN_NO_ERROR; +} + + +/* This implements `svn_opt_subcommand_t'. */ +svn_error_t * +svn_cl__auth(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; + const char *config_path; + struct walk_credentials_baton_t b; + + b.matches = 0; + b.show_passwords = opt_state->show_passwords; + b.list = !opt_state->remove; + b.delete = opt_state->remove; + b.patterns = apr_array_make(pool, 1, sizeof(const char *)); + for (; os->ind < os->argc; os->ind++) + { + /* The apr_getopt targets are still in native encoding. */ + const char *raw_target = os->argv[os->ind]; + const char *utf8_target; + + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target, + raw_target, pool)); + APR_ARRAY_PUSH(b.patterns, const char *) = utf8_target; + } + + SVN_ERR(svn_config_get_user_config_path(&config_path, + opt_state->config_dir, NULL, + pool)); + + if (b.delete && b.patterns->nelts < 1) + return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); + + SVN_ERR(svn_config_walk_auth_data(config_path, walk_credentials, &b, pool)); + + if (b.list) + { + if (b.matches == 0) + { + if (b.patterns->nelts == 0) + SVN_ERR(svn_cmdline_printf(pool, + _("Credentials cache in '%s' is empty\n"), + svn_dirent_local_style(config_path, pool))); + else + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0, + _("Credentials cache in '%s' contains " + "no matching credentials"), + svn_dirent_local_style(config_path, pool)); + } + else + { + if (b.patterns->nelts == 0) + SVN_ERR(svn_cmdline_printf(pool, + _("Credentials cache in '%s' contains %d credentials\n"), + svn_dirent_local_style(config_path, pool), b.matches)); + else + SVN_ERR(svn_cmdline_printf(pool, + _("Credentials cache in '%s' contains %d matching " + "credentials\n"), + svn_dirent_local_style(config_path, pool), b.matches)); + } + + } + + if (b.delete) + { + if (b.matches == 0) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, 0, + _("Credentials cache in '%s' contains " + "no matching credentials"), + svn_dirent_local_style(config_path, pool)); + else + SVN_ERR(svn_cmdline_printf(pool, _("Deleted %d matching credentials " + "from '%s'\n"), b.matches, + svn_dirent_local_style(config_path, pool))); + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svn/blame-cmd.c b/contrib/subversion/subversion/svn/blame-cmd.c index 174a199fe..3911a6425 100644 --- a/contrib/subversion/subversion/svn/blame-cmd.c +++ b/contrib/subversion/subversion/svn/blame-cmd.c @@ -31,6 +31,7 @@ #include "svn_pools.h" #include "svn_props.h" #include "svn_cmdline.h" +#include "svn_sorts.h" #include "svn_xml.h" #include "svn_time.h" #include "cl.h" @@ -42,6 +43,8 @@ typedef struct blame_baton_t svn_cl__opt_state_t *opt_state; svn_stream_t *out; svn_stringbuf_t *sbuf; + + int rev_maxlength; } blame_baton_t; @@ -63,9 +66,9 @@ blame_receiver_xml(void *baton, svn_boolean_t local_change, apr_pool_t *pool) { - svn_cl__opt_state_t *opt_state = - ((blame_baton_t *) baton)->opt_state; - svn_stringbuf_t *sb = ((blame_baton_t *) baton)->sbuf; + blame_baton_t *bb = baton; + svn_cl__opt_state_t *opt_state = bb->opt_state; + svn_stringbuf_t *sb = bb->sbuf; /* "" */ /* line_no is 0-based, but the rest of the world is probably Pascal @@ -74,7 +77,7 @@ blame_receiver_xml(void *baton, "line-number", apr_psprintf(pool, "%" APR_INT64_T_FMT, line_no + 1), - NULL); + SVN_VA_NULL); if (SVN_IS_VALID_REVNUM(revision)) svn_cl__print_xml_commit(&sb, revision, @@ -88,7 +91,7 @@ blame_receiver_xml(void *baton, { /* "" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "merged", - "path", merged_path, NULL); + "path", merged_path, SVN_VA_NULL); svn_cl__print_xml_commit(&sb, merged_revision, svn_prop_get_value(merged_rev_props, @@ -119,23 +122,13 @@ print_line_info(svn_stream_t *out, const char *date, const char *path, svn_boolean_t verbose, - svn_revnum_t end_revnum, + int rev_maxlength, apr_pool_t *pool) { const char *time_utf8; const char *time_stdout; const char *rev_str; - int rev_maxlength; - /* The standard column width for the revision number is 6 characters. - If the revision number can potentially be larger (i.e. if the end_revnum - is larger than 1000000), we increase the column width as needed. */ - rev_maxlength = 6; - while (end_revnum >= 1000000) - { - rev_maxlength++; - end_revnum = end_revnum / 10; - } rev_str = SVN_IS_VALID_REVNUM(revision) ? apr_psprintf(pool, "%*ld", rev_maxlength, revision) : apr_psprintf(pool, "%*s", rev_maxlength, "-"); @@ -189,11 +182,26 @@ blame_receiver(void *baton, svn_boolean_t local_change, apr_pool_t *pool) { - svn_cl__opt_state_t *opt_state = - ((blame_baton_t *) baton)->opt_state; - svn_stream_t *out = ((blame_baton_t *)baton)->out; + blame_baton_t *bb = baton; + svn_cl__opt_state_t *opt_state = bb->opt_state; + svn_stream_t *out = bb->out; svn_boolean_t use_merged = FALSE; + if (!bb->rev_maxlength) + { + svn_revnum_t max_revnum = MAX(start_revnum, end_revnum); + /* The standard column width for the revision number is 6 characters. + If the revision number can potentially be larger (i.e. if the end_revnum + is larger than 1000000), we increase the column width as needed. */ + + bb->rev_maxlength = 6; + while (max_revnum >= 1000000) + { + bb->rev_maxlength++; + max_revnum = max_revnum / 10; + } + } + if (opt_state->use_merge_history) { /* Choose which revision to use. If they aren't equal, prefer the @@ -216,7 +224,8 @@ blame_receiver(void *baton, SVN_PROP_REVISION_AUTHOR), svn_prop_get_value(merged_rev_props, SVN_PROP_REVISION_DATE), - merged_path, opt_state->verbose, end_revnum, + merged_path, opt_state->verbose, + bb->rev_maxlength, pool)); else SVN_ERR(print_line_info(out, revision, @@ -224,7 +233,8 @@ blame_receiver(void *baton, SVN_PROP_REVISION_AUTHOR), svn_prop_get_value(rev_props, SVN_PROP_REVISION_DATE), - NULL, opt_state->verbose, end_revnum, + NULL, opt_state->verbose, + bb->rev_maxlength, pool)); return svn_stream_printf(out, pool, "%s%s", line, APR_EOL_STR); @@ -286,6 +296,7 @@ svn_cl__blame(apr_getopt_t *os, bl.sbuf = svn_stringbuf_create_empty(pool); bl.opt_state = opt_state; + bl.rev_maxlength = 0; subpool = svn_pool_create(pool); @@ -350,7 +361,7 @@ svn_cl__blame(apr_getopt_t *os, if (! svn_path_is_url(target)) outpath = svn_dirent_local_style(truepath, subpool); svn_xml_make_open_tag(&bl.sbuf, pool, svn_xml_normal, "target", - "path", outpath, NULL); + "path", outpath, SVN_VA_NULL); receiver = blame_receiver_xml; } diff --git a/contrib/subversion/subversion/svn/cat-cmd.c b/contrib/subversion/subversion/svn/cat-cmd.c index 551420e7f..eef7696ce 100644 --- a/contrib/subversion/subversion/svn/cat-cmd.c +++ b/contrib/subversion/subversion/svn/cat-cmd.c @@ -76,15 +76,16 @@ svn_cl__cat(apr_getopt_t *os, SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool)); - SVN_ERR(svn_cl__try(svn_client_cat2(out, truepath, &peg_revision, + SVN_ERR(svn_cl__try(svn_client_cat3(NULL, out, truepath, &peg_revision, &(opt_state->start_revision), - ctx, subpool), + !opt_state->ignore_keywords, + ctx, subpool, subpool), errors, opt_state->quiet, SVN_ERR_UNVERSIONED_RESOURCE, SVN_ERR_ENTRY_NOT_FOUND, SVN_ERR_CLIENT_IS_DIRECTORY, SVN_ERR_FS_NOT_FOUND, - SVN_NO_ERROR)); + 0)); } svn_pool_destroy(subpool); diff --git a/contrib/subversion/subversion/svn/changelist-cmd.c b/contrib/subversion/subversion/svn/changelist-cmd.c index 46347b65b..27de62b11 100644 --- a/contrib/subversion/subversion/svn/changelist-cmd.c +++ b/contrib/subversion/subversion/svn/changelist-cmd.c @@ -72,25 +72,7 @@ svn_cl__changelist(apr_getopt_t *os, SVN_ERR(svn_cl__check_targets_are_local_paths(targets)); if (opt_state->quiet) - /* FIXME: This is required because svn_client_create_context() - always initializes ctx->notify_func2 to a wrapper function - which calls ctx->notify_func() if it isn't NULL. In other - words, typically, ctx->notify_func2 is never NULL. This isn't - usually a problem, but the changelist logic generates - svn_error_t's as part of its notification. - - So, svn_wc_set_changelist() checks its notify_func (our - ctx->notify_func2) for NULL-ness, and seeing non-NULL-ness, - generates a notificaton object and svn_error_t to describe some - problem. It passes that off to its notify_func (our - ctx->notify_func2) which drops the notification on the floor - (because it wraps a NULL ctx->notify_func). But svn_error_t's - dropped on the floor cause SEGFAULTs at pool cleanup time -- - they need instead to be cleared. - - SOOOooo... we set our ctx->notify_func2 to NULL so the WC code - doesn't even generate the errors. */ - ctx->notify_func2 = NULL; + ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */ if (depth == svn_depth_unknown) depth = svn_depth_empty; @@ -106,7 +88,7 @@ svn_cl__changelist(apr_getopt_t *os, errors, opt_state->quiet, SVN_ERR_UNVERSIONED_RESOURCE, SVN_ERR_WC_PATH_NOT_FOUND, - SVN_NO_ERROR)); + 0)); } else { @@ -117,7 +99,7 @@ svn_cl__changelist(apr_getopt_t *os, errors, opt_state->quiet, SVN_ERR_UNVERSIONED_RESOURCE, SVN_ERR_WC_PATH_NOT_FOUND, - SVN_NO_ERROR)); + 0)); } if (errors->nelts > 0) diff --git a/contrib/subversion/subversion/svn/checkout-cmd.c b/contrib/subversion/subversion/svn/checkout-cmd.c index 6c192a033..56fd02b03 100644 --- a/contrib/subversion/subversion/svn/checkout-cmd.c +++ b/contrib/subversion/subversion/svn/checkout-cmd.c @@ -72,6 +72,7 @@ svn_cl__checkout(apr_getopt_t *os, svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; apr_pool_t *subpool; apr_array_header_t *targets; + struct svn_cl__check_externals_failed_notify_baton nwb; const char *last_target, *local_dir; int i; @@ -113,6 +114,12 @@ svn_cl__checkout(apr_getopt_t *os, if (! opt_state->quiet) SVN_ERR(svn_cl__notifier_mark_checkout(ctx->notify_baton2)); + 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; + subpool = svn_pool_create(pool); for (i = 0; i < targets->nelts; ++i) { @@ -169,5 +176,10 @@ svn_cl__checkout(apr_getopt_t *os, } svn_pool_destroy(subpool); + if (nwb.had_externals_error) + return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL, + _("Failure occurred processing one or " + "more externals definitions")); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/cl-conflicts.c b/contrib/subversion/subversion/svn/cl-conflicts.c index 440c9d76f..8507e8c3b 100644 --- a/contrib/subversion/subversion/svn/cl-conflicts.c +++ b/contrib/subversion/subversion/svn/cl-conflicts.c @@ -69,11 +69,13 @@ static const svn_token_map_t map_conflict_kind_xml[] = /* Return a localised string representation of the local part of a conflict; NULL for non-localised odd cases. */ static const char * -local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason) +local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason, + svn_wc_operation_t operation) { switch (kind) { case svn_node_file: + case svn_node_symlink: switch (reason) { case svn_wc_conflict_reason_edited: @@ -83,7 +85,10 @@ local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason) case svn_wc_conflict_reason_deleted: return _("local file delete"); case svn_wc_conflict_reason_missing: - return _("local file missing"); + if (operation == svn_wc_operation_merge) + return _("local file missing or deleted or moved away"); + else + return _("local file missing"); case svn_wc_conflict_reason_unversioned: return _("local file unversioned"); case svn_wc_conflict_reason_added: @@ -106,7 +111,10 @@ local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason) case svn_wc_conflict_reason_deleted: return _("local dir delete"); case svn_wc_conflict_reason_missing: - return _("local dir missing"); + if (operation == svn_wc_operation_merge) + return _("local dir missing or deleted or moved away"); + else + return _("local dir missing"); case svn_wc_conflict_reason_unversioned: return _("local dir unversioned"); case svn_wc_conflict_reason_added: @@ -119,9 +127,32 @@ local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason) return _("local dir moved here"); } break; - case svn_node_symlink: case svn_node_none: case svn_node_unknown: + switch (reason) + { + case svn_wc_conflict_reason_edited: + return _("local edit"); + case svn_wc_conflict_reason_obstructed: + return _("local obstruction"); + case svn_wc_conflict_reason_deleted: + return _("local delete"); + case svn_wc_conflict_reason_missing: + if (operation == svn_wc_operation_merge) + return _("local missing or deleted or moved away"); + else + return _("local missing"); + case svn_wc_conflict_reason_unversioned: + return _("local unversioned"); + case svn_wc_conflict_reason_added: + return _("local add"); + case svn_wc_conflict_reason_replaced: + return _("local replace"); + case svn_wc_conflict_reason_moved_away: + return _("local moved away"); + case svn_wc_conflict_reason_moved_here: + return _("local moved here"); + } break; } return NULL; @@ -135,6 +166,7 @@ incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action) switch (kind) { case svn_node_file: + case svn_node_symlink: switch (action) { case svn_wc_conflict_action_edit: @@ -142,9 +174,9 @@ incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action) case svn_wc_conflict_action_add: return _("incoming file add"); case svn_wc_conflict_action_delete: - return _("incoming file delete"); + return _("incoming file delete or move"); case svn_wc_conflict_action_replace: - return _("incoming file replace"); + return _("incoming replace with file"); } break; case svn_node_dir: @@ -155,14 +187,24 @@ incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action) case svn_wc_conflict_action_add: return _("incoming dir add"); case svn_wc_conflict_action_delete: - return _("incoming dir delete"); + return _("incoming dir delete or move"); case svn_wc_conflict_action_replace: - return _("incoming dir replace"); + return _("incoming replace with dir"); } break; - case svn_node_symlink: case svn_node_none: case svn_node_unknown: + switch (action) + { + case svn_wc_conflict_action_edit: + return _("incoming edit"); + case svn_wc_conflict_action_add: + return _("incoming add"); + case svn_wc_conflict_action_delete: + return _("incoming delete or move"); + case svn_wc_conflict_action_replace: + return _("incoming replace"); + } break; } return NULL; @@ -267,7 +309,8 @@ svn_cl__get_human_readable_tree_conflict_description( incoming_kind = conflict->src_right_version->node_kind; } - reason = local_reason_str(conflict->node_kind, conflict->reason); + reason = local_reason_str(conflict->node_kind, conflict->reason, + conflict->operation); action = incoming_action_str(incoming_kind, conflict->action); operation = operation_str(conflict->operation); SVN_ERR_ASSERT(operation); @@ -294,6 +337,27 @@ svn_cl__get_human_readable_tree_conflict_description( return SVN_NO_ERROR; } +svn_error_t * +svn_cl__get_human_readable_action_description( + const char **desc, + svn_wc_conflict_action_t action, + svn_wc_operation_t operation, + svn_node_kind_t kind, + apr_pool_t *pool) +{ + const char *action_s, *operation_s; + + action_s = incoming_action_str(kind, action); + operation_s = operation_str(operation); + + SVN_ERR_ASSERT(operation_s); + + *desc = apr_psprintf(pool, _("%s %s"), + action_s, operation_s); + + return SVN_NO_ERROR; +} + /* Helper for svn_cl__append_tree_conflict_info_xml(). * Appends the attributes of the given VERSION to ATT_HASH. diff --git a/contrib/subversion/subversion/svn/cl-conflicts.h b/contrib/subversion/subversion/svn/cl-conflicts.h index 07591a0ef..d4880748c 100644 --- a/contrib/subversion/subversion/svn/cl-conflicts.h +++ b/contrib/subversion/subversion/svn/cl-conflicts.h @@ -63,6 +63,16 @@ svn_cl__get_human_readable_tree_conflict_description( const svn_wc_conflict_description2_t *conflict, apr_pool_t *pool); +/* Like svn_cl__get_human_readable_tree_conflict_description but + for other conflict types */ +svn_error_t * +svn_cl__get_human_readable_action_description( + const char **desc, + svn_wc_conflict_action_t action, + svn_wc_operation_t operation, + svn_node_kind_t kind, + apr_pool_t *pool); + /** * Append to @a str an XML representation of the conflict data * for @a conflict, in a format suitable for 'svn info --xml'. diff --git a/contrib/subversion/subversion/svn/cl-log.h b/contrib/subversion/subversion/svn/cl-log.h new file mode 100644 index 000000000..5b4c7aa7a --- /dev/null +++ b/contrib/subversion/subversion/svn/cl-log.h @@ -0,0 +1,105 @@ +/* + * cl-log.h: Log entry receiver + * + * ==================================================================== + * 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_CL_LOG_H +#define SVN_CL_LOG_H + +/*** Includes. ***/ +#include + +#include "svn_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +/* The separator between log messages. */ +#define SVN_CL__LOG_SEP_STRING \ + "------------------------------------------------------------------------\n" + +/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */ +typedef struct svn_cl__log_receiver_baton +{ + /* Client context. */ + svn_client_ctx_t *ctx; + + /* The target of the log operation. */ + const char *target_path_or_url; + svn_opt_revision_t target_peg_revision; + + /* Don't print log message body nor its line count. */ + svn_boolean_t omit_log_message; + + /* Whether to show diffs in the log. (maps to --diff) */ + svn_boolean_t show_diff; + + /* Depth applied to diff output. */ + svn_depth_t depth; + + /* Diff arguments received from command line. */ + const char *diff_extensions; + + /* Stack which keeps track of merge revision nesting, using svn_revnum_t's */ + apr_array_header_t *merge_stack; + + /* Log message search patterns. Log entries will only be shown if the author, + * the log message, or a changed path matches one of these patterns. */ + apr_array_header_t *search_patterns; + + /* Pool for persistent allocations. */ + apr_pool_t *pool; +} svn_cl__log_receiver_baton; + +/* Implement `svn_log_entry_receiver_t', printing the logs in + * a human-readable and machine-parseable format. + * + * BATON is of type `struct svn_cl__log_receiver_baton'. + * + * First, print a header line. Then if CHANGED_PATHS is non-null, + * print all affected paths in a list headed "Changed paths:\n", + * immediately following the header line. Then print a newline + * followed by the message body, unless BATON->omit_log_message is true. + */ +svn_error_t * +svn_cl__log_entry_receiver(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool); + +/* This implements `svn_log_entry_receiver_t', printing the logs in XML. + * + * BATON is of type `struct svn_cl__log_receiver_baton'. + */ +svn_error_t * +svn_cl__log_entry_receiver_xml(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_CL_LOG_H */ diff --git a/contrib/subversion/subversion/svn/cl.h b/contrib/subversion/subversion/svn/cl.h index 8a732c7e8..42e770e07 100644 --- a/contrib/subversion/subversion/svn/cl.h +++ b/contrib/subversion/subversion/svn/cl.h @@ -158,7 +158,7 @@ typedef struct svn_cl__opt_state_t /* Was --no-unlock specified? */ svn_boolean_t no_unlock; - const char *message; /* log message */ + const char *message; /* log message (not converted to UTF-8) */ svn_boolean_t force; /* be more forceful, as in "svn rm -f ..." */ svn_boolean_t force_log; /* force validity of a suspect log msg file */ svn_boolean_t incremental; /* yield output suitable for concatenation */ @@ -167,20 +167,22 @@ typedef struct svn_cl__opt_state_t svn_boolean_t version; /* print version information */ svn_boolean_t verbose; /* be verbose */ svn_boolean_t update; /* contact the server for the full story */ - svn_boolean_t strict; /* do strictly what was requested */ - svn_stringbuf_t *filedata; /* contents of file used as option data */ - const char *encoding; /* the locale/encoding of the data*/ + svn_stringbuf_t *filedata; /* contents of file used as option data + (not converted to UTF-8) */ + const char *encoding; /* the locale/encoding of 'message' and of + 'filedata' (not converted to UTF-8) */ svn_boolean_t help; /* print usage message */ - const char *auth_username; /* auth username */ /* UTF-8! */ - const char *auth_password; /* auth password */ /* UTF-8! */ - const char *extensions; /* subprocess extension args */ /* UTF-8! */ - apr_array_header_t *targets; /* target list from file */ /* UTF-8! */ + const char *auth_username; /* auth username */ + const char *auth_password; /* auth password */ + const char *extensions; /* subprocess extension args */ + apr_array_header_t *targets; /* target list from file */ svn_boolean_t xml; /* output in xml, e.g., "svn log --xml" */ svn_boolean_t no_ignore; /* disregard default ignores & svn:ignore's */ svn_boolean_t no_auth_cache; /* do not cache authentication information */ struct { - const char *diff_cmd; /* the external diff command to use */ + const char *diff_cmd; /* the external diff command to use + (not converted to UTF-8) */ svn_boolean_t internal_diff; /* override diff_cmd in config file */ svn_boolean_t no_diff_added; /* do not show diffs for deleted files */ svn_boolean_t no_diff_deleted; /* do not show diffs for deleted files */ @@ -197,8 +199,10 @@ typedef struct svn_cl__opt_state_t svn_boolean_t stop_on_copy; /* don't cross copies during processing */ svn_boolean_t dry_run; /* try operation but make no changes */ svn_boolean_t revprop; /* operate on a revision property */ - const char *merge_cmd; /* the external merge command to use */ - const char *editor_cmd; /* the external editor command to use */ + const char *merge_cmd; /* the external merge command to use + (not converted to UTF-8) */ + const char *editor_cmd; /* the external editor command to use + (not converted to UTF-8) */ svn_boolean_t record_only; /* whether to record mergeinfo */ const char *old_target; /* diff target */ const char *new_target; /* diff target */ @@ -210,21 +214,24 @@ typedef struct svn_cl__opt_state_t const char *native_eol; /* override system standard eol marker */ svn_boolean_t remove; /* deassociate a changelist */ apr_array_header_t *changelists; /* changelist filters */ - const char *changelist; /* operate on this changelist - THIS IS TEMPORARY (LAST OF CHANGELISTS) */ svn_boolean_t keep_changelists;/* don't remove changelists after commit */ svn_boolean_t keep_local; /* delete path only from repository */ svn_boolean_t all_revprops; /* retrieve all revprops */ svn_boolean_t no_revprops; /* retrieve no revprops */ - apr_hash_t *revprop_table; /* table of revision properties to get/set */ + apr_hash_t *revprop_table; /* table of revision properties to get/set + (not converted to UTF-8) */ svn_boolean_t parents; /* create intermediate directories */ svn_boolean_t use_merge_history; /* use/display extra merge information */ svn_cl__accept_t accept_which; /* how to handle conflicts */ svn_cl__show_revs_t show_revs; /* mergeinfo flavor */ svn_depth_t set_depth; /* new sticky ambient depth value */ svn_boolean_t reintegrate; /* use "reintegrate" merge-source heuristic */ - svn_boolean_t trust_server_cert; /* trust server SSL certs that would - otherwise be rejected as "untrusted" */ + /* trust server SSL certs that would otherwise be rejected as "untrusted" */ + svn_boolean_t trust_server_cert_unknown_ca; + svn_boolean_t trust_server_cert_cn_mismatch; + svn_boolean_t trust_server_cert_expired; + svn_boolean_t trust_server_cert_not_yet_valid; + svn_boolean_t trust_server_cert_other_failure; int strip; /* number of leading path components to strip */ svn_boolean_t ignore_keywords; /* do not expand keywords */ svn_boolean_t reverse_diff; /* reverse a diff (e.g. when patching) */ @@ -235,6 +242,13 @@ typedef struct svn_cl__opt_state_t svn_boolean_t include_externals; /* Recurses (in)to file & dir externals */ svn_boolean_t show_inherited_props; /* get inherited properties */ apr_array_header_t* search_patterns; /* pattern arguments for --search */ + svn_boolean_t mergeinfo_log; /* show log message in mergeinfo command */ + svn_boolean_t remove_unversioned;/* remove unversioned items */ + svn_boolean_t remove_ignored; /* remove ignored items */ + svn_boolean_t no_newline; /* do not output the trailing newline */ + svn_boolean_t show_passwords; /* show cached passwords */ + svn_boolean_t pin_externals; /* pin externals to last-changed revisions */ + const char *show_item; /* print only the given item */ } svn_cl__opt_state_t; @@ -248,6 +262,7 @@ typedef struct svn_cl__cmd_baton_t /* Declare all the command procedures */ svn_opt_subcommand_t svn_cl__add, + svn_cl__auth, svn_cl__blame, svn_cl__cat, svn_cl__changelist, @@ -310,7 +325,7 @@ extern const apr_getopt_option_t svn_cl__options[]; * * Typically, error codes like SVN_ERR_UNVERSIONED_RESOURCE, * SVN_ERR_ENTRY_NOT_FOUND, etc, are supplied in varargs. Don't - * forget to terminate the argument list with SVN_NO_ERROR. + * forget to terminate the argument list with 0 (or APR_SUCCESS). */ svn_error_t * svn_cl__try(svn_error_t *err, @@ -347,6 +362,14 @@ svn_cl__conflict_stats_resolved(svn_cl__conflict_stats_t *conflict_stats, const char *path_local, svn_wc_conflict_kind_t conflict_kind); +/* Print the conflict stats accumulated in CONFLICT_STATS. + * + * Return any error encountered during printing. + * See also svn_cl__notifier_print_conflict_stats(). + */ +svn_error_t * +svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats, + apr_pool_t *scratch_pool); /* Create and return an baton for use with svn_cl__conflict_func_interactive * in *B, allocated from RESULT_POOL, and initialised with the values @@ -514,7 +537,8 @@ svn_cl__merge_file_externally(const char *base_path, /* Like svn_cl__merge_file_externally, but using a built-in merge tool * with help from an external editor specified by EDITOR_CMD. */ svn_error_t * -svn_cl__merge_file(const char *base_path, +svn_cl__merge_file(svn_boolean_t *remains_in_conflict, + const char *base_path, const char *their_path, const char *my_path, const char *merged_path, @@ -522,7 +546,8 @@ svn_cl__merge_file(const char *base_path, const char *path_prefix, const char *editor_cmd, apr_hash_t *config, - svn_boolean_t *remains_in_conflict, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool); @@ -571,7 +596,9 @@ svn_cl__check_externals_failed_notify_wrapper(void *baton, apr_pool_t *pool); /* Print the conflict stats accumulated in BATON, which is the - * notifier baton from svn_cl__get_notifier(). + * notifier baton from svn_cl__get_notifier(). This is just like + * calling svn_cl__print_conflict_stats(). + * * Return any error encountered during printing. */ svn_error_t * @@ -828,6 +855,48 @@ svn_cl__deprecated_merge_reintegrate(const char *source_path_or_url, svn_client_ctx_t *ctx, apr_pool_t *pool); + +/* Forward declaration of the similarity check context. */ +typedef struct svn_cl__simcheck_context_t svn_cl__simcheck_context_t; + +/* Token definition for the similarity check. */ +typedef struct svn_cl__simcheck_t +{ + /* The token we're checking for similarity. */ + svn_string_t token; + + /* User data associated with this token. */ + const void *data; + + /* + * The following fields are populated by svn_cl__similarity_check. + */ + + /* Similarity score [0..SVN_STRING__SIM_RANGE_MAX] */ + apr_size_t score; + + /* Number of characters of difference from the key. */ + apr_size_t diff; + + /* Similarity check context (private) */ + svn_cl__simcheck_context_t *context; +} svn_cl__simcheck_t; + +/* Find the entries in TOKENS that are most similar to KEY. + * TOKEN_COUNT is the number of entries in the (mutable) TOKENS array. + * Use SCRATCH_POOL for temporary allocations. + * + * On return, the TOKENS array will be sorted according to similarity + * to KEY, in descending order. The return value will be zero if the + * first token is an exact match; otherwise, it will be one more than + * the number of tokens that are at least two-thirds similar to KEY. + */ +apr_size_t +svn_cl__similarity_check(const char *key, + svn_cl__simcheck_t **tokens, + apr_size_t token_count, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/svn/cleanup-cmd.c b/contrib/subversion/subversion/svn/cleanup-cmd.c index 64fa400d7..6b0b62efe 100644 --- a/contrib/subversion/subversion/svn/cleanup-cmd.c +++ b/contrib/subversion/subversion/svn/cleanup-cmd.c @@ -47,7 +47,7 @@ svn_cl__cleanup(apr_getopt_t *os, 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; - apr_pool_t *subpool; + apr_pool_t *iterpool; int i; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, @@ -61,30 +61,60 @@ svn_cl__cleanup(apr_getopt_t *os, SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool)); - subpool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); - svn_error_t *err; + const char *target_abspath; - svn_pool_clear(subpool); + svn_pool_clear(iterpool); SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); - err = svn_client_cleanup(target, ctx, subpool); - if (err && err->apr_err == SVN_ERR_WC_LOCKED) + + SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool)); + + if (opt_state->remove_unversioned || opt_state->remove_ignored) { - const char *target_abspath; - svn_error_t *err2 = svn_dirent_get_absolute(&target_abspath, - target, subpool); - if (err2) - { - err = svn_error_compose_create(err, err2); - } - else + svn_error_t *err = svn_client_vacuum(target_abspath, + opt_state->remove_unversioned, + opt_state->remove_ignored, + TRUE /* fix_timestamps */, + FALSE /* vacuum_pristines */, + opt_state->include_externals, + ctx, iterpool); + + if (err && err->apr_err == SVN_ERR_WC_LOCKED) + err = svn_error_create(SVN_ERR_WC_LOCKED, err, + _("Working copy locked; if no other " + "Subversion client is currently " + "using the working copy, try running " + "'svn cleanup' without the " + "--remove-unversioned and " + "--remove-ignored options first.")); + else if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) + err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Cannot remove unversioned or ignored " + "items from something that is not a " + "working copy")); + + SVN_ERR(err); + } + else + { + svn_error_t *err = svn_client_cleanup2(target_abspath, + TRUE /* break_locks */, + TRUE /* fix_timestamps */, + TRUE /* clear_dav_cache */, + TRUE /* vacuum_pristines */, + opt_state->include_externals, + ctx, iterpool); + + if (err && err->apr_err == SVN_ERR_WC_LOCKED) { const char *wcroot_abspath; + svn_error_t *err2; err2 = svn_client_get_wc_root(&wcroot_abspath, target_abspath, - ctx, subpool, subpool); + ctx, iterpool, iterpool); if (err2) err = svn_error_compose_create(err, err2); else @@ -93,12 +123,12 @@ svn_cl__cleanup(apr_getopt_t *os, "'svn cleanup' on the root of the " "working copy ('%s') instead."), svn_dirent_local_style(wcroot_abspath, - subpool)); + iterpool)); } + SVN_ERR(err); } - SVN_ERR(err); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/client_errors.h b/contrib/subversion/subversion/svn/client_errors.h deleted file mode 100644 index 19f0bdfad..000000000 --- a/contrib/subversion/subversion/svn/client_errors.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * client_errors.h: error codes this command line client features - * - * ==================================================================== - * 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_CLIENT_ERRORS_H -#define SVN_CLIENT_ERRORS_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * This error defining system is copied from and explained in - * ../../include/svn_error_codes.h - */ - -/* Process this file if we're building an error array, or if we have - not defined the enumerated constants yet. */ -#if defined(SVN_ERROR_BUILD_ARRAY) || !defined(SVN_CMDLINE_ERROR_ENUM_DEFINED) - -#if defined(SVN_ERROR_BUILD_ARRAY) - -#error "Need to update err_defn for r1464679 and un-typo 'CDMLINE'" - -#define SVN_ERROR_START \ - static const err_defn error_table[] = { \ - { SVN_ERR_CDMLINE__WARNING, "Warning" }, -#define SVN_ERRDEF(n, s) { n, s }, -#define SVN_ERROR_END { 0, NULL } }; - -#elif !defined(SVN_CMDLINE_ERROR_ENUM_DEFINED) - -#define SVN_ERROR_START \ - typedef enum svn_client_errno_t { \ - SVN_ERR_CDMLINE__WARNING = SVN_ERR_LAST + 1, -#define SVN_ERRDEF(n, s) n, -#define SVN_ERROR_END SVN_ERR_CMDLINE__ERR_LAST } svn_client_errno_t; - -#define SVN_CMDLINE_ERROR_ENUM_DEFINED - -#endif - -/* Define custom command line client error numbers */ - -SVN_ERROR_START - - /* BEGIN Client errors */ - -SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_WRITE, - "Failed writing to temporary file.") - - SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_STAT, - "Failed getting info about temporary file.") - - SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_OPEN, - "Failed opening temporary file.") - - /* END Client errors */ - - -SVN_ERROR_END - -#undef SVN_ERROR_START -#undef SVN_ERRDEF -#undef SVN_ERROR_END - -#endif /* SVN_ERROR_BUILD_ARRAY || !SVN_CMDLINE_ERROR_ENUM_DEFINED */ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SVN_CLIENT_ERRORS_H */ diff --git a/contrib/subversion/subversion/svn/commit-cmd.c b/contrib/subversion/subversion/svn/commit-cmd.c index 2d04c6971..45eef3b98 100644 --- a/contrib/subversion/subversion/svn/commit-cmd.c +++ b/contrib/subversion/subversion/svn/commit-cmd.c @@ -137,7 +137,9 @@ svn_cl__commit(apr_getopt_t *os, if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_infinity; - cfg = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG); + cfg = ctx->config + ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) + : NULL; if (cfg) SVN_ERR(svn_config_get_bool(cfg, &no_unlock, SVN_CONFIG_SECTION_MISCELLANY, diff --git a/contrib/subversion/subversion/svn/conflict-callbacks.c b/contrib/subversion/subversion/svn/conflict-callbacks.c index 0f12413d2..a9cb39a2a 100644 --- a/contrib/subversion/subversion/svn/conflict-callbacks.c +++ b/contrib/subversion/subversion/svn/conflict-callbacks.c @@ -44,6 +44,9 @@ #include "svn_private_config.h" #define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0]))) +#define MAX_ARRAY_LEN(aryx, aryz) \ + (ARRAY_LEN((aryx)) > ARRAY_LEN((aryz)) \ + ? ARRAY_LEN((aryx)) : ARRAY_LEN((aryz))) @@ -56,6 +59,7 @@ struct svn_cl__interactive_conflict_baton_t { const char *path_prefix; svn_boolean_t quit; svn_cl__conflict_stats_t *conflict_stats; + svn_boolean_t printed_summary; }; svn_error_t * @@ -82,6 +86,7 @@ svn_cl__get_conflict_func_interactive_baton( SVN_ERR(svn_dirent_get_absolute(&(*b)->path_prefix, "", result_pool)); (*b)->quit = FALSE; (*b)->conflict_stats = conflict_stats; + (*b)->printed_summary = FALSE; return SVN_NO_ERROR; } @@ -127,6 +132,8 @@ svn_cl__accept_from_word(const char *word) static svn_error_t * show_diff(const svn_wc_conflict_description2_t *desc, const char *path_prefix, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { const char *path1, *path2; @@ -184,11 +191,14 @@ show_diff(const svn_wc_conflict_description2_t *desc, 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, + return svn_diff_file_output_unified4(output, diff, path1, path2, label1, label2, APR_LOCALE_CHARSET, - NULL, FALSE, + NULL, + options->show_c_function, + options->context_size, + cancel_func, cancel_baton, pool); } @@ -197,6 +207,8 @@ show_diff(const svn_wc_conflict_description2_t *desc, * and 'my' files of DESC. */ static svn_error_t * show_conflicts(const svn_wc_conflict_description2_t *desc, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { svn_diff_t *diff; @@ -213,7 +225,7 @@ show_conflicts(const svn_wc_conflict_description2_t *desc, options, pool)); /* ### Consider putting the markers/labels from ### svn_wc__merge_internal in the conflict description. */ - return svn_diff_file_output_merge2(output, diff, + return svn_diff_file_output_merge3(output, diff, desc->base_abspath, desc->my_abspath, desc->their_abspath, @@ -222,6 +234,8 @@ show_conflicts(const svn_wc_conflict_description2_t *desc, _(">>>>>>> THEIRS (select with 'tc')"), "=======", svn_diff_conflict_display_only_conflicts, + cancel_func, + cancel_baton, pool); } @@ -237,6 +251,8 @@ static svn_error_t * merge_prop_conflict(svn_stream_t *output, const svn_wc_conflict_description2_t *desc, const char *merged_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { const char *base_abspath = desc->base_abspath; @@ -268,7 +284,7 @@ merge_prop_conflict(svn_stream_t *output, merged_abspath ? merged_abspath : my_abspath, their_abspath, options, pool)); - SVN_ERR(svn_diff_file_output_merge2(output, diff, + SVN_ERR(svn_diff_file_output_merge3(output, diff, base_abspath, merged_abspath ? merged_abspath : my_abspath, @@ -278,6 +294,8 @@ merge_prop_conflict(svn_stream_t *output, _(">>>>>>> THEIRS"), "=======", svn_diff_conflict_display_modified_original_latest, + cancel_func, + cancel_baton, pool)); return SVN_NO_ERROR; @@ -293,12 +311,15 @@ merge_prop_conflict(svn_stream_t *output, static svn_error_t * show_prop_conflict(const svn_wc_conflict_description2_t *desc, const char *merged_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, 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)); + SVN_ERR(merge_prop_conflict(output, desc, merged_abspath, + cancel_func, cancel_baton, pool)); return SVN_NO_ERROR; } @@ -324,22 +345,14 @@ open_editor(svn_boolean_t *performed_edit, { 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)) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { - svn_error_t *root_err = svn_error_root_cause(err); + char buf[1024]; + const char *message; - 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."))); + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", message)); svn_error_clear(err); } else if (err) @@ -375,60 +388,18 @@ edit_prop_conflict(const char **merged_file_path, 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(merge_prop_conflict(merged_prop, desc, NULL, + b->pb->cancel_func, + b->pb->cancel_baton, + scratch_pool)); SVN_ERR(svn_stream_close(merged_prop)); - SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); + SVN_ERR(svn_io_file_flush(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 @@ -438,7 +409,8 @@ 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 */ + svn_wc_conflict_choice_t choice; + /* or ..._undefined if not a simple choice */ } resolver_option_t; /* Resolver options for a text conflict */ @@ -450,14 +422,15 @@ static const resolver_option_t text_conflict_options[] = brackets. */ { "e", N_("edit file"), N_("change merged file in an editor" " [edit]"), - -1 }, + svn_wc_conflict_choose_undefined }, { "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_undefined }, + { "r", N_("mark resolved"), N_("accept merged version of file [working]"), svn_wc_conflict_choose_merged }, { "", "", "", svn_wc_conflict_choose_unspecified }, { "dc", N_("display conflict"), N_("show all conflicts " - "(ignoring merged version)"), -1 }, + "(ignoring merged version)"), + svn_wc_conflict_choose_undefined }, { "mc", N_("my side of conflict"), N_("accept my version for all conflicts " "(same) [mine-conflict]"), svn_wc_conflict_choose_mine_conflict }, @@ -473,16 +446,43 @@ static const resolver_option_t text_conflict_options[] = "(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 }, + { "m", N_("merge"), N_("use merge tool to resolve conflict"), + svn_wc_conflict_choose_undefined }, + { "l", N_("launch tool"), N_("launch external merge tool to resolve " + "conflict [launch]"), + svn_wc_conflict_choose_undefined }, + { "i", N_("internal merge tool"), N_("use built-in merge tool to " + "resolve conflict"), + svn_wc_conflict_choose_undefined }, { "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 }, + { "s", N_("show all options"), N_("show this list (also 'h', '?')"), + svn_wc_conflict_choose_undefined }, + { NULL } +}; + +/* Resolver options for a binary file conflict. */ +static const resolver_option_t binary_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. */ + { "r", N_("mark resolved"), N_("accept the working copy version of file " + " [working]"), + svn_wc_conflict_choose_merged }, + { "tf", N_("their version"), N_("accept the incoming version of file " + " [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 }, + { "s", N_("show all options"), N_("show this list (also 'h', '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; @@ -495,9 +495,11 @@ static const resolver_option_t prop_conflict_options[] = { "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 }, + { "dc", N_("display conflict"), N_("show conflicts in this property"), + svn_wc_conflict_choose_undefined }, { "e", N_("edit property"), N_("change merged property value in an editor" - " [edit]"), -1 }, + " [edit]"), + svn_wc_conflict_choose_undefined }, { "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" @@ -505,7 +507,8 @@ static const resolver_option_t prop_conflict_options[] = 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 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; @@ -518,31 +521,14 @@ static const resolver_option_t tree_conflict_options[] = 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 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { 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"), + { "mc", N_("apply update to move destination (recommended)"), N_("apply incoming update to move destination" " [mine-conflict]"), svn_wc_conflict_choose_mine_conflict }, @@ -550,43 +536,26 @@ static const resolver_option_t tree_conflict_options_update_edit_moved_away[] = 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 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; -static const resolver_option_t tree_conflict_options_update_replaced[] = +static const resolver_option_t tree_conflict_options_update_edit_deleted_dir[] = { - { "mc", N_("keep affected local moves"), N_("keep any moves affected by this " - "replacement [mine-conflict]"), + { "mc", N_("prepare for updating moved-away children, if any (recommended)"), + N_("allow updating moved-away children " + "with 'svn resolve' [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 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { 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 * @@ -637,21 +606,21 @@ prompt_string(const resolver_option_t *options, } if (! first) - result = apr_pstrcat(pool, result, ",", (char *)NULL); + result = apr_pstrcat(pool, result, ",", SVN_VA_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); + result = apr_pstrcat(pool, result, line_sep, SVN_VA_NULL); this_line_len = left_margin; } - result = apr_pstrcat(pool, result, s, (char *)NULL); + result = apr_pstrcat(pool, result, s, SVN_VA_NULL); this_line_len += slen; first = FALSE; } - return apr_pstrcat(pool, result, ": ", (char *)NULL); + return apr_pstrcat(pool, result, ": ", SVN_VA_NULL); } /* Return a help string listing the OPTIONS. */ @@ -674,13 +643,13 @@ help_string(const resolver_option_t *options, } else { - result = apr_pstrcat(pool, result, "\n", (char *)NULL); + result = apr_pstrcat(pool, result, "\n", SVN_VA_NULL); } } result = apr_pstrcat(pool, result, _("Words in square brackets are the corresponding " "--accept option arguments.\n"), - (char *)NULL); + SVN_VA_NULL); return result; } @@ -736,31 +705,50 @@ handle_text_conflict(svn_wc_conflict_result_t *result, 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)? */ + file (so that we need to save a .edited copy by setting the + result->save_merge flag)? */ 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; + const char *local_relpath; 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))); + local_relpath = svn_cl__local_style_skip_ancestor(b->path_prefix, + desc->local_abspath, + scratch_pool);; + + if (desc->is_binary) + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, + _("Conflict discovered in binary file '%s'.\n"), + local_relpath)); + else + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, + _("Conflict discovered in file '%s'.\n"), + local_relpath)); + + /* ### TODO This whole feature availability check is grossly outdated. + DIFF_ALLOWED needs either to be redefined or to go away. + */ /* 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)) + if (!desc->is_binary && + ((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 *options[1 + MAX_ARRAY_LEN(binary_conflict_options, + text_conflict_options)]; + + const resolver_option_t *conflict_options = desc->is_binary + ? binary_conflict_options + : text_conflict_options; const char **next_option = options; const resolver_option_t *opt; @@ -769,31 +757,39 @@ handle_text_conflict(svn_wc_conflict_result_t *result, *next_option++ = "p"; if (diff_allowed) { - *next_option++ = "df"; + /* We need one more path for this feature. */ + if (desc->my_abspath) + *next_option++ = "df"; + *next_option++ = "e"; - *next_option++ = "m"; + + /* We need one more path for this feature. */ + if (desc->my_abspath) + *next_option++ = "m"; if (knows_something) *next_option++ = "r"; - if (! desc->is_binary) - { - *next_option++ = "mc"; - *next_option++ = "tc"; - } + *next_option++ = "mc"; + *next_option++ = "tc"; } else { - if (knows_something) + if (knows_something || desc->is_binary) *next_option++ = "r"; - *next_option++ = "mf"; + + /* The 'mine-full' option selects the ".mine" file so only offer + * it if that file exists. It does not exist for binary files, + * for example (questionable historical behaviour since 1.0). */ + if (desc->my_abspath) + *next_option++ = "mf"; + *next_option++ = "tf"; } *next_option++ = "s"; *next_option++ = NULL; - SVN_ERR(prompt_user(&opt, text_conflict_options, options, b->pb, - iterpool)); + SVN_ERR(prompt_user(&opt, conflict_options, options, b->pb, iterpool)); if (! opt) continue; @@ -807,7 +803,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result, else if (strcmp(opt->code, "s") == 0) { SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n", - help_string(text_conflict_options, + help_string(conflict_options, iterpool))); } else if (strcmp(opt->code, "dc") == 0) @@ -828,12 +824,16 @@ handle_text_conflict(svn_wc_conflict_result_t *result, "files not available.\n\n"))); continue; } - SVN_ERR(show_conflicts(desc, iterpool)); + SVN_ERR(show_conflicts(desc, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); knows_something = TRUE; } else if (strcmp(opt->code, "df") == 0) { - if (! diff_allowed) + /* Re-check preconditions. */ + if (! diff_allowed || ! desc->my_abspath) { SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, _("Invalid option; there's no " @@ -841,7 +841,9 @@ handle_text_conflict(svn_wc_conflict_result_t *result, continue; } - SVN_ERR(show_diff(desc, b->path_prefix, iterpool)); + SVN_ERR(show_diff(desc, b->path_prefix, + b->pb->cancel_func, b->pb->cancel_baton, + iterpool)); knows_something = TRUE; } else if (strcmp(opt->code, "e") == 0 || strcmp(opt->code, ":-E") == 0) @@ -853,36 +855,68 @@ handle_text_conflict(svn_wc_conflict_result_t *result, 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_error_t *err; + + /* Re-check preconditions. */ + if (! desc->my_abspath) { SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, - _("Invalid option; can only " - "resolve text conflicts with " - "the internal merge tool." - "\n\n"))); + _("Invalid option; there's no " + "base path to merge.\n\n"))); continue; } - if (desc->base_abspath && desc->their_abspath && - desc->my_abspath && desc->merged_file) + err = svn_cl__merge_file_externally(desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, b->config, + NULL, iterpool); + if (err) { - 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; + if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL) + { + svn_boolean_t remains_in_conflict = TRUE; + + /* Try the internal merge tool. */ + svn_error_clear(err); + SVN_ERR(svn_cl__merge_file(&remains_in_conflict, + desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->path_prefix, + b->editor_cmd, + b->config, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); + knows_something = !remains_in_conflict; + } + else if (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) + { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, + "%s\n", message)); + svn_error_clear(err); + continue; + } + else + return svn_error_trace(err); } else - SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, - _("Invalid option.\n\n"))); + { + /* The external merge tool's exit code was either 0 or 1. + * The tool may leave the file conflicted by exiting with + * exit code 1, and we allow the user to mark the conflict + * resolved in this case. */ + performed_edit = TRUE; + knows_something = TRUE; + } } else if (strcmp(opt->code, "l") == 0 || strcmp(opt->code, ":-l") == 0) { @@ -893,7 +927,29 @@ handle_text_conflict(svn_wc_conflict_result_t *result, if (desc->base_abspath && desc->their_abspath && desc->my_abspath && desc->merged_file) { - SVN_ERR(launch_resolver(&performed_edit, desc, b, iterpool)); + svn_error_t *err; + char buf[1024]; + const char *message; + + err = svn_cl__merge_file_externally(desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->config, NULL, iterpool); + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) + { + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", + message)); + svn_error_clear(err); + } + else if (err) + return svn_error_trace(err); + else + performed_edit = TRUE; + if (performed_edit) knows_something = TRUE; } @@ -901,7 +957,27 @@ handle_text_conflict(svn_wc_conflict_result_t *result, SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, _("Invalid option.\n\n"))); } - else if (opt->choice != -1) + else if (strcmp(opt->code, "i") == 0) + { + svn_boolean_t remains_in_conflict = TRUE; + + SVN_ERR(svn_cl__merge_file(&remains_in_conflict, + desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->path_prefix, + b->editor_cmd, + b->config, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); + + if (!remains_in_conflict) + knows_something = TRUE; + } + else if (opt->choice != svn_wc_conflict_choose_undefined) { if ((opt->choice == svn_wc_conflict_choose_mine_conflict || opt->choice == svn_wc_conflict_choose_theirs_conflict) @@ -918,7 +994,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result, the file if they've edited it, or at least looked at the diff. */ if (opt->choice == svn_wc_conflict_choose_merged - && ! knows_something) + && ! knows_something && diff_allowed) { SVN_ERR(svn_cmdline_fprintf( stderr, iterpool, @@ -1008,7 +1084,9 @@ handle_prop_conflict(svn_wc_conflict_result_t *result, } else if (strcmp(opt->code, "dc") == 0) { - SVN_ERR(show_prop_conflict(desc, merged_file_path, scratch_pool)); + SVN_ERR(show_prop_conflict(desc, merged_file_path, + b->pb->cancel_func, b->pb->cancel_baton, + scratch_pool)); } else if (strcmp(opt->code, "e") == 0) { @@ -1030,7 +1108,7 @@ handle_prop_conflict(svn_wc_conflict_result_t *result, result->choice = svn_wc_conflict_choose_merged; break; } - else if (opt->choice != -1) + else if (opt->choice != svn_wc_conflict_choose_undefined) { result->choice = opt->choice; break; @@ -1072,25 +1150,23 @@ handle_tree_conflict(svn_wc_conflict_result_t *result, svn_pool_clear(iterpool); + tc_opts = tree_conflict_options; + 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; + tc_opts = tree_conflict_options_update_moved_away; + } + else if (desc->reason == svn_wc_conflict_reason_deleted || + desc->reason == svn_wc_conflict_reason_replaced) + { + if (desc->action == svn_wc_conflict_action_edit && + desc->node_kind == svn_node_dir) + tc_opts = tree_conflict_options_update_edit_deleted_dir; } - 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) @@ -1103,7 +1179,7 @@ handle_tree_conflict(svn_wc_conflict_result_t *result, b->quit = TRUE; break; } - else if (opt->choice != -1) + else if (opt->choice != svn_wc_conflict_choose_undefined) { result->choice = opt->choice; break; @@ -1172,21 +1248,15 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, 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)) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("Error running editor;" - " leaving all conflicts."))); + message)); svn_error_clear(err); b->external_failed = TRUE; } @@ -1217,21 +1287,15 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, 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) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("Error running merge tool;" - " leaving all conflicts."))); + message)); b->external_failed = TRUE; return svn_error_trace(err); } @@ -1248,6 +1312,13 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, break; } + /* Print a summary of conflicts before starting interactive resolution */ + if (! b->printed_summary) + { + SVN_ERR(svn_cl__print_conflict_stats(b->conflict_stats, scratch_pool)); + b->printed_summary = TRUE; + } + /* We're in interactive mode and either the user gave no --accept option or the option did not apply; let's prompt. */ @@ -1293,7 +1364,7 @@ svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result, b->path_prefix, desc->local_abspath, scratch_pool); svn_cl__conflict_stats_resolved(b->conflict_stats, local_path, - desc->kind); + desc->kind); } return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/copy-cmd.c b/contrib/subversion/subversion/svn/copy-cmd.c index e6fbd4b19..5b50713f9 100644 --- a/contrib/subversion/subversion/svn/copy-cmd.c +++ b/contrib/subversion/subversion/svn/copy-cmd.c @@ -150,8 +150,7 @@ svn_cl__copy(apr_getopt_t *os, } else { - /* URL -> URL, meaning that no notification is needed. */ - ctx->notify_func2 = NULL; + /* URL -> URL */ } if (! dst_is_url) @@ -168,8 +167,11 @@ svn_cl__copy(apr_getopt_t *os, SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state, NULL, ctx->config, pool)); - err = svn_client_copy6(sources, dst_path, TRUE, + err = svn_client_copy7(sources, dst_path, TRUE, opt_state->parents, opt_state->ignore_externals, + FALSE /* metadata_only */, + opt_state->pin_externals, + NULL, /* pin all externals */ opt_state->revprop_table, (opt_state->quiet ? NULL : svn_cl__print_commit_info), NULL, diff --git a/contrib/subversion/subversion/svn/diff-cmd.c b/contrib/subversion/subversion/svn/diff-cmd.c index 2cbd202e3..71853c7f2 100644 --- a/contrib/subversion/subversion/svn/diff-cmd.c +++ b/contrib/subversion/subversion/svn/diff-cmd.c @@ -37,6 +37,7 @@ #include "svn_types.h" #include "svn_cmdline.h" #include "svn_xml.h" +#include "svn_hash.h" #include "cl.h" #include "svn_private_config.h" @@ -81,6 +82,7 @@ kind_to_word(svn_client_diff_summarize_kind_t kind) struct summarize_baton_t { const char *anchor; + svn_boolean_t ignore_properties; }; /* Print summary information about a given change as XML, implements the @@ -97,6 +99,11 @@ summarize_xml(const svn_client_diff_summarize_t *summary, * baton, and appending the target's relative path. */ const char *path = b->anchor; svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); + const char *prop_change; + + if (b->ignore_properties && + summary->summarize_kind == svn_client_diff_summarize_kind_normal) + return SVN_NO_ERROR; /* Tack on the target path, so we can differentiate between different parts * of the output when we're given multiple targets. */ @@ -113,11 +120,15 @@ summarize_xml(const svn_client_diff_summarize_t *summary, path = svn_dirent_local_style(path, pool); } + prop_change = summary->prop_changed ? "modified" : "none"; + if (b->ignore_properties) + prop_change = "none"; + svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path", "kind", svn_cl__node_kind_str_xml(summary->node_kind), "item", kind_to_word(summary->summarize_kind), - "props", summary->prop_changed ? "modified" : "none", - NULL); + "props", prop_change, + SVN_VA_NULL); svn_xml_escape_cdata_cstring(&sb, path, pool); svn_xml_make_close_tag(&sb, pool, "path"); @@ -134,6 +145,11 @@ summarize_regular(const svn_client_diff_summarize_t *summary, { struct summarize_baton_t *b = baton; const char *path = b->anchor; + char prop_change; + + if (b->ignore_properties && + summary->summarize_kind == svn_client_diff_summarize_kind_normal) + return SVN_NO_ERROR; /* Tack on the target path, so we can differentiate between different parts * of the output when we're given multiple targets. */ @@ -154,11 +170,13 @@ summarize_regular(const svn_client_diff_summarize_t *summary, * thus the blank spaces where information that is not relevant to * a diff summary would go. */ - SVN_ERR(svn_cmdline_printf(pool, - "%c%c %s\n", + prop_change = summary->prop_changed ? 'M' : ' '; + if (b->ignore_properties) + prop_change = ' '; + + SVN_ERR(svn_cmdline_printf(pool, "%c%c %s\n", kind_to_char(summary->summarize_kind), - summary->prop_changed ? 'M' : ' ', - path)); + prop_change, path)); return svn_cmdline_fflush(stdout); } @@ -179,6 +197,7 @@ svn_cl__diff(apr_getopt_t *os, const char *old_target, *new_target; apr_pool_t *iterpool; svn_boolean_t pegged_diff = FALSE; + svn_boolean_t ignore_content_type; svn_boolean_t show_copies_as_adds = opt_state->diff.patch_compatible || opt_state->diff.show_copies_as_adds; svn_boolean_t ignore_properties = @@ -211,7 +230,7 @@ svn_cl__diff(apr_getopt_t *os, SVN_ERR(svn_cl__xml_print_header("diff", pool)); sb = svn_stringbuf_create_empty(pool); - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths", SVN_VA_NULL); SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); } @@ -337,6 +356,25 @@ svn_cl__diff(apr_getopt_t *os, } + /* Should we ignore the content-type when deciding what to diff? */ + if (opt_state->force) + { + ignore_content_type = TRUE; + } + else if (ctx->config) + { + SVN_ERR(svn_config_get_bool(svn_hash_gets(ctx->config, + SVN_CONFIG_CATEGORY_CONFIG), + &ignore_content_type, + SVN_CONFIG_SECTION_MISCELLANY, + SVN_CONFIG_OPTION_DIFF_IGNORE_CONTENT_TYPE, + FALSE)); + } + else + { + ignore_content_type = FALSE; + } + svn_opt_push_implicit_dot_target(targets, pool); iterpool = svn_pool_create(pool); @@ -374,6 +412,7 @@ svn_cl__diff(apr_getopt_t *os, if (opt_state->diff.summarize) { summarize_baton.anchor = target1; + summarize_baton.ignore_properties = ignore_properties; SVN_ERR(svn_client_diff_summarize2( target1, @@ -399,7 +438,7 @@ svn_cl__diff(apr_getopt_t *os, opt_state->diff.no_diff_added, opt_state->diff.no_diff_deleted, show_copies_as_adds, - opt_state->force, + ignore_content_type, ignore_properties, opt_state->diff.properties_only, opt_state->diff.use_git_diff_format, @@ -426,6 +465,7 @@ svn_cl__diff(apr_getopt_t *os, if (opt_state->diff.summarize) { summarize_baton.anchor = truepath; + summarize_baton.ignore_properties = ignore_properties; SVN_ERR(svn_client_diff_summarize_peg2( truepath, &peg_revision, @@ -450,7 +490,7 @@ svn_cl__diff(apr_getopt_t *os, opt_state->diff.no_diff_added, opt_state->diff.no_diff_deleted, show_copies_as_adds, - opt_state->force, + ignore_content_type, ignore_properties, opt_state->diff.properties_only, opt_state->diff.use_git_diff_format, diff --git a/contrib/subversion/subversion/svn/export-cmd.c b/contrib/subversion/subversion/svn/export-cmd.c index 75b6723a3..45554fa4c 100644 --- a/contrib/subversion/subversion/svn/export-cmd.c +++ b/contrib/subversion/subversion/svn/export-cmd.c @@ -85,7 +85,15 @@ svn_cl__export(apr_getopt_t *os, if (strcmp("", to) != 0) /* svn_cl__eat_peg_revisions() but only on one target */ - SVN_ERR(svn_opt__split_arg_at_peg_revision(&to, NULL, to, pool)); + { + const char *peg; + + SVN_ERR(svn_opt__split_arg_at_peg_revision(&to, &peg, to, pool)); + if (peg[0] && peg[1]) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("'%s': a peg revision is not allowed here"), + APR_ARRAY_IDX(targets, 1, const char *)); + } } SVN_ERR(svn_cl__check_target_is_local_path(to)); diff --git a/contrib/subversion/subversion/svn/file-merge.c b/contrib/subversion/subversion/svn/file-merge.c index c64f5772d..14017db29 100644 --- a/contrib/subversion/subversion/svn/file-merge.c +++ b/contrib/subversion/subversion/svn/file-merge.c @@ -51,6 +51,10 @@ #include #include +#if defined(HAVE_TERMIOS_H) +#include +#endif + /* Baton for functions in this file which implement svn_diff_output_fns_t. */ struct file_merge_baton { /* The files being merged. */ @@ -75,12 +79,12 @@ struct file_merge_baton { /* The client configuration hash. */ apr_hash_t *config; - /* Wether the merge should be aborted. */ + /* Whether the merge should be aborted. */ svn_boolean_t abort_merge; /* Pool for temporary allocations. */ apr_pool_t *scratch_pool; -} file_merge_baton; +}; /* Copy LEN lines from SOURCE_FILE to the MERGED_FILE, starting at * line START. The CURRENT_LINE is the current line in the source file. @@ -490,7 +494,7 @@ edit_chunk(apr_array_header_t **merged_chunk, return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, _("Could not write data to temporary file")); } - SVN_ERR(svn_io_file_flush_to_disk(temp_file, scratch_pool)); + SVN_ERR(svn_io_file_flush(temp_file, scratch_pool)); err = svn_cmdline__edit_file_externally(temp_file_name, editor_cmd, config, scratch_pool); @@ -853,7 +857,8 @@ static svn_diff_output_fns_t file_merge_diff_output_fns = { }; svn_error_t * -svn_cl__merge_file(const char *base_path, +svn_cl__merge_file(svn_boolean_t *remains_in_conflict, + const char *base_path, const char *their_path, const char *my_path, const char *merged_path, @@ -861,7 +866,8 @@ svn_cl__merge_file(const char *base_path, const char *path_prefix, const char *editor_cmd, apr_hash_t *config, - svn_boolean_t *remains_in_conflict, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { svn_diff_t *diff; @@ -918,7 +924,8 @@ svn_cl__merge_file(const char *base_path, fmb.abort_merge = FALSE; fmb.scratch_pool = scratch_pool; - SVN_ERR(svn_diff_output(diff, &fmb, &file_merge_diff_output_fns)); + SVN_ERR(svn_diff_output2(diff, &fmb, &file_merge_diff_output_fns, + cancel_func, cancel_baton)); SVN_ERR(svn_io_file_close(original_file, scratch_pool)); SVN_ERR(svn_io_file_close(modified_file, scratch_pool)); diff --git a/contrib/subversion/subversion/svn/help-cmd.c b/contrib/subversion/subversion/svn/help-cmd.c index 93fecc322..b095d30fd 100644 --- a/contrib/subversion/subversion/svn/help-cmd.c +++ b/contrib/subversion/subversion/svn/help-cmd.c @@ -30,8 +30,8 @@ #include "svn_hash.h" #include "svn_string.h" #include "svn_config.h" +#include "svn_dirent_uri.h" #include "svn_error.h" -#include "svn_version.h" #include "cl.h" #include "svn_private_config.h" @@ -47,11 +47,11 @@ svn_cl__help(apr_getopt_t *os, { svn_cl__opt_state_t *opt_state = NULL; svn_stringbuf_t *version_footer = NULL; + const char *config_path; - /* xgettext: the %s is for SVN_VER_NUMBER. */ - char help_header_template[] = + char help_header[] = N_("usage: svn [options] [args]\n" - "Subversion command-line client, version %s.\n" + "Subversion command-line client.\n" "Type 'svn help ' for help on a specific subcommand.\n" "Type 'svn --version' to see the program version and RA modules\n" " or 'svn --version --quiet' to see just the version number.\n" @@ -66,9 +66,6 @@ svn_cl__help(apr_getopt_t *os, N_("Subversion is a tool for version control.\n" "For additional information, see http://subversion.apache.org/\n"); - char *help_header = - apr_psprintf(pool, _(help_header_template), SVN_VER_NUMBER); - const char *ra_desc_start = _("The following repository access (RA) modules are available:\n\n"); @@ -138,6 +135,46 @@ svn_cl__help(apr_getopt_t *os, version_footer = svn_stringbuf_create(ra_desc_start, pool); SVN_ERR(svn_ra_print_modules(version_footer, pool)); + /* + * Show auth creds storage providers. + */ + SVN_ERR(svn_config_get_user_config_path(&config_path, + opt_state ? opt_state->config_dir + : NULL, + NULL, + pool)); + svn_stringbuf_appendcstr(version_footer, + _("\nThe following authentication credential caches are available:\n\n")); + + /*### There is no API to query available providers at run time. */ +#if (defined(WIN32) && !defined(__MINGW32__)) + version_footer = + svn_stringbuf_create(apr_psprintf(pool, _("%s* Wincrypt cache in %s\n"), + version_footer->data, + svn_dirent_local_style(config_path, + pool)), + pool); +#elif !defined(SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE) + version_footer = + svn_stringbuf_create(apr_psprintf(pool, _("%s* Plaintext cache in %s\n"), + version_footer->data, + svn_dirent_local_style(config_path, + pool)), + pool); +#endif +#ifdef SVN_HAVE_GNOME_KEYRING + svn_stringbuf_appendcstr(version_footer, "* Gnome Keyring\n"); +#endif +#ifdef SVN_HAVE_GPG_AGENT + svn_stringbuf_appendcstr(version_footer, "* GPG-Agent\n"); +#endif +#ifdef SVN_HAVE_KEYCHAIN_SERVICES + svn_stringbuf_appendcstr(version_footer, "* Mac OS X Keychain\n"); +#endif +#ifdef SVN_HAVE_KWALLET + svn_stringbuf_appendcstr(version_footer, "* KWallet (KDE)\n"); +#endif + return svn_opt_print_help4(os, "svn", /* ### erm, derive somehow? */ opt_state ? opt_state->version : FALSE, diff --git a/contrib/subversion/subversion/svn/info-cmd.c b/contrib/subversion/subversion/svn/info-cmd.c index 56833f6ed..004098620 100644 --- a/contrib/subversion/subversion/svn/info-cmd.c +++ b/contrib/subversion/subversion/svn/info-cmd.c @@ -76,6 +76,168 @@ schedule_str(svn_wc_schedule_t schedule) } } +/* Return a relative URL from information in INFO using POOL for + temporary allocation. */ +static const char* +relative_url(const svn_client_info2_t *info, apr_pool_t *pool) +{ + return apr_pstrcat(pool, "^/", + svn_path_uri_encode( + svn_uri_skip_ancestor(info->repos_root_URL, + info->URL, pool), + pool), SVN_VA_NULL); +} + + +/* The kinds of items for print_info_item(). */ +typedef enum +{ + /* Entry kind */ + info_item_kind, + + /* Repository location. */ + info_item_url, + info_item_relative_url, + info_item_repos_root_url, + info_item_repos_uuid, + + /* Working copy revision or repository HEAD revision */ + info_item_revision, + + /* Commit details. */ + info_item_last_changed_rev, + info_item_last_changed_date, + info_item_last_changed_author, + + /* Working copy information */ + info_item_wc_root +} info_item_t; + +/* Mapping between option keywords and info_item_t. */ +typedef struct info_item_map_t +{ + const svn_string_t keyword; + const info_item_t print_what; +} info_item_map_t; + +#define MAKE_STRING(x) { x, sizeof(x) - 1 } +static const info_item_map_t info_item_map[] = + { + { MAKE_STRING("kind"), info_item_kind }, + { MAKE_STRING("url"), info_item_url }, + { MAKE_STRING("relative-url"), info_item_relative_url }, + { MAKE_STRING("repos-root-url"), info_item_repos_root_url }, + { MAKE_STRING("repos-uuid"), info_item_repos_uuid }, + { MAKE_STRING("revision"), info_item_revision }, + { MAKE_STRING("last-changed-revision"), + info_item_last_changed_rev }, + { MAKE_STRING("last-changed-date"), info_item_last_changed_date }, + { MAKE_STRING("last-changed-author"), info_item_last_changed_author }, + { MAKE_STRING("wc-root"), info_item_wc_root } + }; +#undef MAKE_STRING + +static const apr_size_t info_item_map_len = + (sizeof(info_item_map) / sizeof(info_item_map[0])); + + +/* The baton type used by the info receiver functions. */ +typedef struct print_info_baton_t +{ + /* The path prefix that output paths should be normalized to. */ + const char *path_prefix; + + /* + * The following fields are used by print_info_item(). + */ + + /* Which item to print. */ + info_item_t print_what; + + /* Do we expect to show info for multiple targets? */ + svn_boolean_t multiple_targets; + + /* TRUE iff the current is a local path. */ + svn_boolean_t target_is_path; + + /* Did we already print a line of output? */ + svn_boolean_t start_new_line; +} print_info_baton_t; + + +/* Find the appropriate info_item_t for KEYWORD and initialize + * RECEIVER_BATON for print_info_item(). Use SCRATCH_POOL for + * temporary allocation. + */ +static svn_error_t * +find_print_what(const char *keyword, + print_info_baton_t *receiver_baton, + apr_pool_t *scratch_pool) +{ + svn_cl__simcheck_t **keywords = apr_palloc( + scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t*)); + svn_cl__simcheck_t *kwbuf = apr_palloc( + scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t)); + apr_size_t i; + + for (i = 0; i < info_item_map_len; ++i) + { + keywords[i] = &kwbuf[i]; + kwbuf[i].token.data = info_item_map[i].keyword.data; + kwbuf[i].token.len = info_item_map[i].keyword.len; + kwbuf[i].data = &info_item_map[i]; + } + + switch (svn_cl__similarity_check(keyword, keywords, + info_item_map_len, scratch_pool)) + { + const info_item_map_t *kw0; + const info_item_map_t *kw1; + const info_item_map_t *kw2; + + case 0: /* Exact match. */ + kw0 = keywords[0]->data; + receiver_baton->print_what = kw0->print_what; + return SVN_NO_ERROR; + + case 1: + /* The best alternative isn't good enough */ + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item"), + keyword); + + case 2: + /* There is only one good candidate */ + kw0 = keywords[0]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s'?"), + keyword, kw0->keyword.data); + + case 3: + /* Suggest a list of the most likely candidates */ + kw0 = keywords[0]->data; + kw1 = keywords[1]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s' or '%s'?"), + keyword, kw0->keyword.data, kw1->keyword.data); + + default: + /* Never suggest more than three candidates */ + kw0 = keywords[0]->data; + kw1 = keywords[1]->data; + kw2 = keywords[2]->data; + return svn_error_createf( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid value for --show-item;" + " did you mean '%s', '%s' or '%s'?"), + keyword, kw0->keyword.data, kw1->keyword.data, kw2->keyword.data); + } +} /* A callback of type svn_client_info_receiver2_t. Prints svn info in xml mode to standard out */ @@ -87,7 +249,7 @@ print_info_xml(void *baton, { svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); const char *rev_str; - const char *path_prefix = baton; + print_info_baton_t *const receiver_baton = baton; if (SVN_IS_VALID_REVNUM(info->rev)) rev_str = apr_psprintf(pool, "%ld", info->rev); @@ -97,10 +259,10 @@ print_info_xml(void *baton, /* "" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "path", svn_cl__local_style_skip_ancestor( - path_prefix, target, pool), + receiver_baton->path_prefix, target, pool), "kind", svn_cl__node_kind_str_xml(info->kind), "revision", rev_str, - NULL); + SVN_VA_NULL); /* " xx " */ svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL); @@ -109,19 +271,14 @@ print_info_xml(void *baton, { /* " xx " */ svn_cl__xml_tagged_cdata(&sb, pool, "relative-url", - apr_pstrcat(pool, "^/", - svn_path_uri_encode( - svn_uri_skip_ancestor( - info->repos_root_URL, - info->URL, pool), - pool), - NULL)); + relative_url(info, pool)); } if (info->repos_root_URL || info->repos_UUID) { /* "" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", + SVN_VA_NULL); /* " xx " */ svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL); @@ -136,7 +293,8 @@ print_info_xml(void *baton, if (info->wc_info) { /* "" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", + SVN_VA_NULL); /* " xx " */ if (info->wc_info->wcroot_abspath) @@ -261,11 +419,11 @@ print_info(void *baton, const svn_client_info2_t *info, apr_pool_t *pool) { - const char *path_prefix = baton; + print_info_baton_t *const receiver_baton = baton; SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, target, pool))); + receiver_baton->path_prefix, target, pool))); /* ### remove this someday: it's only here for cmdline output compatibility with svn 1.1 and older. */ @@ -283,11 +441,8 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL)); if (info->URL && info->repos_root_URL) - SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: ^/%s\n"), - svn_path_uri_encode( - svn_uri_skip_ancestor(info->repos_root_URL, - info->URL, pool), - pool))); + SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: %s\n"), + relative_url(info, pool))); if (info->repos_root_URL) SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"), @@ -387,30 +542,18 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"), info->wc_info->copyfrom_rev)); if (info->wc_info->moved_from_abspath) - { - const char *relpath; - - relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath, - info->wc_info->moved_from_abspath); - if (relpath && relpath[0] != '\0') - SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), relpath)); - else - SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), - info->wc_info->moved_from_abspath)); - } + SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"), + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + info->wc_info->moved_from_abspath, + pool))); if (info->wc_info->moved_to_abspath) - { - const char *relpath; - - relpath = svn_dirent_skip_ancestor(info->wc_info->wcroot_abspath, - info->wc_info->moved_to_abspath); - if (relpath && relpath[0] != '\0') - SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), relpath)); - else - SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), - info->wc_info->moved_to_abspath)); - } + SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"), + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + info->wc_info->moved_to_abspath, + pool))); } if (info->last_changed_author) @@ -439,6 +582,7 @@ print_info(void *baton, if (info->wc_info->conflicts) { svn_boolean_t printed_prop_conflict_file = FALSE; + svn_boolean_t printed_tc = FALSE; int i; for (i = 0; i < info->wc_info->conflicts->nelts; i++) @@ -455,21 +599,24 @@ print_info(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("Conflict Previous Base File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->base_abspath, + receiver_baton->path_prefix, + conflict->base_abspath, pool))); if (conflict->my_abspath) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Previous Working File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->my_abspath, + receiver_baton->path_prefix, + conflict->my_abspath, pool))); if (conflict->their_abspath) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Current Base File: %s\n"), svn_cl__local_style_skip_ancestor( - path_prefix, conflict->their_abspath, + receiver_baton->path_prefix, + conflict->their_abspath, pool))); break; @@ -477,12 +624,15 @@ print_info(void *baton, if (! printed_prop_conflict_file) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Properties File: %s\n"), - svn_dirent_local_style(conflict->their_abspath, - pool))); + svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, + conflict->prop_reject_abspath, + pool))); printed_prop_conflict_file = TRUE; break; case svn_wc_conflict_kind_tree: + printed_tc = TRUE; SVN_ERR( svn_cl__get_human_readable_tree_conflict_description( &desc, conflict, pool)); @@ -504,6 +654,19 @@ print_info(void *baton, APR_ARRAY_IDX(info->wc_info->conflicts, 0, const svn_wc_conflict_description2_t *); + if (!printed_tc) + { + const char *desc; + + SVN_ERR(svn_cl__get_human_readable_action_description(&desc, + svn_wc_conflict_action_edit, + conflict->operation, + conflict->node_kind, pool)); + + SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n", + _("Conflict Details"), desc)); + } + src_left_version = svn_cl__node_description(conflict->src_left_version, info->repos_root_URL, pool); @@ -570,6 +733,123 @@ print_info(void *baton, } +/* Helper for print_info_item(): Print the value TEXT for TARGET_PATH, + either of which may be NULL. Use POOL for temporary allocation. */ +static svn_error_t * +print_info_item_string(const char *text, const char *target_path, + apr_pool_t *pool) +{ + if (text) + { + if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", text, target_path)); + else + SVN_ERR(svn_cmdline_fputs(text, stdout, pool)); + } + else if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path)); + + return SVN_NO_ERROR; +} + +/* Helper for print_info_item(): Print the revision number REV, which + may be SVN_INVALID_REVNUM, for TARGET_PATH, which may be NULL. Use + POOL for temporary allocation. */ +static svn_error_t * +print_info_item_revision(svn_revnum_t rev, const char *target_path, + apr_pool_t *pool) +{ + if (SVN_IS_VALID_REVNUM(rev)) + { + if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10ld %s", rev, target_path)); + else + SVN_ERR(svn_cmdline_printf(pool, "%-10ld", rev)); + } + else if (target_path) + SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path)); + + return SVN_NO_ERROR; +} + +/* A callback of type svn_client_info_receiver2_t. */ +static svn_error_t * +print_info_item(void *baton, + const char *target, + const svn_client_info2_t *info, + apr_pool_t *pool) +{ + print_info_baton_t *const receiver_baton = baton; + const char *const target_path = + (!receiver_baton->multiple_targets ? NULL + : (!receiver_baton->target_is_path ? info->URL + : svn_cl__local_style_skip_ancestor( + receiver_baton->path_prefix, target, pool))); + + if (receiver_baton->start_new_line) + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); + + switch (receiver_baton->print_what) + { + case info_item_kind: + SVN_ERR(print_info_item_string(svn_node_kind_to_word(info->kind), + target_path, pool)); + break; + + case info_item_url: + SVN_ERR(print_info_item_string(info->URL, target_path, pool)); + break; + + case info_item_relative_url: + SVN_ERR(print_info_item_string(relative_url(info, pool), + target_path, pool)); + break; + + case info_item_repos_root_url: + SVN_ERR(print_info_item_string(info->repos_root_URL, target_path, pool)); + break; + + case info_item_repos_uuid: + SVN_ERR(print_info_item_string(info->repos_UUID, target_path, pool)); + break; + + case info_item_revision: + SVN_ERR(print_info_item_revision(info->rev, target_path, pool)); + break; + + case info_item_last_changed_rev: + SVN_ERR(print_info_item_revision(info->last_changed_rev, + target_path, pool)); + break; + + case info_item_last_changed_date: + SVN_ERR(print_info_item_string( + (!info->last_changed_date ? NULL + : svn_time_to_cstring(info->last_changed_date, pool)), + target_path, pool)); + break; + + case info_item_last_changed_author: + SVN_ERR(print_info_item_string(info->last_changed_author, + target_path, pool)); + break; + + case info_item_wc_root: + SVN_ERR(print_info_item_string( + (info->wc_info && info->wc_info->wcroot_abspath + ? info->wc_info->wcroot_abspath : NULL), + target_path, pool)); + break; + + default: + SVN_ERR_MALFUNCTION(); + } + + receiver_baton->start_new_line = TRUE; + return SVN_NO_ERROR; +} + + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__info(apr_getopt_t *os, @@ -585,7 +865,7 @@ svn_cl__info(apr_getopt_t *os, svn_boolean_t seen_nonexistent_target = FALSE; svn_opt_revision_t peg_revision; svn_client_info_receiver2_t receiver; - const char *path_prefix; + print_info_baton_t receiver_baton = { 0 }; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, @@ -598,26 +878,59 @@ svn_cl__info(apr_getopt_t *os, { receiver = print_info_xml; + if (opt_state->show_item) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--show-item is not valid in --xml mode")); + if (opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline is not valid 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("info", pool)); } + else if (opt_state->show_item) + { + receiver = print_info_item; + + if (opt_state->incremental) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--incremental is only valid in --xml mode")); + + receiver_baton.multiple_targets = (opt_state->depth > svn_depth_empty + || targets->nelts > 1); + if (receiver_baton.multiple_targets && opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline is only available for single-target," + " non-recursive info operations")); + + SVN_ERR(find_print_what(opt_state->show_item, &receiver_baton, pool)); + receiver_baton.start_new_line = FALSE; + } else { receiver = print_info; if (opt_state->incremental) - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("'incremental' option only valid in XML " - "mode")); + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--incremental is only valid in --xml mode")); + if (opt_state->no_newline) + return svn_error_create( + SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--no-newline' is only valid with --show-item")); } if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_empty; - SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool)); + SVN_ERR(svn_dirent_get_absolute(&receiver_baton.path_prefix, "", pool)); for (i = 0; i < targets->nelts; i++) { @@ -635,17 +948,22 @@ svn_cl__info(apr_getopt_t *os, { if (peg_revision.kind == svn_opt_revision_unspecified) peg_revision.kind = svn_opt_revision_head; + receiver_baton.target_is_path = FALSE; } else { SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool)); + receiver_baton.target_is_path = TRUE; } - err = svn_client_info3(truepath, + err = svn_client_info4(truepath, &peg_revision, &(opt_state->start_revision), - opt_state->depth, TRUE, TRUE, + opt_state->depth, + TRUE /* fetch_excluded */, + TRUE /* fetch_actual_only */, + opt_state->include_externals, opt_state->changelists, - receiver, (void *) path_prefix, + receiver, &receiver_baton, ctx, subpool); if (err) @@ -672,6 +990,9 @@ svn_cl__info(apr_getopt_t *os, if (opt_state->xml && (! opt_state->incremental)) SVN_ERR(svn_cl__xml_print_footer("info", pool)); + else if (opt_state->show_item && !opt_state->no_newline + && receiver_baton.start_new_line) + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); if (seen_nonexistent_target) return svn_error_create( diff --git a/contrib/subversion/subversion/svn/list-cmd.c b/contrib/subversion/subversion/svn/list-cmd.c index 022ad5497..5ea140fc9 100644 --- a/contrib/subversion/subversion/svn/list-cmd.c +++ b/contrib/subversion/subversion/svn/list-cmd.c @@ -222,7 +222,7 @@ print_dirent_xml(void *baton, svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "external", "parent_url", external_parent_url, "target", external_target, - NULL); + SVN_VA_NULL); pb->last_external_parent_url = external_parent_url; pb->last_external_target = external_target; @@ -232,7 +232,7 @@ print_dirent_xml(void *baton, svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "entry", "kind", svn_cl__node_kind_str_xml(dirent->kind), - NULL); + SVN_VA_NULL); svn_cl__xml_tagged_cdata(&sb, scratch_pool, "name", entryname); @@ -246,7 +246,7 @@ print_dirent_xml(void *baton, svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "commit", "revision", apr_psprintf(scratch_pool, "%ld", dirent->created_rev), - NULL); + SVN_VA_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", @@ -255,7 +255,8 @@ print_dirent_xml(void *baton, if (lock) { - svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "lock", NULL); + svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "lock", + SVN_VA_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); @@ -370,7 +371,7 @@ svn_cl__list(apr_getopt_t *os, 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_VA_NULL); SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); } @@ -430,6 +431,8 @@ svn_cl__list(apr_getopt_t *os, if (seen_nonexistent_target) err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, _("Could not list all targets because some targets don't exist")); + else + err = NULL; return svn_error_compose_create(externals_err, err); } diff --git a/contrib/subversion/subversion/svn/lock-cmd.c b/contrib/subversion/subversion/svn/lock-cmd.c index c2795da9a..e527ff858 100644 --- a/contrib/subversion/subversion/svn/lock-cmd.c +++ b/contrib/subversion/subversion/svn/lock-cmd.c @@ -80,6 +80,29 @@ get_comment(const char **comment, svn_client_ctx_t *ctx, return SVN_NO_ERROR; } +/* Baton for notify_lock_handler */ +struct notify_lock_baton_t +{ + void *inner_baton; + svn_wc_notify_func2_t inner_notify; + svn_boolean_t had_failure; +}; + +/* Implements svn_wc_notify_func2_t for svn_cl__lock */ +static void +notify_lock_handler(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *scratch_pool) +{ + struct notify_lock_baton_t *nlb = baton; + + if (notify->action == svn_wc_notify_failed_lock) + nlb->had_failure = TRUE; + + if (nlb->inner_notify) + nlb->inner_notify(nlb->inner_baton, notify, scratch_pool); +} + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__lock(apr_getopt_t *os, @@ -90,6 +113,7 @@ svn_cl__lock(apr_getopt_t *os, svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; apr_array_header_t *targets; const char *comment; + struct notify_lock_baton_t nlb; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, @@ -106,5 +130,18 @@ svn_cl__lock(apr_getopt_t *os, SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool)); - return svn_client_lock(targets, comment, opt_state->force, ctx, pool); + nlb.inner_notify = ctx->notify_func2; + nlb.inner_baton = ctx->notify_baton2; + nlb.had_failure = FALSE; + + ctx->notify_func2 = notify_lock_handler; + ctx->notify_baton2 = &nlb; + + SVN_ERR(svn_client_lock(targets, comment, opt_state->force, ctx, pool)); + + if (nlb.had_failure) + return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, + _("One or more locks could not be obtained")); + + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/log-cmd.c b/contrib/subversion/subversion/svn/log-cmd.c index af57cf4de..44f8a4cff 100644 --- a/contrib/subversion/subversion/svn/log-cmd.c +++ b/contrib/subversion/subversion/svn/log-cmd.c @@ -37,51 +37,16 @@ #include "svn_pools.h" #include "private/svn_cmdline_private.h" +#include "private/svn_sorts_private.h" #include "cl.h" +#include "cl-log.h" #include "svn_private_config.h" /*** Code. ***/ -/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */ -struct log_receiver_baton -{ - /* Client context. */ - svn_client_ctx_t *ctx; - - /* The target of the log operation. */ - const char *target_path_or_url; - svn_opt_revision_t target_peg_revision; - - /* Don't print log message body nor its line count. */ - svn_boolean_t omit_log_message; - - /* Whether to show diffs in the log. (maps to --diff) */ - svn_boolean_t show_diff; - - /* Depth applied to diff output. */ - svn_depth_t depth; - - /* Diff arguments received from command line. */ - const char *diff_extensions; - - /* Stack which keeps track of merge revision nesting, using svn_revnum_t's */ - apr_array_header_t *merge_stack; - - /* Log message search patterns. Log entries will only be shown if the author, - * the log message, or a changed path matches one of these patterns. */ - apr_array_header_t *search_patterns; - - /* Pool for persistent allocations. */ - apr_pool_t *pool; -}; - - -/* The separator between log messages. */ -#define SEP_STRING \ - "------------------------------------------------------------------------\n" /* Display a diff of the subtree TARGET_PATH_OR_URL@TARGET_PEG_REVISION as @@ -182,14 +147,14 @@ match_search_pattern(const char *search_pattern, hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); svn_log_changed_path2_t *log_item; if (apr_fnmatch(pattern, path, flags) == APR_SUCCESS) return TRUE; /* Match copy-from paths, too. */ - log_item = svn__apr_hash_index_val(hi); + log_item = apr_hash_this_val(hi); if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev) && apr_fnmatch(pattern, @@ -249,7 +214,7 @@ match_search_patterns(apr_array_header_t *search_patterns, /* Implement `svn_log_entry_receiver_t', printing the logs in * a human-readable and machine-parseable format. * - * BATON is of type `struct log_receiver_baton'. + * BATON is of type `svn_cl__log_receiver_baton'. * * First, print a header line. Then if CHANGED_PATHS is non-null, * print all affected paths in a list headed "Changed paths:\n", @@ -323,12 +288,12 @@ match_search_patterns(apr_array_header_t *search_patterns, * ------------------------------------------------------------------------ * */ -static svn_error_t * -log_entry_receiver(void *baton, - svn_log_entry_t *log_entry, - apr_pool_t *pool) +svn_error_t * +svn_cl__log_entry_receiver(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool) { - struct log_receiver_baton *lb = baton; + svn_cl__log_receiver_baton *lb = baton; const char *author; const char *date; const char *message; @@ -343,7 +308,9 @@ log_entry_receiver(void *baton, if (! SVN_IS_VALID_REVNUM(log_entry->revision)) { - apr_array_pop(lb->merge_stack); + if (lb->merge_stack) + apr_array_pop(lb->merge_stack); + return SVN_NO_ERROR; } @@ -367,13 +334,18 @@ log_entry_receiver(void *baton, log_entry->changed_paths2, pool)) { if (log_entry->has_children) - APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + { + if (! lb->merge_stack) + lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t)); + + APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + } return SVN_NO_ERROR; } SVN_ERR(svn_cmdline_printf(pool, - SEP_STRING "r%ld | %s | %s", + SVN_CL__LOG_SEP_STRING "r%ld | %s | %s", log_entry->revision, author, date)); if (message != NULL) @@ -392,6 +364,7 @@ log_entry_receiver(void *baton, { apr_array_header_t *sorted_paths; int i; + apr_pool_t *iterpool; /* Get an array of sorted hash keys. */ sorted_paths = svn_sort__hash(log_entry->changed_paths2, @@ -399,6 +372,7 @@ log_entry_receiver(void *baton, SVN_ERR(svn_cmdline_printf(pool, _("Changed paths:\n"))); + iterpool = svn_pool_create(pool); for (i = 0; i < sorted_paths->nelts; i++) { svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i, @@ -407,6 +381,8 @@ log_entry_receiver(void *baton, svn_log_changed_path2_t *log_item = item->value; const char *copy_data = ""; + svn_pool_clear(iterpool); + if (lb->ctx->cancel_func) SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton)); @@ -414,34 +390,39 @@ log_entry_receiver(void *baton, && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) { copy_data - = apr_psprintf(pool, + = apr_psprintf(iterpool, _(" (from %s:%ld)"), log_item->copyfrom_path, log_item->copyfrom_rev); } - SVN_ERR(svn_cmdline_printf(pool, " %c %s%s\n", + SVN_ERR(svn_cmdline_printf(iterpool, " %c %s%s\n", log_item->action, path, copy_data)); } + svn_pool_destroy(iterpool); } - if (lb->merge_stack->nelts > 0) + if (lb->merge_stack && lb->merge_stack->nelts > 0) { int i; + apr_pool_t *iterpool; /* Print the result of merge line */ if (log_entry->subtractive_merge) SVN_ERR(svn_cmdline_printf(pool, _("Reverse merged via:"))); else SVN_ERR(svn_cmdline_printf(pool, _("Merged via:"))); + iterpool = svn_pool_create(pool); for (i = 0; i < lb->merge_stack->nelts; i++) { svn_revnum_t rev = APR_ARRAY_IDX(lb->merge_stack, i, svn_revnum_t); - SVN_ERR(svn_cmdline_printf(pool, " r%ld%c", rev, + svn_pool_clear(iterpool); + SVN_ERR(svn_cmdline_printf(iterpool, " r%ld%c", rev, i == lb->merge_stack->nelts - 1 ? '\n' : ',')); } + svn_pool_destroy(iterpool); } if (message != NULL) @@ -473,7 +454,12 @@ log_entry_receiver(void *baton, } if (log_entry->has_children) - APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + { + if (! lb->merge_stack) + lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t)); + + APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + } return SVN_NO_ERROR; } @@ -481,7 +467,7 @@ log_entry_receiver(void *baton, /* This implements `svn_log_entry_receiver_t', printing the logs in XML. * - * BATON is of type `struct log_receiver_baton'. + * BATON is of type `svn_cl__log_receiver_baton'. * * Here is an example of the output; note that the "" and * "" tags are not emitted by this function: @@ -515,12 +501,12 @@ log_entry_receiver(void *baton, * * */ -static svn_error_t * -log_entry_receiver_xml(void *baton, - svn_log_entry_t *log_entry, - apr_pool_t *pool) +svn_error_t * +svn_cl__log_entry_receiver_xml(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool) { - struct log_receiver_baton *lb = baton; + svn_cl__log_receiver_baton *lb = baton; /* Collate whole log message into sb before printing. */ svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); char *revstr; @@ -540,7 +526,8 @@ log_entry_receiver_xml(void *baton, { svn_xml_make_close_tag(&sb, pool, "logentry"); SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); - apr_array_pop(lb->merge_stack); + if (lb->merge_stack) + apr_array_pop(lb->merge_stack); return SVN_NO_ERROR; } @@ -551,7 +538,12 @@ log_entry_receiver_xml(void *baton, log_entry->changed_paths2, pool)) { if (log_entry->has_children) - APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + { + if (! lb->merge_stack) + lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t)); + + APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + } return SVN_NO_ERROR; } @@ -565,8 +557,19 @@ log_entry_receiver_xml(void *baton, revstr = apr_psprintf(pool, "%ld", log_entry->revision); /* */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry", - "revision", revstr, NULL); + if (lb->merge_stack && lb->merge_stack->nelts > 0) + { + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry", + "revision", revstr, "reverse-merge", + log_entry->subtractive_merge ? "true" : "false", + SVN_VA_NULL); + + } + else + { + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry", + "revision", revstr, SVN_VA_NULL); + } /* xxx */ svn_cl__xml_tagged_cdata(&sb, pool, "author", author); @@ -587,7 +590,7 @@ log_entry_receiver_xml(void *baton, /* */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths", - NULL); + SVN_VA_NULL); /* Get an array of sorted hash keys. */ sorted_paths = svn_sort__hash(log_entry->changed_paths2, @@ -619,7 +622,7 @@ log_entry_receiver_xml(void *baton, log_item->text_modified), "prop-mods", svn_tristate__to_word( log_item->props_modified), - NULL); + SVN_VA_NULL); } else { @@ -632,7 +635,7 @@ log_entry_receiver_xml(void *baton, log_item->text_modified), "prop-mods", svn_tristate__to_word( log_item->props_modified), - NULL); + SVN_VA_NULL); } /* xxx */ svn_xml_escape_cdata_cstring(&sb, path, pool); @@ -652,7 +655,7 @@ log_entry_receiver_xml(void *baton, svn_compat_log_revprops_clear(log_entry->revprops); if (log_entry->revprops && apr_hash_count(log_entry->revprops) > 0) { - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", SVN_VA_NULL); SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, log_entry->revprops, FALSE, /* name_only */ FALSE, pool)); @@ -660,7 +663,12 @@ log_entry_receiver_xml(void *baton, } if (log_entry->has_children) - APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + { + if (! lb->merge_stack) + lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t)); + + APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision; + } else svn_xml_make_close_tag(&sb, pool, "logentry"); @@ -677,7 +685,7 @@ svn_cl__log(apr_getopt_t *os, 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; - struct log_receiver_baton lb; + svn_cl__log_receiver_baton lb; const char *target; int i; apr_array_header_t *revprops; @@ -785,7 +793,7 @@ svn_cl__log(apr_getopt_t *os, lb.depth = opt_state->depth == svn_depth_unknown ? svn_depth_infinity : opt_state->depth; lb.diff_extensions = opt_state->extensions; - lb.merge_stack = apr_array_make(pool, 0, sizeof(svn_revnum_t)); + lb.merge_stack = NULL; lb.search_patterns = opt_state->search_patterns; lb.pool = pool; @@ -813,8 +821,8 @@ svn_cl__log(apr_getopt_t *os, hi != NULL; hi = apr_hash_next(hi)) { - const char *property = svn__apr_hash_index_key(hi); - svn_string_t *value = svn__apr_hash_index_val(hi); + const char *property = apr_hash_this_key(hi); + svn_string_t *value = apr_hash_this_val(hi); if (value && value->data[0] != '\0') return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, @@ -839,7 +847,7 @@ svn_cl__log(apr_getopt_t *os, opt_state->stop_on_copy, opt_state->use_merge_history, revprops, - log_entry_receiver_xml, + svn_cl__log_entry_receiver_xml, &lb, ctx, pool)); @@ -862,13 +870,13 @@ svn_cl__log(apr_getopt_t *os, opt_state->stop_on_copy, opt_state->use_merge_history, revprops, - log_entry_receiver, + svn_cl__log_entry_receiver, &lb, ctx, pool)); if (! opt_state->incremental) - SVN_ERR(svn_cmdline_printf(pool, SEP_STRING)); + SVN_ERR(svn_cmdline_printf(pool, SVN_CL__LOG_SEP_STRING)); } return SVN_NO_ERROR; diff --git a/contrib/subversion/subversion/svn/merge-cmd.c b/contrib/subversion/subversion/svn/merge-cmd.c index 17507a2cb..6feda4cd1 100644 --- a/contrib/subversion/subversion/svn/merge-cmd.c +++ b/contrib/subversion/subversion/svn/merge-cmd.c @@ -37,10 +37,6 @@ #include "svn_private_config.h" -/* A handy constant */ -static const svn_opt_revision_t unspecified_revision - = { svn_opt_revision_unspecified, { 0 } }; - /*** Code. ***/ diff --git a/contrib/subversion/subversion/svn/mergeinfo-cmd.c b/contrib/subversion/subversion/svn/mergeinfo-cmd.c index 41edcdafc..c3c03058c 100644 --- a/contrib/subversion/subversion/svn/mergeinfo-cmd.c +++ b/contrib/subversion/subversion/svn/mergeinfo-cmd.c @@ -27,7 +27,9 @@ /*** Includes. ***/ +#include "svn_compat.h" #include "svn_pools.h" +#include "svn_props.h" #include "svn_client.h" #include "svn_cmdline.h" #include "svn_path.h" @@ -35,6 +37,7 @@ #include "svn_error_codes.h" #include "svn_types.h" #include "cl.h" +#include "cl-log.h" #include "svn_private_config.h" @@ -55,6 +58,25 @@ print_log_rev(void *baton, return SVN_NO_ERROR; } +/* Implements a svn_log_entry_receiver_t interface that filters out changed + * paths data before calling the svn_cl__log_entry_receiver(). Right now we + * always have to pass TRUE for discover_changed_paths for + * svn_client_mergeinfo_log2() due to the side effect of that option. The + * svn_cl__log_entry_receiver() discovers if it should print the changed paths + * implicitly by the path info existing. As a result this filter is needed + * to allow expected output without changed paths. + */ +static svn_error_t * +print_log_details(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool) +{ + log_entry->changed_paths = NULL; + log_entry->changed_paths2 = NULL; + + return svn_cl__log_entry_receiver(baton, log_entry, pool); +} + /* Draw a diagram (by printing text to the console) summarizing the state * of merging between two branches, given the merge description * indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */ @@ -218,7 +240,8 @@ mergeinfo_summary( 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); + || target_revision->kind == svn_opt_revision_working + || target_revision->kind == svn_opt_revision_base); SVN_ERR(svn_client_get_merging_summary( &is_reintegration, &yca_url, &yca_rev, @@ -238,6 +261,77 @@ mergeinfo_summary( return SVN_NO_ERROR; } +static svn_error_t * +mergeinfo_log(svn_boolean_t finding_merged, + const char *target, + const svn_opt_revision_t *tgt_peg_revision, + const char *source, + const svn_opt_revision_t *src_peg_revision, + const svn_opt_revision_t *src_start_revision, + const svn_opt_revision_t *src_end_revision, + svn_depth_t depth, + svn_boolean_t include_log_details, + svn_boolean_t quiet, + svn_boolean_t verbose, + svn_boolean_t incremental, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + apr_array_header_t *revprops; + svn_log_entry_receiver_t log_receiver; + void *log_receiver_baton; + + if (include_log_details) + { + svn_cl__log_receiver_baton *baton; + + revprops = apr_array_make(pool, 3, sizeof(const char *)); + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR; + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; + if (!quiet) + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; + + if (verbose) + log_receiver = svn_cl__log_entry_receiver; + else + log_receiver = print_log_details; + + baton = apr_palloc(pool, sizeof(svn_cl__log_receiver_baton)); + baton->ctx = ctx; + baton->target_path_or_url = target; + baton->target_peg_revision = *tgt_peg_revision; + baton->omit_log_message = quiet; + baton->show_diff = FALSE; + baton->depth = depth; + baton->diff_extensions = NULL; + baton->merge_stack = NULL; + baton->search_patterns = NULL; + baton->pool = pool; + log_receiver_baton = baton; + } + else + { + /* We need only revisions number, not revision properties. */ + revprops = apr_array_make(pool, 0, sizeof(const char *)); + log_receiver = print_log_rev; + log_receiver_baton = NULL; + } + + SVN_ERR(svn_client_mergeinfo_log2(finding_merged, target, + tgt_peg_revision, + source, src_peg_revision, + src_start_revision, + src_end_revision, + log_receiver, log_receiver_baton, + TRUE, depth, revprops, ctx, + pool)); + + if (include_log_details && !incremental) + SVN_ERR(svn_cmdline_printf(pool, SVN_CL__LOG_SEP_STRING)); + + return SVN_NO_ERROR; +} + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__mergeinfo(apr_getopt_t *os, @@ -303,36 +397,44 @@ svn_cl__mergeinfo(apr_getopt_t *os, else src_end_revision = &(opt_state->end_revision); - /* Do the real work, depending on the requested data flavor. */ - if (opt_state->show_revs == svn_cl__show_revs_merged) + if (!opt_state->mergeinfo_log) { - apr_array_header_t *revprops; + if (opt_state->quiet) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--quiet (-q) option valid only with --log " + "option")); - /* We need only revisions number, not revision properties. */ - revprops = apr_array_make(pool, 0, sizeof(const char *)); + if (opt_state->verbose) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--verbose (-v) option valid only with " + "--log option")); - SVN_ERR(svn_client_mergeinfo_log2(TRUE, target, &tgt_peg_revision, - source, &src_peg_revision, - src_start_revision, - src_end_revision, - print_log_rev, NULL, - TRUE, depth, revprops, ctx, - pool)); + if (opt_state->incremental) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--incremental option valid only with " + "--log option")); + } + + /* Do the real work, depending on the requested data flavor. */ + if (opt_state->show_revs == svn_cl__show_revs_merged) + { + SVN_ERR(mergeinfo_log(TRUE, target, &tgt_peg_revision, + source, &src_peg_revision, + src_start_revision, + src_end_revision, + depth, opt_state->mergeinfo_log, + opt_state->quiet, opt_state->verbose, + opt_state->incremental, ctx, pool)); } else if (opt_state->show_revs == svn_cl__show_revs_eligible) { - apr_array_header_t *revprops; - - /* We need only revisions number, not revision properties. */ - revprops = apr_array_make(pool, 0, sizeof(const char *)); - - SVN_ERR(svn_client_mergeinfo_log2(FALSE, target, &tgt_peg_revision, - source, &src_peg_revision, - src_start_revision, - src_end_revision, - print_log_rev, NULL, - TRUE, depth, revprops, ctx, - pool)); + SVN_ERR(mergeinfo_log(FALSE, target, &tgt_peg_revision, + source, &src_peg_revision, + src_start_revision, + src_end_revision, + depth, opt_state->mergeinfo_log, + opt_state->quiet, opt_state->verbose, + opt_state->incremental, ctx, pool)); } else { @@ -345,6 +447,11 @@ svn_cl__mergeinfo(apr_getopt_t *os, return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Depth specification options valid only " "with --show-revs option")); + if (opt_state->mergeinfo_log) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--log option valid only with " + "--show-revs option")); + SVN_ERR(mergeinfo_summary(source, &src_peg_revision, target, &tgt_peg_revision, diff --git a/contrib/subversion/subversion/svn/notify.c b/contrib/subversion/subversion/svn/notify.c index 6498fb178..c530694c7 100644 --- a/contrib/subversion/subversion/svn/notify.c +++ b/contrib/subversion/subversion/svn/notify.c @@ -52,7 +52,7 @@ struct notify_baton svn_boolean_t is_export; svn_boolean_t is_wc_to_repos_copy; svn_boolean_t sent_first_txdelta; - svn_boolean_t in_external; + int in_external; svn_boolean_t had_print_error; /* Used to not keep printing error messages when we've already had one print error. */ @@ -129,7 +129,7 @@ svn_cl__conflict_stats_resolved(svn_cl__conflict_stats_t *conflict_stats, static const char * remaining_str(apr_pool_t *pool, int n_remaining) { - return apr_psprintf(pool, Q_("%d remaining", + return apr_psprintf(pool, Q_("%d remaining", "%d remaining", n_remaining), n_remaining); @@ -145,20 +145,20 @@ resolved_str(apr_pool_t *pool, int n_resolved) } svn_error_t * -svn_cl__notifier_print_conflict_stats(void *baton, apr_pool_t *scratch_pool) +svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats, + apr_pool_t *scratch_pool) { - struct notify_baton *nb = baton; - int n_text = apr_hash_count(nb->conflict_stats->text_conflicts); - int n_prop = apr_hash_count(nb->conflict_stats->prop_conflicts); - int n_tree = apr_hash_count(nb->conflict_stats->tree_conflicts); - int n_text_r = nb->conflict_stats->text_conflicts_resolved; - int n_prop_r = nb->conflict_stats->prop_conflicts_resolved; - int n_tree_r = nb->conflict_stats->tree_conflicts_resolved; + int n_text = apr_hash_count(conflict_stats->text_conflicts); + int n_prop = apr_hash_count(conflict_stats->prop_conflicts); + int n_tree = apr_hash_count(conflict_stats->tree_conflicts); + int n_text_r = conflict_stats->text_conflicts_resolved; + int n_prop_r = conflict_stats->prop_conflicts_resolved; + int n_tree_r = conflict_stats->tree_conflicts_resolved; if (n_text > 0 || n_text_r > 0 || n_prop > 0 || n_prop_r > 0 || n_tree > 0 || n_tree_r > 0 - || nb->conflict_stats->skipped_paths > 0) + || conflict_stats->skipped_paths > 0) SVN_ERR(svn_cmdline_printf(scratch_pool, _("Summary of conflicts:\n"))); @@ -195,32 +195,43 @@ svn_cl__notifier_print_conflict_stats(void *baton, apr_pool_t *scratch_pool) remaining_str(scratch_pool, n_tree), resolved_str(scratch_pool, n_tree_r))); } - if (nb->conflict_stats->skipped_paths > 0) + if (conflict_stats->skipped_paths > 0) SVN_ERR(svn_cmdline_printf(scratch_pool, _(" Skipped paths: %d\n"), - nb->conflict_stats->skipped_paths)); + conflict_stats->skipped_paths)); return SVN_NO_ERROR; } -/* This implements `svn_wc_notify_func2_t'. - * NOTE: This function can't fail, so we just ignore any print errors. */ -static void -notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) +svn_error_t * +svn_cl__notifier_print_conflict_stats(void *baton, apr_pool_t *scratch_pool) { struct notify_baton *nb = baton; + + SVN_ERR(svn_cl__print_conflict_stats(nb->conflict_stats, scratch_pool)); + return SVN_NO_ERROR; +} + +/* The body for notify() function with standard error handling semantic. + * Handling of errors implemented at caller side. */ +static svn_error_t * +notify_body(struct notify_baton *nb, + const svn_wc_notify_t *n, + apr_pool_t *pool) +{ char statchar_buf[5] = " "; const char *path_local; - svn_error_t *err; if (n->url) path_local = n->url; else { + /* Skip the path prefix in N, if supplied, or else the path prefix + in NB (which was set to the current working directory). */ if (n->path_prefix) path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path, pool); - else /* skip nb->path_prefix, if it's non-null */ + else path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path, pool); } @@ -231,90 +242,78 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) nb->conflict_stats->skipped_paths++; if (n->content_state == svn_wc_notify_state_missing) { - if ((err = svn_cmdline_printf - (pool, _("Skipped missing target: '%s'\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Skipped missing target: '%s'\n"), + path_local)); } else if (n->content_state == svn_wc_notify_state_source_missing) { - if ((err = svn_cmdline_printf - (pool, _("Skipped target: '%s' -- copy-source is missing\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, + _("Skipped target: '%s' -- copy-source is missing\n"), + path_local)); } else { - if ((err = svn_cmdline_printf - (pool, _("Skipped '%s'\n"), path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Skipped '%s'\n"), path_local)); } break; case svn_wc_notify_update_skip_obstruction: nb->conflict_stats->skipped_paths++; - if ((err = svn_cmdline_printf( - pool, _("Skipped '%s' -- An obstructing working copy was found\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, + _("Skipped '%s' -- An obstructing working copy was found\n"), + path_local)); break; case svn_wc_notify_update_skip_working_only: nb->conflict_stats->skipped_paths++; - if ((err = svn_cmdline_printf( - pool, _("Skipped '%s' -- Has no versioned parent\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, _("Skipped '%s' -- Has no versioned parent\n"), + path_local)); break; case svn_wc_notify_update_skip_access_denied: nb->conflict_stats->skipped_paths++; - if ((err = svn_cmdline_printf( - pool, _("Skipped '%s' -- Access denied\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, _("Skipped '%s' -- Access denied\n"), + path_local)); break; case svn_wc_notify_skip_conflicted: nb->conflict_stats->skipped_paths++; - if ((err = svn_cmdline_printf( - pool, _("Skipped '%s' -- Node remains in conflict\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, _("Skipped '%s' -- Node remains in conflict\n"), + path_local)); break; case svn_wc_notify_update_delete: case svn_wc_notify_exclude: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, "D %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "D %s\n", path_local)); break; case svn_wc_notify_update_broken_lock: - if ((err = svn_cmdline_printf(pool, "B %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "B %s\n", path_local)); break; case svn_wc_notify_update_external_removed: nb->received_some_change = TRUE; if (n->err && n->err->message) { - if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n", - path_local, n->err->message))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Removed external '%s': %s\n"), + path_local, n->err->message)); } else { - if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n", - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Removed external '%s'\n"), + path_local)); } break; case svn_wc_notify_left_local_modifications: - if ((err = svn_cmdline_printf(pool, "Left local modifications as '%s'\n", - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Left local modifications as '%s'\n"), + path_local)); break; case svn_wc_notify_update_replace: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, "R %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "R %s\n", path_local)); break; case svn_wc_notify_update_add: @@ -322,13 +321,11 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) if (n->content_state == svn_wc_notify_state_conflicted) { store_path(nb, nb->conflict_stats->text_conflicts, path_local); - if ((err = svn_cmdline_printf(pool, "C %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "C %s\n", path_local)); } else { - if ((err = svn_cmdline_printf(pool, "A %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "A %s\n", path_local)); } break; @@ -350,34 +347,29 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) else if (n->prop_state == svn_wc_notify_state_merged) statchar_buf[1] = 'G'; - if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local)); break; case svn_wc_notify_restore: - if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Restored '%s'\n"), + path_local)); break; case svn_wc_notify_revert: - if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Reverted '%s'\n"), + path_local)); break; case svn_wc_notify_failed_revert: - if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- " - "try updating instead.\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Failed to revert '%s' -- " + "try updating instead.\n"), + path_local)); break; case svn_wc_notify_resolved: - if ((err = svn_cmdline_printf(pool, - _("Resolved conflicted state of '%s'\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Resolved conflicted state of '%s'\n"), + path_local)); break; case svn_wc_notify_add: @@ -386,23 +378,20 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) is a binary addition. */ if (n->mime_type && (svn_mime_type_is_binary(n->mime_type))) { - if ((err = svn_cmdline_printf(pool, "A (bin) %s\n", - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "A (bin) %s\n", + path_local)); } else { - if ((err = svn_cmdline_printf(pool, "A %s\n", - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "A %s\n", + path_local)); } break; case svn_wc_notify_delete: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, "D %s\n", - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "D %s\n", + path_local)); break; case svn_wc_notify_patch: @@ -431,9 +420,8 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ') { - if ((err = svn_cmdline_printf(pool, "%s %s\n", - statchar_buf, path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "%s %s\n", + statchar_buf, path_local)); } } break; @@ -473,37 +461,34 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " "with offset %s"); - err = svn_cmdline_printf(pool, - apr_pstrcat(pool, s, - "%"APR_UINT64_T_FMT - " and fuzz %lu (%s)\n", - (char *)NULL), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - minus, off, n->hunk_fuzz, - n->prop_name); + SVN_ERR(svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT + " and fuzz %lu (%s)\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->hunk_fuzz, + n->prop_name)); } else { s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " "with offset %s"); - err = svn_cmdline_printf(pool, - apr_pstrcat(pool, s, - "%"APR_UINT64_T_FMT - " and fuzz %lu\n", - (char *)NULL), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - minus, off, n->hunk_fuzz); + SVN_ERR(svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT + " and fuzz %lu\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->hunk_fuzz)); } - - if (err) - goto print_error; } else { @@ -512,39 +497,36 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) { s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " "with offset %s"); - err = svn_cmdline_printf(pool, - apr_pstrcat(pool, s, - "%"APR_UINT64_T_FMT" (%s)\n", - (char *)NULL), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - minus, off, n->prop_name); + SVN_ERR(svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT" (%s)\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->prop_name)); } else { s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " "with offset %s"); - err = svn_cmdline_printf(pool, - apr_pstrcat(pool, s, - "%"APR_UINT64_T_FMT"\n", - (char *)NULL), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - minus, off); + SVN_ERR(svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT"\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off)); } - - if (err) - goto print_error; } } else if (n->hunk_fuzz) { if (n->prop_name) - err = svn_cmdline_printf(pool, + SVN_ERR(svn_cmdline_printf(pool, _("> applied hunk ## -%lu,%lu +%lu,%lu ## " "with fuzz %lu (%s)\n"), n->hunk_original_start, @@ -552,18 +534,16 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) n->hunk_modified_start, n->hunk_modified_length, n->hunk_fuzz, - n->prop_name); + n->prop_name)); else - err = svn_cmdline_printf(pool, + SVN_ERR(svn_cmdline_printf(pool, _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " "with fuzz %lu\n"), n->hunk_original_start, n->hunk_original_length, n->hunk_modified_start, n->hunk_modified_length, - n->hunk_fuzz); - if (err) - goto print_error; + n->hunk_fuzz)); } break; @@ -572,49 +552,45 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) nb->received_some_change = TRUE; if (n->prop_name) - err = svn_cmdline_printf(pool, - _("> rejected hunk " - "## -%lu,%lu +%lu,%lu ## (%s)\n"), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - n->prop_name); + SVN_ERR(svn_cmdline_printf(pool, + _("> rejected hunk " + "## -%lu,%lu +%lu,%lu ## (%s)\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->prop_name)); else - err = svn_cmdline_printf(pool, - _("> rejected hunk " - "@@ -%lu,%lu +%lu,%lu @@\n"), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("> rejected hunk " + "@@ -%lu,%lu +%lu,%lu @@\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length)); break; case svn_wc_notify_patch_hunk_already_applied: nb->received_some_change = TRUE; if (n->prop_name) - err = svn_cmdline_printf(pool, - _("> hunk " - "## -%lu,%lu +%lu,%lu ## " - "already applied (%s)\n"), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length, - n->prop_name); + SVN_ERR(svn_cmdline_printf(pool, + _("> hunk " + "## -%lu,%lu +%lu,%lu ## " + "already applied (%s)\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->prop_name)); else - err = svn_cmdline_printf(pool, - _("> hunk " - "@@ -%lu,%lu +%lu,%lu @@ " - "already applied\n"), - n->hunk_original_start, - n->hunk_original_length, - n->hunk_modified_start, - n->hunk_modified_length); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("> hunk " + "@@ -%lu,%lu +%lu,%lu @@ " + "already applied\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length)); break; case svn_wc_notify_update_update: @@ -652,23 +628,21 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ' || statchar_buf[2] != ' ') { - if ((err = svn_cmdline_printf(pool, "%s %s\n", - statchar_buf, path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "%s %s\n", + statchar_buf, path_local)); } } break; case svn_wc_notify_update_external: /* Remember that we're now "inside" an externals definition. */ - nb->in_external = TRUE; + ++nb->in_external; /* Currently this is used for checkouts and switches too. If we want different output, we'll have to add new actions. */ - if ((err = svn_cmdline_printf(pool, - _("\nFetching external item into '%s':\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("\nFetching external item into '%s':\n"), + path_local)); break; case svn_wc_notify_failed_external: @@ -679,9 +653,8 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) if (nb->in_external) { svn_handle_warning2(stderr, n->err, "svn: "); - nb->in_external = FALSE; - if ((err = svn_cmdline_printf(pool, "\n"))) - goto print_error; + --nb->in_external; + SVN_ERR(svn_cmdline_printf(pool, "\n")); } /* Otherwise, we'll just print two warnings. Why? Because svn_handle_warning2() only shows the single "best message", @@ -691,7 +664,7 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) else { svn_error_t *warn_err = - svn_error_createf(SVN_ERR_BASE, NULL, + svn_error_createf(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL, _("Error handling externals definition for '%s':"), path_local); svn_handle_warning2(stderr, warn_err, "svn: "); @@ -705,9 +678,8 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) nb->is_checkout || nb->is_export)) { - if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Updating '%s':\n"), + path_local)); } break; @@ -717,42 +689,38 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) { if (nb->is_export) { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("Exported external at revision %ld.\n") - : _("Exported revision %ld.\n"), - n->revision))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("Exported external at revision %ld.\n") + : _("Exported revision %ld.\n"), + n->revision)); } else if (nb->is_checkout) { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("Checked out external at revision %ld.\n") - : _("Checked out revision %ld.\n"), - n->revision))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("Checked out external at revision %ld.\n") + : _("Checked out revision %ld.\n"), + n->revision)); } else { if (nb->received_some_change) { nb->received_some_change = FALSE; - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("Updated external to revision %ld.\n") - : _("Updated to revision %ld.\n"), - n->revision))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("Updated external to revision %ld.\n") + : _("Updated to revision %ld.\n"), + n->revision)); } else { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("External at revision %ld.\n") - : _("At revision %ld.\n"), - n->revision))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("External at revision %ld.\n") + : _("At revision %ld.\n"), + n->revision)); } } } @@ -760,128 +728,119 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) { if (nb->is_export) { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("External export complete.\n") - : _("Export complete.\n")))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("External export complete.\n") + : _("Export complete.\n"))); } else if (nb->is_checkout) { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("External checkout complete.\n") - : _("Checkout complete.\n")))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("External checkout complete.\n") + : _("Checkout complete.\n"))); } else { - if ((err = svn_cmdline_printf - (pool, nb->in_external - ? _("External update complete.\n") - : _("Update complete.\n")))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, nb->in_external + ? _("External update complete.\n") + : _("Update complete.\n"))); } } } if (nb->in_external) { - nb->in_external = FALSE; - if ((err = svn_cmdline_printf(pool, "\n"))) - goto print_error; + --nb->in_external; + SVN_ERR(svn_cmdline_printf(pool, "\n")); } break; case svn_wc_notify_status_external: - if ((err = svn_cmdline_printf - (pool, _("\nPerforming status on external item at '%s':\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf( + pool, _("\nPerforming status on external item at '%s':\n"), + path_local)); + break; + + case svn_wc_notify_info_external: + SVN_ERR(svn_cmdline_printf( + pool, _("\nPerforming info on external item at '%s':\n"), + path_local)); break; case svn_wc_notify_status_completed: if (SVN_IS_VALID_REVNUM(n->revision)) - if ((err = svn_cmdline_printf(pool, - _("Status against revision: %6ld\n"), - n->revision))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Status against revision: %6ld\n"), + n->revision)); break; case svn_wc_notify_commit_modified: /* xgettext: Align the %s's on this and the following 4 messages */ - if ((err = svn_cmdline_printf(pool, - nb->is_wc_to_repos_copy - ? _("Sending copy of %s\n") - : _("Sending %s\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Sending copy of %s\n") + : _("Sending %s\n"), + path_local)); break; case svn_wc_notify_commit_added: case svn_wc_notify_commit_copied: if (n->mime_type && svn_mime_type_is_binary(n->mime_type)) { - if ((err = svn_cmdline_printf(pool, - nb->is_wc_to_repos_copy - ? _("Adding copy of (bin) %s\n") - : _("Adding (bin) %s\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Adding copy of (bin) %s\n") + : _("Adding (bin) %s\n"), + path_local)); } else { - if ((err = svn_cmdline_printf(pool, - nb->is_wc_to_repos_copy - ? _("Adding copy of %s\n") - : _("Adding %s\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Adding copy of %s\n") + : _("Adding %s\n"), + path_local)); } break; case svn_wc_notify_commit_deleted: - if ((err = svn_cmdline_printf(pool, - nb->is_wc_to_repos_copy - ? _("Deleting copy of %s\n") - : _("Deleting %s\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Deleting copy of %s\n") + : _("Deleting %s\n"), + path_local)); break; case svn_wc_notify_commit_replaced: case svn_wc_notify_commit_copied_replaced: - if ((err = svn_cmdline_printf(pool, - nb->is_wc_to_repos_copy - ? _("Replacing copy of %s\n") - : _("Replacing %s\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Replacing copy of %s\n") + : _("Replacing %s\n"), + path_local)); break; case svn_wc_notify_commit_postfix_txdelta: if (! nb->sent_first_txdelta) { nb->sent_first_txdelta = TRUE; - if ((err = svn_cmdline_printf(pool, - _("Transmitting file data ")))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Transmitting file data "))); } - if ((err = svn_cmdline_printf(pool, "."))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, ".")); break; case svn_wc_notify_locked: - if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), - path_local, n->lock->owner))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), + path_local, n->lock->owner)); break; case svn_wc_notify_unlocked: - if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked.\n"), + path_local)); break; case svn_wc_notify_failed_lock: @@ -890,212 +849,182 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) break; case svn_wc_notify_changelist_set: - if ((err = svn_cmdline_printf(pool, "A [%s] %s\n", - n->changelist_name, path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "A [%s] %s\n", + n->changelist_name, path_local)); break; case svn_wc_notify_changelist_clear: case svn_wc_notify_changelist_moved: - if ((err = svn_cmdline_printf(pool, - "D [%s] %s\n", - n->changelist_name, path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + "D [%s] %s\n", + n->changelist_name, path_local)); break; case svn_wc_notify_merge_begin: if (n->merge_range == NULL) - err = svn_cmdline_printf(pool, - _("--- Merging differences between " - "repository URLs into '%s':\n"), - path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Merging differences between " + "repository URLs into '%s':\n"), + path_local)); else if (n->merge_range->start == n->merge_range->end - 1 || n->merge_range->start == n->merge_range->end) - err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"), - n->merge_range->end, path_local); + SVN_ERR(svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"), + n->merge_range->end, path_local)); else if (n->merge_range->start - 1 == n->merge_range->end) - err = svn_cmdline_printf(pool, - _("--- Reverse-merging r%ld into '%s':\n"), - n->merge_range->start, path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Reverse-merging r%ld into '%s':\n"), + n->merge_range->start, path_local)); else if (n->merge_range->start < n->merge_range->end) - err = svn_cmdline_printf(pool, - _("--- Merging r%ld through r%ld into " - "'%s':\n"), - n->merge_range->start + 1, - n->merge_range->end, path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Merging r%ld through r%ld into " + "'%s':\n"), + n->merge_range->start + 1, + n->merge_range->end, path_local)); else /* n->merge_range->start > n->merge_range->end - 1 */ - err = svn_cmdline_printf(pool, - _("--- Reverse-merging r%ld through r%ld " - "into '%s':\n"), - n->merge_range->start, - n->merge_range->end + 1, path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("--- Reverse-merging r%ld through r%ld " + "into '%s':\n"), + n->merge_range->start, + n->merge_range->end + 1, path_local)); break; case svn_wc_notify_merge_record_info_begin: if (!n->merge_range) { - err = svn_cmdline_printf(pool, - _("--- Recording mergeinfo for merge " - "between repository URLs into '%s':\n"), - path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Recording mergeinfo for merge " + "between repository URLs into '%s':\n"), + path_local)); } else { if (n->merge_range->start == n->merge_range->end - 1 || n->merge_range->start == n->merge_range->end) - err = svn_cmdline_printf( + SVN_ERR(svn_cmdline_printf( pool, _("--- Recording mergeinfo for merge of r%ld into '%s':\n"), - n->merge_range->end, path_local); + n->merge_range->end, path_local)); else if (n->merge_range->start - 1 == n->merge_range->end) - err = svn_cmdline_printf( + SVN_ERR(svn_cmdline_printf( pool, _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"), - n->merge_range->start, path_local); + n->merge_range->start, path_local)); else if (n->merge_range->start < n->merge_range->end) - err = svn_cmdline_printf( + SVN_ERR(svn_cmdline_printf( pool, _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"), - n->merge_range->start + 1, n->merge_range->end, path_local); + n->merge_range->start + 1, n->merge_range->end, path_local)); else /* n->merge_range->start > n->merge_range->end - 1 */ - err = svn_cmdline_printf( + SVN_ERR(svn_cmdline_printf( pool, _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"), - n->merge_range->start, n->merge_range->end + 1, path_local); + n->merge_range->start, n->merge_range->end + 1, path_local)); } - - if (err) - goto print_error; break; case svn_wc_notify_merge_elide_info: - if ((err = svn_cmdline_printf(pool, - _("--- Eliding mergeinfo from '%s':\n"), - path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("--- Eliding mergeinfo from '%s':\n"), + path_local)); break; case svn_wc_notify_foreign_merge_begin: if (n->merge_range == NULL) - err = svn_cmdline_printf(pool, - _("--- Merging differences between " - "foreign repository URLs into '%s':\n"), - path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Merging differences between " + "foreign repository URLs into '%s':\n"), + path_local)); else if (n->merge_range->start == n->merge_range->end - 1 || n->merge_range->start == n->merge_range->end) - err = svn_cmdline_printf(pool, - _("--- Merging (from foreign repository) " - "r%ld into '%s':\n"), - n->merge_range->end, path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Merging (from foreign repository) " + "r%ld into '%s':\n"), + n->merge_range->end, path_local)); else if (n->merge_range->start - 1 == n->merge_range->end) - err = svn_cmdline_printf(pool, - _("--- Reverse-merging (from foreign " - "repository) r%ld into '%s':\n"), - n->merge_range->start, path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Reverse-merging (from foreign " + "repository) r%ld into '%s':\n"), + n->merge_range->start, path_local)); else if (n->merge_range->start < n->merge_range->end) - err = svn_cmdline_printf(pool, - _("--- Merging (from foreign repository) " - "r%ld through r%ld into '%s':\n"), - n->merge_range->start + 1, - n->merge_range->end, path_local); + SVN_ERR(svn_cmdline_printf(pool, + _("--- Merging (from foreign repository) " + "r%ld through r%ld into '%s':\n"), + n->merge_range->start + 1, + n->merge_range->end, path_local)); else /* n->merge_range->start > n->merge_range->end - 1 */ - err = svn_cmdline_printf(pool, - _("--- Reverse-merging (from foreign " - "repository) r%ld through r%ld into " - "'%s':\n"), - n->merge_range->start, - n->merge_range->end + 1, path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("--- Reverse-merging (from foreign " + "repository) r%ld through r%ld into " + "'%s':\n"), + n->merge_range->start, + n->merge_range->end + 1, path_local)); break; case svn_wc_notify_tree_conflict: store_path(nb, nb->conflict_stats->tree_conflicts, path_local); - if ((err = svn_cmdline_printf(pool, " C %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, " C %s\n", path_local)); break; case svn_wc_notify_update_shadowed_add: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, " A %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, " A %s\n", path_local)); break; case svn_wc_notify_update_shadowed_update: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, " U %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, " U %s\n", path_local)); break; case svn_wc_notify_update_shadowed_delete: nb->received_some_change = TRUE; - if ((err = svn_cmdline_printf(pool, " D %s\n", path_local))) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, " D %s\n", path_local)); break; case svn_wc_notify_property_modified: case svn_wc_notify_property_added: - err = svn_cmdline_printf(pool, - _("property '%s' set on '%s'\n"), - n->prop_name, path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("property '%s' set on '%s'\n"), + n->prop_name, path_local)); break; case svn_wc_notify_property_deleted: - err = svn_cmdline_printf(pool, - _("property '%s' deleted from '%s'.\n"), - n->prop_name, path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("property '%s' deleted from '%s'.\n"), + n->prop_name, path_local)); break; case svn_wc_notify_property_deleted_nonexistent: - err = svn_cmdline_printf(pool, - _("Attempting to delete nonexistent " - "property '%s' on '%s'\n"), n->prop_name, - path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Attempting to delete nonexistent " + "property '%s' on '%s'\n"), n->prop_name, + path_local)); break; case svn_wc_notify_revprop_set: - err = svn_cmdline_printf(pool, + SVN_ERR(svn_cmdline_printf(pool, _("property '%s' set on repository revision %ld\n"), - n->prop_name, n->revision); - if (err) - goto print_error; + n->prop_name, n->revision)); break; case svn_wc_notify_revprop_deleted: - err = svn_cmdline_printf(pool, + SVN_ERR(svn_cmdline_printf(pool, _("property '%s' deleted from repository revision %ld\n"), - n->prop_name, n->revision); - if (err) - goto print_error; + n->prop_name, n->revision)); break; case svn_wc_notify_upgraded_path: - err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local)); break; case svn_wc_notify_url_redirect: - err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"), - n->url); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"), + n->url)); break; case svn_wc_notify_path_nonexistent: - err = svn_cmdline_printf(pool, "%s\n", - apr_psprintf(pool, _("'%s' is not under version control"), - path_local)); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, "%s\n", + apr_psprintf(pool, _("'%s' is not under version control"), + path_local))); break; case svn_wc_notify_conflict_resolver_starting: @@ -1110,37 +1039,56 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) case svn_wc_notify_foreign_copy_begin: if (n->merge_range == NULL) { - err = svn_cmdline_printf( + SVN_ERR(svn_cmdline_printf( pool, _("--- Copying from foreign repository URL '%s':\n"), - n->url); - if (err) - goto print_error; + n->url)); } break; case svn_wc_notify_move_broken: - err = svn_cmdline_printf(pool, - _("Breaking move with source path '%s'\n"), - path_local); - if (err) - goto print_error; + SVN_ERR(svn_cmdline_printf(pool, + _("Breaking move with source path '%s'\n"), + path_local)); + break; + + case svn_wc_notify_cleanup_external: + SVN_ERR(svn_cmdline_printf + (pool, _("Performing cleanup on external item at '%s'.\n"), + path_local)); + break; + + case svn_wc_notify_commit_finalizing: + if (nb->sent_first_txdelta) + { + SVN_ERR(svn_cmdline_printf(pool, _("done\n"))); + } + SVN_ERR(svn_cmdline_printf(pool, _("Committing transaction...\n"))); break; default: break; } - if ((err = svn_cmdline_fflush(stdout))) - goto print_error; + SVN_ERR(svn_cmdline_fflush(stdout)); - return; + return SVN_NO_ERROR; +} + +/* This implements `svn_wc_notify_func2_t'. + * NOTE: This function can't fail, so we just ignore any print errors. */ +static void +notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) +{ + struct notify_baton *nb = baton; + svn_error_t *err; + + err = notify_body(nb, n, pool); - print_error: /* If we had no errors before, print this error to stderr. Else, don't print anything. The user already knows there were some output errors, so there is no point in flooding her with an error per notification. */ - if (!nb->had_print_error) + if (err && !nb->had_print_error) { nb->had_print_error = TRUE; /* Issue #3014: @@ -1156,7 +1104,6 @@ notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) svn_error_clear(err); } - svn_error_t * svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p, void **notify_baton_p, @@ -1170,7 +1117,7 @@ svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p, nb->is_checkout = FALSE; nb->is_export = FALSE; nb->is_wc_to_repos_copy = FALSE; - nb->in_external = FALSE; + nb->in_external = 0; nb->had_print_error = FALSE; nb->conflict_stats = conflict_stats; SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool)); diff --git a/contrib/subversion/subversion/svn/propget-cmd.c b/contrib/subversion/subversion/svn/propget-cmd.c index e29191184..fa04c202c 100644 --- a/contrib/subversion/subversion/svn/propget-cmd.c +++ b/contrib/subversion/subversion/svn/propget-cmd.c @@ -44,7 +44,8 @@ #include "cl.h" #include "private/svn_cmdline_private.h" - +#include "private/svn_opt_private.h" +#include "private/svn_sorts_private.h" #include "svn_private_config.h" @@ -88,7 +89,7 @@ print_properties_xml(const char *pname, const char *name_local; svn_prop_inherited_item_t *iprop = APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); - svn_string_t *propval = svn__apr_hash_index_val( + svn_string_t *propval = apr_hash_this_val( apr_hash_first(pool, iprop->prop_hash)); sb = NULL; @@ -100,7 +101,7 @@ print_properties_xml(const char *pname, name_local = svn_dirent_local_style(iprop->path_or_url, iterpool); svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target", - "path", name_local, NULL); + "path", name_local, SVN_VA_NULL); svn_cmdline__print_xml_prop(&sb, pname, propval, TRUE, iterpool); svn_xml_make_close_tag(&sb, iterpool, "target"); @@ -123,7 +124,7 @@ print_properties_xml(const char *pname, svn_pool_clear(iterpool); svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target", - "path", filename, NULL); + "path", filename, SVN_VA_NULL); svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE, iterpool); svn_xml_make_close_tag(&sb, iterpool, "target"); @@ -277,7 +278,7 @@ print_properties(svn_stream_t *out, { svn_prop_inherited_item_t *iprop = APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); - svn_string_t *propval = svn__apr_hash_index_val(apr_hash_first(pool, + svn_string_t *propval = apr_hash_this_val(apr_hash_first(pool, iprop->prop_hash)); SVN_ERR(print_single_prop(propval, target_abspath_or_url, iprop->path_or_url, @@ -319,12 +320,13 @@ svn_cl__propget(apr_getopt_t *os, const char *pname, *pname_utf8; apr_array_header_t *args, *targets; svn_stream_t *out; + svn_boolean_t warned = FALSE; - if (opt_state->verbose && (opt_state->revprop || opt_state->strict + if (opt_state->verbose && (opt_state->revprop || opt_state->no_newline || opt_state->xml)) return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, _("--verbose cannot be used with --revprop or " - "--strict or --xml")); + "--no-newline or --xml")); /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version thereof) */ @@ -365,7 +367,17 @@ svn_cl__propget(apr_getopt_t *os, URL, &(opt_state->start_revision), &rev, ctx, pool)); - if (propval != NULL) + if (propval == NULL) + { + return svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL, + _("Property '%s' not found on " + "revision %s"), + pname_utf8, + svn_opt__revision_to_string( + &opt_state->start_revision, + pool)); + } + else { if (opt_state->xml) { @@ -376,7 +388,7 @@ svn_cl__propget(apr_getopt_t *os, svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", - "rev", revstr, NULL); + "rev", revstr, SVN_VA_NULL); svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE, pool); @@ -399,7 +411,7 @@ svn_cl__propget(apr_getopt_t *os, SVN_ERR(stream_write(out, printable_val->data, printable_val->len)); - if (! opt_state->strict) + if (! opt_state->no_newline) SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR))); } } @@ -415,15 +427,16 @@ svn_cl__propget(apr_getopt_t *os, if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_empty; - /* Strict mode only makes sense for a single target. So make + /* No-newline mode only makes sense for a single target. So make sure we have only a single target, and that we're not being asked to recurse on that target. */ - if (opt_state->strict - && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty))) + if (opt_state->no_newline + && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty) + || (opt_state->show_inherited_props))) return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Strict output of property values only available for single-" - "target, non-recursive propget operations")); + _("--no-newline is only available for single-target," + " non-recursive propget operations")); for (i = 0; i < targets->nelts; i++) { @@ -459,15 +472,30 @@ svn_cl__propget(apr_getopt_t *os, /* Any time there is more than one thing to print, or where the path associated with a printed thing is not obvious, we'll print filenames. That is, unless we've been told - not to do so with the --strict option. */ + not to do so with the --no-newline option. */ print_filenames = ((opt_state->depth > svn_depth_empty || targets->nelts > 1 || apr_hash_count(props) > 1 || opt_state->verbose || opt_state->show_inherited_props) - && (! opt_state->strict)); - omit_newline = opt_state->strict; - like_proplist = opt_state->verbose && !opt_state->strict; + && (! opt_state->no_newline)); + omit_newline = opt_state->no_newline; + like_proplist = opt_state->verbose && !opt_state->no_newline; + + /* If there are no properties, and exactly one node was queried, + then warn. */ + if (opt_state->depth == svn_depth_empty + && !opt_state->show_inherited_props + && apr_hash_count(props) == 0) + { + svn_error_t *err; + err = svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL, + _("Property '%s' not found on '%s'"), + pname_utf8, target); + svn_handle_warning2(stderr, err, "svn: "); + svn_error_clear(err); + warned = TRUE; + } if (opt_state->xml) SVN_ERR(print_properties_xml( @@ -489,5 +517,8 @@ svn_cl__propget(apr_getopt_t *os, svn_pool_destroy(subpool); } + if (warned) + return svn_error_create(SVN_ERR_BASE, NULL, NULL); + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/proplist-cmd.c b/contrib/subversion/subversion/svn/proplist-cmd.c index fe23a67f6..80e0364f3 100644 --- a/contrib/subversion/subversion/svn/proplist-cmd.c +++ b/contrib/subversion/subversion/svn/proplist-cmd.c @@ -83,7 +83,7 @@ proplist_receiver_xml(void *baton, name_local = svn_dirent_local_style(iprop->path_or_url, iterpool); svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target", - "path", name_local, NULL); + "path", name_local, SVN_VA_NULL); SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, iprop->prop_hash, (! opt_state->verbose), TRUE, iterpool)); @@ -105,7 +105,7 @@ proplist_receiver_xml(void *baton, { /* "" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target", - "path", name_local, NULL); + "path", name_local, SVN_VA_NULL); SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, prop_hash, (! opt_state->verbose), @@ -230,7 +230,7 @@ svn_cl__proplist(apr_getopt_t *os, svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "revprops", - "rev", revstr, NULL); + "rev", revstr, SVN_VA_NULL); SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, proplist, (! opt_state->verbose), FALSE, scratch_pool)); @@ -299,7 +299,7 @@ svn_cl__proplist(apr_getopt_t *os, errors, opt_state->quiet, SVN_ERR_UNVERSIONED_RESOURCE, SVN_ERR_ENTRY_NOT_FOUND, - SVN_NO_ERROR)); + 0)); } svn_pool_destroy(iterpool); diff --git a/contrib/subversion/subversion/svn/props.c b/contrib/subversion/subversion/svn/props.c index 2a41ac81d..b4a75f54f 100644 --- a/contrib/subversion/subversion/svn/props.c +++ b/contrib/subversion/subversion/svn/props.c @@ -112,59 +112,6 @@ svn_cl__check_boolean_prop_val(const char *propname, const char *propval, } } - -/* Context for sorting property names */ -struct simprop_context_t -{ - svn_string_t name; /* The name of the property we're comparing with */ - svn_membuf_t buffer; /* Buffer for similarity testing */ -}; - -struct simprop_t -{ - const char *propname; /* The original svn: property name */ - svn_string_t name; /* The property name without the svn: prefix */ - unsigned int score; /* The similarity score */ - apr_size_t diff; /* Number of chars different from context.name */ - struct simprop_context_t *context; /* Sorting context for qsort() */ -}; - -/* Similarity test between two property names */ -static APR_INLINE unsigned int -simprop_key_diff(const svn_string_t *key, const svn_string_t *ctx, - svn_membuf_t *buffer, apr_size_t *diff) -{ - apr_size_t lcs; - const unsigned int score = svn_string__similarity(key, ctx, buffer, &lcs); - if (key->len > ctx->len) - *diff = key->len - lcs; - else - *diff = ctx->len - lcs; - return score; -} - -/* Key comparator for qsort for simprop_t */ -static int -simprop_compare(const void *pkeya, const void *pkeyb) -{ - struct simprop_t *const keya = *(struct simprop_t *const *)pkeya; - struct simprop_t *const keyb = *(struct simprop_t *const *)pkeyb; - struct simprop_context_t *const context = keya->context; - - if (keya->score == -1) - keya->score = simprop_key_diff(&keya->name, &context->name, - &context->buffer, &keya->diff); - if (keyb->score == -1) - keyb->score = simprop_key_diff(&keyb->name, &context->name, - &context->buffer, &keyb->diff); - - return (keya->score < keyb->score ? 1 - : (keya->score > keyb->score ? -1 - : (keya->diff > keyb->diff ? 1 - : (keya->diff < keyb->diff ? -1 : 0)))); -} - - static const char* force_prop_option_message(svn_cl__prop_use_t prop_use, const char *prop_name, apr_pool_t *scratch_pool) @@ -174,18 +121,18 @@ force_prop_option_message(svn_cl__prop_use_t prop_use, const char *prop_name, case svn_cl__prop_use_set: return apr_psprintf( scratch_pool, - _("(To set the '%s' property, re-run with '--force'.)"), + _("Use '--force' to set the '%s' property."), prop_name); case svn_cl__prop_use_edit: return apr_psprintf( scratch_pool, - _("(To edit the '%s' property, re-run with '--force'.)"), + _("Use '--force' to edit the '%s' property."), prop_name); case svn_cl__prop_use_use: default: return apr_psprintf( scratch_pool, - _("(To use the '%s' property, re-run with '--force'.)"), + _("Use '--force' to use the '%s' property'."), prop_name); } } @@ -199,21 +146,18 @@ wrong_prop_error_message(svn_cl__prop_use_t prop_use, const char *prop_name, case svn_cl__prop_use_set: return apr_psprintf( scratch_pool, - _("'%s' is not a valid %s property name;" - " re-run with '--force' to set it"), + _("'%s' is not a valid %s property name; use '--force' to set it"), prop_name, SVN_PROP_PREFIX); case svn_cl__prop_use_edit: return apr_psprintf( scratch_pool, - _("'%s' is not a valid %s property name;" - " re-run with '--force' to edit it"), + _("'%s' is not a valid %s property name; use '--force' to edit it"), prop_name, SVN_PROP_PREFIX); case svn_cl__prop_use_use: default: return apr_psprintf( scratch_pool, - _("'%s' is not a valid %s property name;" - " re-run with '--force' to use it"), + _("'%s' is not a valid %s property name; use '--force' to use it"), prop_name, SVN_PROP_PREFIX); } } @@ -239,33 +183,34 @@ svn_cl__check_svn_prop_name(const char *propname, const char *const *const proplist = (revprop ? revprops : nodeprops); const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len); - struct simprop_t **propkeys; - struct simprop_t *propbuf; + svn_cl__simcheck_t **propkeys; + svn_cl__simcheck_t *propbuf; apr_size_t i; - struct simprop_context_t context; + svn_string_t propstring; svn_string_t prefix; + svn_membuf_t buffer; - context.name.data = propname; - context.name.len = strlen(propname); + propstring.data = propname; + propstring.len = strlen(propname); prefix.data = SVN_PROP_PREFIX; prefix.len = strlen(SVN_PROP_PREFIX); - svn_membuf__create(&context.buffer, 0, scratch_pool); + svn_membuf__create(&buffer, 0, scratch_pool); /* First, check if the name is even close to being in the svn: namespace. It must contain a colon in the right place, and we only allow one-char typos or a single transposition. */ - if (context.name.len < prefix.len - || context.name.data[prefix.len - 1] != prefix.data[prefix.len - 1]) + if (propstring.len < prefix.len + || propstring.data[prefix.len - 1] != prefix.data[prefix.len - 1]) return SVN_NO_ERROR; /* Wrong prefix, ignore */ else { apr_size_t lcs; - const apr_size_t name_len = context.name.len; - context.name.len = prefix.len; /* Only check up to the prefix length */ - svn_string__similarity(&context.name, &prefix, &context.buffer, &lcs); - context.name.len = name_len; /* Restore the original propname length */ + const apr_size_t name_len = propstring.len; + propstring.len = prefix.len; /* Only check up to the prefix length */ + svn_string__similarity(&propstring, &prefix, &buffer, &lcs); + propstring.len = name_len; /* Restore the original propname length */ if (lcs < prefix.len - 1) return SVN_NO_ERROR; /* Wrong prefix, ignore */ @@ -276,11 +221,11 @@ svn_cl__check_svn_prop_name(const char *propname, for (i = 0; i < numprops; ++i) { if (0 == strcmp(proplist[i] + prefix.len, propname + prefix.len)) - return svn_error_createf( + return svn_error_quick_wrap(svn_error_createf( SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is not a valid %s property name;" - " did you mean '%s'?\n%s"), - propname, SVN_PROP_PREFIX, proplist[i], + " did you mean '%s'?"), + propname, SVN_PROP_PREFIX, proplist[i]), force_prop_option_message(prop_use, propname, scratch_pool)); } return SVN_NO_ERROR; @@ -292,65 +237,59 @@ svn_cl__check_svn_prop_name(const char *propname, we already know that it's the same and looking at it would only skew the results. */ propkeys = apr_palloc(scratch_pool, - numprops * sizeof(struct simprop_t*)); + numprops * sizeof(svn_cl__simcheck_t*)); propbuf = apr_palloc(scratch_pool, - numprops * sizeof(struct simprop_t)); - context.name.data += prefix.len; - context.name.len -= prefix.len; + numprops * sizeof(svn_cl__simcheck_t)); + propstring.data += prefix.len; + propstring.len -= prefix.len; for (i = 0; i < numprops; ++i) { propkeys[i] = &propbuf[i]; - propbuf[i].propname = proplist[i]; - propbuf[i].name.data = proplist[i] + prefix.len; - propbuf[i].name.len = strlen(propbuf[i].name.data); - propbuf[i].score = (unsigned int)-1; - propbuf[i].context = &context; + propbuf[i].token.data = proplist[i] + prefix.len; + propbuf[i].token.len = strlen(propbuf[i].token.data); + propbuf[i].data = proplist[i]; } - qsort(propkeys, numprops, sizeof(*propkeys), simprop_compare); - - if (0 == propkeys[0]->diff) - return SVN_NO_ERROR; /* We found an exact match. */ - - /* See if we can suggest a sane alternative spelling */ - for (i = 0; i < numprops; ++i) - if (propkeys[i]->score < 666) /* 2/3 similarity required */ - break; - - switch (i) + switch (svn_cl__similarity_check( + propstring.data, propkeys, numprops, scratch_pool)) { case 0: + return SVN_NO_ERROR; /* We found an exact match. */ + + case 1: /* The best alternative isn't good enough */ return svn_error_create( SVN_ERR_CLIENT_PROPERTY_NAME, NULL, wrong_prop_error_message(prop_use, propname, scratch_pool)); - case 1: + case 2: /* There is only one good candidate */ - return svn_error_createf( + return svn_error_quick_wrap(svn_error_createf( SVN_ERR_CLIENT_PROPERTY_NAME, NULL, - _("'%s' is not a valid %s property name; did you mean '%s'?\n%s"), - propname, SVN_PROP_PREFIX, propkeys[0]->propname, + _("'%s' is not a valid %s property name; did you mean '%s'?"), + propname, SVN_PROP_PREFIX, + (const char *)propkeys[0]->data), force_prop_option_message(prop_use, propname, scratch_pool)); - case 2: + case 3: /* Suggest a list of the most likely candidates */ - return svn_error_createf( + return svn_error_quick_wrap(svn_error_createf( SVN_ERR_CLIENT_PROPERTY_NAME, NULL, - _("'%s' is not a valid %s property name\n" - "Did you mean '%s' or '%s'?\n%s"), + _("'%s' is not a valid %s property name; " + "did you mean '%s' or '%s'?"), propname, SVN_PROP_PREFIX, - propkeys[0]->propname, propkeys[1]->propname, + (const char *)propkeys[0]->data, (const char *)propkeys[1]->data), force_prop_option_message(prop_use, propname, scratch_pool)); default: /* Never suggest more than three candidates */ - return svn_error_createf( + return svn_error_quick_wrap(svn_error_createf( SVN_ERR_CLIENT_PROPERTY_NAME, NULL, - _("'%s' is not a valid %s property name\n" - "Did you mean '%s', '%s' or '%s'?\n%s"), + _("'%s' is not a valid %s property name; " + "did you mean '%s', '%s' or '%s'?"), propname, SVN_PROP_PREFIX, - propkeys[0]->propname, propkeys[1]->propname, propkeys[2]->propname, + (const char *)propkeys[0]->data, + (const char *)propkeys[1]->data, (const char *)propkeys[2]->data), force_prop_option_message(prop_use, propname, scratch_pool)); } } diff --git a/contrib/subversion/subversion/svn/resolve-cmd.c b/contrib/subversion/subversion/svn/resolve-cmd.c index ce4818ead..035bf29af 100644 --- a/contrib/subversion/subversion/svn/resolve-cmd.c +++ b/contrib/subversion/subversion/svn/resolve-cmd.c @@ -123,7 +123,7 @@ svn_cl__resolve(apr_getopt_t *os, svn_pool_destroy(iterpool); if (had_error) - return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL, + return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Failure occurred resolving one or more " "conflicts")); diff --git a/contrib/subversion/subversion/svn/revert-cmd.c b/contrib/subversion/subversion/svn/revert-cmd.c index d17aeb6f7..74d7ff185 100644 --- a/contrib/subversion/subversion/svn/revert-cmd.c +++ b/contrib/subversion/subversion/svn/revert-cmd.c @@ -67,8 +67,11 @@ svn_cl__revert(apr_getopt_t *os, SVN_ERR(svn_cl__check_targets_are_local_paths(targets)); - err = svn_client_revert2(targets, opt_state->depth, - opt_state->changelists, ctx, scratch_pool); + err = svn_client_revert3(targets, opt_state->depth, + opt_state->changelists, + FALSE /* clear_changelists */, + FALSE /* metadata_only */, + ctx, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH) && (! SVN_DEPTH_IS_RECURSIVE(opt_state->depth))) diff --git a/contrib/subversion/subversion/svn/similarity.c b/contrib/subversion/subversion/svn/similarity.c new file mode 100644 index 000000000..0bcf0f559 --- /dev/null +++ b/contrib/subversion/subversion/svn/similarity.c @@ -0,0 +1,126 @@ +/* + * similarity.c: Utility functions for finding similar strings in lists + * + * ==================================================================== + * 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 + +#include "svn_string.h" +#include "cl.h" + +#include "private/svn_string_private.h" + +#include "svn_private_config.h" + + +/* Context for token similarity checking */ +struct svn_cl__simcheck_context_t +{ + svn_string_t key; /* The token we're comparing with */ + svn_membuf_t buffer; /* Buffer for similarity testing */ +}; + + +/* Similarity test between two property names */ +static APR_INLINE apr_size_t +simcheck_key_diff(const svn_string_t *key, const svn_string_t *ctx, + svn_membuf_t *buffer, apr_size_t *diff) +{ + apr_size_t lcs; + const apr_size_t score = svn_string__similarity(key, ctx, buffer, &lcs); + if (key->len > ctx->len) + *diff = key->len - lcs; + else + *diff = ctx->len - lcs; + return score; +} + + +/* Key comparator for qsort for svn_cl__simcheck_t */ +static int +simcheck_compare(const void *pkeya, const void *pkeyb) +{ + svn_cl__simcheck_t *const keya = *(svn_cl__simcheck_t *const *)pkeya; + svn_cl__simcheck_t *const keyb = *(svn_cl__simcheck_t *const *)pkeyb; + svn_cl__simcheck_context_t *const context = keya->context; + + if (keya->score == -1) + keya->score = simcheck_key_diff(&keya->token, &context->key, + &context->buffer, &keya->diff); + if (keyb->score == -1) + keyb->score = simcheck_key_diff(&keyb->token, &context->key, + &context->buffer, &keyb->diff); + + return (keya->score < keyb->score ? 1 + : (keya->score > keyb->score ? -1 + : (keya->diff > keyb->diff ? 1 + : (keya->diff < keyb->diff ? -1 : 0)))); +} + +apr_size_t +svn_cl__similarity_check(const char *key, + svn_cl__simcheck_t **tokens, + apr_size_t token_count, + apr_pool_t *scratch_pool) +{ + apr_size_t result; + apr_size_t i; + + svn_cl__simcheck_context_t context; + context.key.data = key; + context.key.len = strlen(key); + svn_membuf__create(&context.buffer, 0, scratch_pool); + + /* Populate the score, diff and context members. */ + for (i = 0; i < token_count; ++i) + { + svn_cl__simcheck_t *const token = tokens[i]; + token->score = -1; + token->diff = 0; + token->context = &context; + } + + /* Sort the tokens by similarity. */ + qsort(tokens, token_count, sizeof(*tokens), simcheck_compare); + + /* Remove references to the context, since it points to the stack, + and calculate the number of results that are at least two-thirds + similar to the key. */ + for (i = 0, result = 1; i < token_count; ++i) + { + svn_cl__simcheck_t *const token = tokens[i]; + token->context = NULL; + /* If you update this factor, consider updating + * ../libsvn_subr/cmdline.c:most_similar(). */ + if (token->score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3) + ++result; + } + + if (0 == tokens[0]->diff) + return 0; /* We found an exact match. */ + return result; +} diff --git a/contrib/subversion/subversion/svn/status-cmd.c b/contrib/subversion/subversion/svn/status-cmd.c index 9840cd2bd..7692eb3a7 100644 --- a/contrib/subversion/subversion/svn/status-cmd.c +++ b/contrib/subversion/subversion/svn/status-cmd.c @@ -115,7 +115,7 @@ print_start_target_xml(const char *target, apr_pool_t *pool) svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target", - "path", target, NULL); + "path", target, SVN_VA_NULL); return svn_cl__error_checked_fputs(sb->data, stdout); } @@ -135,7 +135,7 @@ print_finish_target_xml(svn_revnum_t repos_rev, const char *repos_rev_str; repos_rev_str = apr_psprintf(pool, "%ld", repos_rev); svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "against", - "revision", repos_rev_str, NULL); + "revision", repos_rev_str, SVN_VA_NULL); } svn_xml_make_close_tag(&sb, pool, "target"); @@ -187,7 +187,7 @@ print_status(void *baton, * ### _read_info() returns. The svn_wc_status_func4_t callback is * ### suppposed to handle the gathering of additional information from the * ### WORKING nodes on its own. Until we've agreed on how the CLI should - * ### handle the revision information, we use this appproach to stay compat + * ### handle the revision information, we use this approach to stay compat * ### with our testsuite. */ if (status->versioned && !SVN_IS_VALID_REVNUM(status->revision) @@ -288,8 +288,15 @@ svn_cl__status(apr_getopt_t *os, SVN_ERR(svn_cl__check_targets_are_local_paths(targets)); - /* We want our -u statuses to be against HEAD. */ - rev.kind = svn_opt_revision_head; + /* We want our -u statuses to be against HEAD by default. */ + if (opt_state->start_revision.kind == svn_opt_revision_unspecified) + rev.kind = svn_opt_revision_head; + else if (! opt_state->update) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--revision (-r) option valid only with " + "--show-updates (-u) option")); + else + rev = opt_state->start_revision; sb.had_print_error = FALSE; @@ -345,10 +352,11 @@ svn_cl__status(apr_getopt_t *os, /* Retrieve a hash of status structures with the information requested by the user. */ - SVN_ERR(svn_cl__try(svn_client_status5(&repos_rev, ctx, target, &rev, + SVN_ERR(svn_cl__try(svn_client_status6(&repos_rev, ctx, target, &rev, opt_state->depth, opt_state->verbose, opt_state->update, + TRUE /* check_working_copy */, opt_state->no_ignore, opt_state->ignore_externals, FALSE /* depth_as_sticky */, @@ -359,13 +367,13 @@ svn_cl__status(apr_getopt_t *os, /* not versioned: */ SVN_ERR_WC_NOT_WORKING_COPY, SVN_ERR_WC_PATH_NOT_FOUND, - SVN_NO_ERROR)); + 0)); if (opt_state->xml) SVN_ERR(print_finish_target_xml(repos_rev, iterpool)); } - /* If any paths were cached because they were associatied with + /* If any paths were cached because they were associated with changelists, we can now display them as grouped changelists. */ if (apr_hash_count(master_cl_hash) > 0) { @@ -378,8 +386,8 @@ svn_cl__status(apr_getopt_t *os, for (hi = apr_hash_first(scratch_pool, master_cl_hash); hi; hi = apr_hash_next(hi)) { - const char *changelist_name = svn__apr_hash_index_key(hi); - apr_array_header_t *path_array = svn__apr_hash_index_val(hi); + const char *changelist_name = apr_hash_this_key(hi); + apr_array_header_t *path_array = apr_hash_this_val(hi); int j; /* ### TODO: For non-XML output, we shouldn't print the @@ -390,7 +398,7 @@ svn_cl__status(apr_getopt_t *os, svn_stringbuf_setempty(buf); svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal, "changelist", "name", changelist_name, - NULL); + SVN_VA_NULL); SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout)); } else diff --git a/contrib/subversion/subversion/svn/status.c b/contrib/subversion/subversion/svn/status.c index 9f1ad341b..9ab9c5926 100644 --- a/contrib/subversion/subversion/svn/status.c +++ b/contrib/subversion/subversion/svn/status.c @@ -138,37 +138,38 @@ generate_status_desc(enum svn_wc_status_kind status) /* Make a relative path containing '..' elements as needed. TARGET_ABSPATH shall be the absolute version of TARGET_PATH. - TARGET_ABSPATH, TARGET_PATH and PATH shall be canonical. + TARGET_ABSPATH, TARGET_PATH and LOCAL_ABSPATH shall be canonical If above conditions are met, a relative path that leads to PATH from TARGET_PATH is returned, but there is no error checking involved. The returned path is allocated from RESULT_POOL, all other - allocations are made in SCRATCH_POOL. */ + allocations are made in SCRATCH_POOL. + + Note that it is not possible to just join the resulting path to + reconstruct the real path as the "../" paths are relative from + a different base than the normal relative paths! + */ static const char * make_relpath(const char *target_abspath, const char *target_path, - const char *path, + const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *la; const char *parent_dir_els = ""; - const char *abspath, *relative; - svn_error_t *err = svn_dirent_get_absolute(&abspath, path, scratch_pool); + const char *t_relpath; + const char *p_relpath; - if (err) - { - /* We probably got passed some invalid path. */ - svn_error_clear(err); - return apr_pstrdup(result_pool, path); - } +#ifdef SVN_DEBUG + SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath)); +#endif - relative = svn_dirent_skip_ancestor(target_abspath, abspath); - if (relative) - { - return svn_dirent_join(target_path, relative, result_pool); - } + t_relpath = svn_dirent_skip_ancestor(target_abspath, local_abspath); + + if (t_relpath) + return svn_dirent_join(target_path, t_relpath, result_pool); /* An example: * relative_to_path = /a/b/c @@ -180,17 +181,16 @@ make_relpath(const char *target_abspath, * path = C:/wc * result = C:/wc */ - /* Skip the common ancestor of both paths, here '/a'. */ - la = svn_dirent_get_longest_ancestor(target_abspath, abspath, + la = svn_dirent_get_longest_ancestor(target_abspath, local_abspath, scratch_pool); if (*la == '\0') { /* Nothing in common: E.g. C:/ vs F:/ on Windows */ - return apr_pstrdup(result_pool, path); + return apr_pstrdup(result_pool, local_abspath); } - relative = svn_dirent_skip_ancestor(la, target_abspath); - path = svn_dirent_skip_ancestor(la, path); + t_relpath = svn_dirent_skip_ancestor(la, target_abspath); + p_relpath = svn_dirent_skip_ancestor(la, local_abspath); /* In above example, we'd now have: * relative_to_path = b/c @@ -198,14 +198,14 @@ make_relpath(const char *target_abspath, /* Count the elements of relative_to_path and prepend as many '..' elements * to path. */ - while (*relative) + while (*t_relpath) { - svn_dirent_split(&relative, NULL, relative, - scratch_pool); + t_relpath = svn_dirent_dirname(t_relpath, scratch_pool); parent_dir_els = svn_dirent_join(parent_dir_els, "..", scratch_pool); } - return svn_dirent_join(parent_dir_els, path, result_pool); + /* This returns a ../ style path relative from the status target */ + return svn_dirent_join(parent_dir_els, p_relpath, result_pool); } @@ -232,8 +232,6 @@ print_status(const char *target_abspath, const char *moved_from_line = ""; const char *moved_to_line = ""; - path = make_relpath(target_abspath, target_path, path, pool, pool); - /* For historic reasons svn ignores the property status for added nodes, even if these nodes were copied and have local property changes. @@ -317,7 +315,7 @@ print_status(const char *target_abspath, apr_psprintf(pool, _("swapped places with %s"), relpath), - (char *)NULL); + SVN_VA_NULL); } else if (status->moved_from_abspath || status->moved_to_abspath) { @@ -332,7 +330,7 @@ print_status(const char *target_abspath, moved_from_line = apr_pstrcat(pool, "\n > ", apr_psprintf(pool, _("moved from %s"), relpath), - (char *)NULL); + SVN_VA_NULL); } if (status->moved_to_abspath) @@ -344,7 +342,7 @@ print_status(const char *target_abspath, moved_to_line = apr_pstrcat(pool, "\n > ", apr_psprintf(pool, _("moved to %s"), relpath), - (char *)NULL); + SVN_VA_NULL); } } @@ -487,10 +485,9 @@ svn_cl__print_status_xml(const char *target_abspath, SVN_ERR(svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, pool)); - path = make_relpath(target_abspath, target_path, path, pool, pool); - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", - "path", svn_dirent_local_style(path, pool), NULL); + "path", svn_dirent_local_style(path, pool), + SVN_VA_NULL); att_hash = apr_hash_make(pool); svn_hash_sets(att_hash, "item", @@ -560,7 +557,7 @@ svn_cl__print_status_xml(const char *target_abspath, generate_status_desc(combined_repos_status(status)), "props", generate_status_desc(status->repos_prop_status), - NULL); + SVN_VA_NULL); if (status->repos_lock) svn_cl__print_xml_lock(&sb, status->repos_lock, pool); diff --git a/contrib/subversion/subversion/svn/svn.c b/contrib/subversion/subversion/svn/svn.c index c2db8bf68..058be707d 100644 --- a/contrib/subversion/subversion/svn/svn.c +++ b/contrib/subversion/subversion/svn/svn.c @@ -110,7 +110,7 @@ typedef enum svn_cl__longopt_t { opt_remove, opt_revprop, opt_stop_on_copy, - opt_strict, + opt_strict, /* ### DEPRECATED */ opt_targets, opt_depth, opt_set_depth, @@ -125,6 +125,7 @@ typedef enum svn_cl__longopt_t { opt_show_revs, opt_reintegrate, opt_trust_server_cert, + opt_trust_server_cert_failures, opt_strip, opt_ignore_keywords, opt_reverse_diff, @@ -134,7 +135,14 @@ typedef enum svn_cl__longopt_t { opt_include_externals, opt_show_inherited_props, opt_search, - opt_search_and + opt_search_and, + opt_mergeinfo_log, + opt_remove_unversioned, + opt_remove_ignored, + opt_no_newline, + opt_show_passwords, + opt_pin_externals, + opt_show_item, } svn_cl__longopt_t; @@ -186,7 +194,10 @@ const apr_getopt_option_t svn_cl__options[] = {"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")}, + {"password", opt_auth_password, 1, + N_("specify a password ARG (caution: on many operating\n" + " " + "systems, other users will be able to see this)")}, {"extensions", 'x', 1, N_("Specify differencing options for external diff or\n" " " @@ -204,6 +215,8 @@ const apr_getopt_option_t svn_cl__options[] = " " " --ignore-eol-style: Ignore changes in EOL style\n" " " + " -U ARG, --context ARG: Show ARG lines of context\n" + " " " -p, --show-c-function: Show C function name")}, {"targets", opt_targets, 1, N_("pass contents of file ARG as additional args")}, @@ -216,7 +229,7 @@ const apr_getopt_option_t svn_cl__options[] = " " "'empty', 'files', 'immediates', or 'infinity')")}, {"xml", opt_xml, 0, N_("output in XML")}, - {"strict", opt_strict, 0, N_("use strict semantics")}, + {"strict", opt_strict, 0, N_("DEPRECATED")}, {"stop-on-copy", opt_stop_on_copy, 0, N_("do not cross copies while traversing history")}, {"no-ignore", opt_no_ignore, 0, @@ -226,11 +239,23 @@ const apr_getopt_option_t svn_cl__options[] = {"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" + N_("deprecated; same as\n" + " " + "--trust-server-cert-failures=unknown-ca")}, + {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, + N_("with --non-interactive, accept SSL server\n" + " " + "certificates with failures; ARG is comma-separated\n" + " " + "list of 'unknown-ca' (Unknown Authority),\n" " " - "certificate authorities without prompting (but only\n" + "'cn-mismatch' (Hostname mismatch), 'expired'\n" " " - "with '--non-interactive')") }, + "(Expired certificate), 'not-yet-valid' (Not yet\n" + " " + "valid certificate) and 'other' (all other not\n" + " " + "separately classified certificate errors).")}, {"non-interactive", opt_non_interactive, 0, N_("do no interactive prompting (default is to prompt\n" " " @@ -369,17 +394,36 @@ const apr_getopt_option_t svn_cl__options[] = " " "Please run 'svn update' instead.")}, {"include-externals", opt_include_externals, 0, - N_("Also commit file and dir externals reached by\n" + N_("also operate on externals defined by\n" " " - "recursion. This does not include externals with a\n" - " " - "fixed revision. (See the svn:externals property)")}, + "svn:externals properties")}, {"show-inherited-props", opt_show_inherited_props, 0, - N_("retrieve target's inherited properties")}, + N_("retrieve properties set on parents of the target")}, {"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")}, + {"log", opt_mergeinfo_log, 0, + N_("show revision log message, author and date")}, + {"remove-unversioned", opt_remove_unversioned, 0, + N_("remove unversioned items")}, + {"remove-ignored", opt_remove_ignored, 0, N_("remove ignored items")}, + {"no-newline", opt_no_newline, 0, N_("do not output the trailing newline")}, + {"show-passwords", opt_show_passwords, 0, N_("show cached passwords")}, + {"pin-externals", opt_pin_externals, 0, + N_("pin externals with no explicit revision to their\n" + " " + "current revision (recommended when tagging)")}, + {"show-item", opt_show_item, 1, + N_("print only the item identified by ARG ('kind',\n" + " " + "'url', 'relative-url', 'repos-root-url',\n" + " " + "'repos-uuid', 'revision', 'last-changed-revision',\n" + " " + "'last-changed-date', 'last-changed-author',\n" + " " + "'wc-root')")}, /* Long-opt Aliases * @@ -412,8 +456,9 @@ const apr_getopt_option_t svn_cl__options[] = 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 + opt_force_interactive, opt_trust_server_cert, + opt_trust_server_cert_failures, + opt_config_dir, opt_config_options, 0 }; /* Options for giving a log message. (Some of these also have other uses.) @@ -434,13 +479,54 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = opt_no_autoprops, opt_parents }, {{opt_parents, N_("add intermediate parents")}} }, + { "auth", svn_cl__auth, {0}, N_ + ("Manage cached authentication credentials.\n" + "usage: 1. svn auth [PATTERN ...]\n" + "usage: 2. svn auth --remove PATTERN [PATTERN ...]\n" + "\n" + " With no arguments, list all cached authentication credentials.\n" + " Authentication credentials include usernames, passwords,\n" + " SSL certificates, and SSL client-certificate passphrases.\n" + " If PATTERN is specified, only list credentials with attributes matching one\n" + " or more patterns. With the --remove option, remove cached authentication\n" + " credentials matching one or more patterns.\n" + "\n" + " If more than one pattern is specified credentials are considered only they\n" + " match all specified patterns. Patterns are matched case-sensitively and may\n" + " contain glob 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" + " Note that wildcards will usually need to be quoted or escaped on the\n" + " command line because many command shells will interfere by trying to\n" + " expand them.\n"), + { opt_remove, opt_show_passwords }, + { {opt_remove, N_("remove matching authentication credentials")} } + + }, + { "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" + ("Show when each line of a file was last (or\n" + "next) changed.\n" + "usage: blame [-rM:N] TARGET[@REV]...\n" + "\n" + " Annotate each line of a file with the revision number and author of the\n" + " last change (or optionally the next change) to that line.\n" + "\n" + " With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the last revision at or before rN that changed or added the line,\n" + " looking back no further than rM.\n" + "\n" + " With a reverse revision range '-r M:N' where M > N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the next revision after rN that changed or deleted the line,\n" + " looking forward no further than rM.\n" "\n" " If specified, REV determines in which revision the target is first\n" - " looked up.\n"), + " looked up.\n" + "\n" + " Write the annotated result to standard output.\n"), {'r', 'v', 'g', opt_incremental, opt_xml, 'x', opt_force} }, { "cat", svn_cl__cat, {0}, N_ @@ -449,7 +535,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = "\n" " If specified, REV determines in which revision the target is first\n" " looked up.\n"), - {'r'} }, + {'r', opt_ignore_keywords} }, { "changelist", svn_cl__changelist, {"cl"}, N_ ("Associate (or dissociate) changelist CLNAME with the named files.\n" @@ -489,16 +575,25 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = "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" + " By default, finish any unfinished business in the working copy at WCPATH,\n" + " and remove write locks (shown as 'L' by the 'svn status' command) from\n" + " the working copy. Usually, this is only necessary if a Subversion client\n" + " has crashed 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} }, + " the working copy beyond repair!\n" + "\n" + " If the --remove-unversioned option or the --remove-ignored option\n" + " is given, remove any unversioned or ignored items within WCPATH.\n" + " To prevent accidental working copy corruption, unversioned or ignored\n" + " items can only be removed if the working copy is not already locked\n" + " for writing by another Subversion client.\n" + " Note that the 'svn status' command shows unversioned items as '?',\n" + " and ignored items as 'I' if the --no-ignore option is given to it.\n"), + {opt_merge_cmd, opt_remove_unversioned, opt_remove_ignored, + opt_include_externals, 'q'} }, { "commit", svn_cl__commit, {"ci"}, N_("Send changes from your working copy to the repository.\n" @@ -507,7 +602,11 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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"), + " unlocked after a successful commit.\n" + "\n" + " If --include-externals is given, also commit file and directory\n" + " externals reached by recursion. Do not commit externals with a\n" + " fixed revision.\n"), {'q', 'N', opt_depth, opt_targets, opt_no_unlock, SVN_CL__LOG_MSG_OPTIONS, opt_changelist, opt_keep_changelists, opt_include_externals} }, @@ -528,7 +627,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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} }, + {'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS, + opt_pin_externals} }, { "delete", svn_cl__delete, {"del", "remove", "rm"}, N_ ("Remove files and directories from version control.\n" @@ -635,8 +735,24 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = "\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} + " determines in which revision the target is first looked up.\n" + "\n" + " With --show-item, print only the value of one item of information\n" + " about TARGET. One of the following items can be selected:\n" + " kind the kind of TARGET\n" + " url the URL of TARGET in the repository\n" + " relative-url the repository-relative URL\n" + " repos-root-url the repository root URL\n" + " repos-uuid the repository UUID\n" + " revision the revision of TARGET (defaults to BASE\n" + " for working copy paths and HEAD for URLs)\n" + " last-changed-revision the most recent revision in which TARGET\n" + " was changed\n" + " last-changed-date the date of the last-changed revision\n" + " last-changed-author the author of the last-changed revision\n" + " wc-root the root of TARGET's working copy\n"), + {'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml, + opt_changelist, opt_include_externals, opt_show_item, opt_no_newline} }, { "list", svn_cl__list, {"ls"}, N_ @@ -659,8 +775,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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")}} }, + opt_include_externals}, }, { "lock", svn_cl__lock, {0}, N_ ("Lock working copy paths or URLs in the repository, so that\n" @@ -748,12 +863,14 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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, }, + {'r', 'c', 'q', 'v', 'g', 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")}} }, + {'c', N_("the change made in revision ARG")}, + {'v', N_("also print all affected paths")}, + {'q', N_("do not print the log message")}} }, { "merge", svn_cl__merge, {0}, N_ ( /* For this large section, let's keep it unindented for easier @@ -1088,7 +1205,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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'} }, + opt_allow_mixed_revisions, 'v'}, + { { opt_force, N_("force deletions even if deleted contents don't match") } } + }, { "mergeinfo", svn_cl__mergeinfo, {0}, N_ ("Display merge-related information.\n" @@ -1119,7 +1238,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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} }, + {'r', 'R', 'q', 'v', opt_depth, opt_show_revs, opt_mergeinfo_log, + opt_incremental } }, { "mkdir", svn_cl__mkdir, {0}, N_ ("Create a new directory under version control.\n" @@ -1215,7 +1335,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = "\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"), + " TARGET only determines which repository to access.\n" + "\n" + " See 'svn help propset' for descriptions of the svn:* special properties.\n"), {'q', 'R', opt_depth, 'r', opt_revprop, opt_changelist} }, { "propedit", svn_cl__propedit, {"pedit", "pe"}, N_ @@ -1227,7 +1349,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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"), + " See 'svn help propset' for descriptions of the svn:* special properties.\n"), {'r', opt_revprop, SVN_CL__LOG_MSG_OPTIONS, opt_force} }, { "propget", svn_cl__propget, {"pget", "pg"}, N_ @@ -1246,13 +1368,15 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " '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, + " the output looks pretty. With a single TARGET, depth 'empty' and without\n" + " --show-inherited-props, you can use the --no-newline option to disable this\n" + " (useful when redirecting a binary property value to a file, for example).\n" + "\n" + " See 'svn help propset' for descriptions of the svn:* special properties.\n"), + {'v', 'R', opt_depth, 'r', opt_revprop, opt_strict, opt_no_newline, 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")}} }, + {opt_strict, N_("(deprecated; use --no-newline)")}} }, { "proplist", svn_cl__proplist, {"plist", "pl"}, N_ ("List all properties on files, dirs, or revisions.\n" @@ -1265,7 +1389,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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"), + " --verbose'. With --quiet, the paths are not printed.\n" + "\n" + " See 'svn help propset' for descriptions of the svn:* special properties.\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")}, @@ -1350,6 +1476,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " ^/ to the repository root\n" " / to the server root\n" " // to the URL scheme\n" + " ^/../ to a sibling repository beneath the same SVNParentPath location\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" @@ -1452,9 +1579,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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" + " Fourth column: Scheduled commit will create a copy (addition-with-history)\n" + " ' ' no history scheduled with commit (item was newly added)\n" + " '+' history scheduled with commit (item was copied)\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" @@ -1514,8 +1641,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " ! 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}, + { 'u', 'v', 'N', opt_depth, 'r', '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_ @@ -1561,7 +1688,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = { '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")}} + N_("allow switching to a node with no common ancestor")}, + {opt_force, + N_("handle unversioned obstructions as changes")}} }, { "unlock", svn_cl__unlock, {0}, N_ @@ -1619,7 +1748,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = " 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} }, + opt_parents}, + { {opt_force, + N_("handle unversioned obstructions as changes")} } }, { "upgrade", svn_cl__upgrade, {0}, N_ ("Upgrade the metadata storage format for a working copy.\n" @@ -1667,6 +1798,8 @@ signal_handler(int signum) svn_error_t * svn_cl__check_cancel(void *baton) { + /* Cancel baton should be always NULL in command line client. */ + SVN_ERR_ASSERT(baton == NULL); if (cancelled) return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); else @@ -1715,23 +1848,13 @@ add_search_pattern_to_latest_group(svn_cl__opt_state_t *opt_state, /*** 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) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err; int opt_id; @@ -1741,7 +1864,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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; + const char *dash_F_arg = NULL; svn_cl__cmd_baton_t command_baton; svn_auth_baton_t *ab; svn_config_t *cfg_config; @@ -1758,18 +1881,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); /* Check library versions */ - SVN_INT_ERR(check_lib_versions()); + SVN_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)); + SVN_ERR(svn_wc_set_adm_dir("_svn", pool)); } #endif /* Initialize the RA library. */ - SVN_INT_ERR(svn_ra_initialize(pool)); + SVN_ERR(svn_ra_initialize(pool)); /* Init our changelists hash. */ changelists = apr_hash_make(pool); @@ -1787,12 +1910,13 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* No args? Show usage. */ if (argc <= 1) { - SVN_INT_ERR(svn_cl__help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Else, parse options. */ - SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; while (1) @@ -1807,8 +1931,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) break; else if (apr_err) { - SVN_INT_ERR(svn_cl__help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -1817,39 +1942,37 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) switch (opt_id) { case 'l': { - err = svn_cstring_atoi(&opt_state.limit, opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + err = svn_cstring_atoi(&opt_state.limit, utf8_opt_arg); if (err) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, - _("Non-numeric limit argument given")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Non-numeric limit argument given")); } if (opt_state.limit <= 0) { - err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, - _("Argument to --limit must be positive")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Argument to --limit must be positive")); } } 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. */ + /* We store the raw message here. We will convert it to UTF-8 + * later, according to the value of the '--encoding' option. */ 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); + apr_array_header_t *change_revs; + + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + change_revs = svn_cstring_split(utf8_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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Can't specify -c with --old")); } for (i = 0; i < change_revs->nelts; i++) @@ -1877,12 +2000,11 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { 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); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, + NULL, + _("Negative number in range (%s)" + " not supported with -c"), + change_str); } s = end + 1; while (*s == 'r') @@ -1891,17 +2013,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) } 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); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Non-numeric change argument (%s) " + "given to -c"), change_str); } if (changeno == 0) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("There is no change 0")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("There is no change 0")); } if (is_negative) @@ -1937,15 +2057,13 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) break; case 'r': opt_state.used_revision_arg = TRUE; + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); if (svn_opt_parse_revision_to_range(opt_state.revision_ranges, - opt_arg, pool) != 0) + utf8_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, + return 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': @@ -1965,23 +2083,22 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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)); + /* We read the raw file content here. We will convert it to UTF-8 + * later (if it's a log/lock message or an svn:* prop value), + * according to the value of the '--encoding' option. */ + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_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; + dash_F_arg = utf8_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)); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool)); + SVN_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool)); opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r", TRUE, pool); } @@ -2007,51 +2124,47 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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"))); + return 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)); + return 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"))); + return 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)); + return 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)); + SVN_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)); + SVN_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); @@ -2062,9 +2175,6 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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; @@ -2077,8 +2187,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) case opt_force_interactive: force_interactive = TRUE; break; - case opt_trust_server_cert: - opt_state.trust_server_cert = TRUE; + case opt_trust_server_cert: /* backwards compat to 1.8 */ + opt_state.trust_server_cert_unknown_ca = TRUE; + break; + case opt_trust_server_cert_failures: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &opt_state.trust_server_cert_unknown_ca, + &opt_state.trust_server_cert_cn_mismatch, + &opt_state.trust_server_cert_expired, + &opt_state.trust_server_cert_not_yet_valid, + &opt_state.trust_server_cert_other_failure, + utf8_opt_arg, pool)); break; case opt_no_diff_added: opt_state.diff.no_diff_added = TRUE; @@ -2105,8 +2225,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.relocate = TRUE; break; case 'x': - SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.extensions, - opt_arg, pool)); + SVN_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); @@ -2123,22 +2243,19 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Can't specify -c with --old")); } - opt_state.old_target = apr_pstrdup(pool, opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.old_target = apr_pstrdup(pool, utf8_opt_arg); break; case opt_new_cmd: - opt_state.new_target = apr_pstrdup(pool, opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.new_target = apr_pstrdup(pool, utf8_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); - } + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.config_dir = svn_dirent_internal_style(utf8_opt_arg, pool); break; case opt_config_options: if (!opt_state.config_options) @@ -2146,9 +2263,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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)); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(opt_state.config_options, + utf8_opt_arg, "svn: ", pool)); break; case opt_autoprops: opt_state.autoprops = TRUE; @@ -2157,17 +2274,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + if ( !strcmp("LF", utf8_opt_arg) || !strcmp("CR", utf8_opt_arg) || + !strcmp("CRLF", utf8_opt_arg)) + opt_state.native_eol = utf8_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, + return 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: @@ -2180,15 +2295,13 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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') + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + if (utf8_opt_arg[0] == '\0') { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Changelist names must not be empty")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Changelist names must not be empty")); } - svn_hash_sets(changelists, opt_state.changelist, (void *)1); + svn_hash_sets(changelists, utf8_opt_arg, (void *)1); break; case opt_keep_changelists: opt_state.keep_changelists = TRUE; @@ -2205,8 +2318,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.no_revprops = TRUE; break; case opt_with_revprop: - SVN_INT_ERR(svn_opt_parse_revprop(&opt_state.revprop_table, - opt_arg, pool)); + SVN_ERR(svn_opt_parse_revprop(&opt_state.revprop_table, + opt_arg, pool)); break; case opt_parents: opt_state.parents = TRUE; @@ -2215,38 +2328,41 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.use_merge_history = TRUE; break; case opt_accept: - opt_state.accept_which = svn_cl__accept_from_word(opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.accept_which = svn_cl__accept_from_word(utf8_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)); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid --accept value"), + utf8_opt_arg); break; case opt_show_revs: - opt_state.show_revs = svn_cl__show_revs_from_word(opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.show_revs = svn_cl__show_revs_from_word(utf8_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)); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a valid --show-revs value"), + utf8_opt_arg); + break; + case opt_mergeinfo_log: + opt_state.mergeinfo_log = TRUE; break; case opt_reintegrate: opt_state.reintegrate = TRUE; break; case opt_strip: { - err = svn_cstring_atoi(&opt_state.strip, opt_arg); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + err = svn_cstring_atoi(&opt_state.strip, utf8_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); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Invalid strip count '%s'"), + utf8_opt_arg); } if (opt_state.strip < 0) { - err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, - _("Argument to --strip must be positive")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Argument to --strip must be positive")); } } break; @@ -2284,10 +2400,32 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.diff.properties_only = TRUE; break; case opt_search: - add_search_pattern_group(&opt_state, opt_arg, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + add_search_pattern_group(&opt_state, utf8_opt_arg, pool); break; case opt_search_and: - add_search_pattern_to_latest_group(&opt_state, opt_arg, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + add_search_pattern_to_latest_group(&opt_state, utf8_opt_arg, pool); + case opt_remove_unversioned: + opt_state.remove_unversioned = TRUE; + break; + case opt_remove_ignored: + opt_state.remove_ignored = TRUE; + break; + case opt_no_newline: + case opt_strict: /* ### DEPRECATED */ + opt_state.no_newline = TRUE; + break; + case opt_show_passwords: + opt_state.show_passwords = TRUE; + break; + case opt_pin_externals: + opt_state.pin_externals = TRUE; + break; + case opt_show_item: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.show_item = utf8_opt_arg; + break; default: /* Hmmm. Perhaps this would be a good place to squirrel away opts that commands like svn diff might need. Hmmm indeed. */ @@ -2299,10 +2437,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) * 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); } else opt_state.non_interactive = !svn_cmdline__be_interactive( @@ -2310,7 +2447,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) force_interactive); /* Turn our hash of changelists into an array of unique ones. */ - SVN_INT_ERR(svn_hash_keys(&(opt_state.changelists), changelists, pool)); + SVN_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 @@ -2322,7 +2459,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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)); + SVN_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 @@ -2356,7 +2493,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) (svn_cmdline_fprintf(stderr, pool, _("Subcommand argument required\n"))); svn_error_clear(svn_cl__help(NULL, NULL, pool)); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -2367,8 +2505,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) if (subcommand == NULL) { const char *first_arg_utf8; - SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, - first_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, + first_arg, pool)); svn_error_clear (svn_cmdline_fprintf(stderr, pool, _("Unknown subcommand: '%s'\n"), @@ -2385,7 +2523,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) "command.\n"))); } - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -2418,7 +2557,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) (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; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } @@ -2428,11 +2568,10 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments " + "encountered; can't specify -c twice, " + "or both -c and -r")); } } @@ -2440,32 +2579,47 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--depth and --set-depth are mutually " + "exclusive")); } /* 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-all-revprops and --with-no-revprops " + "are mutually exclusive")); } /* 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-revprop and --with-no-revprops " + "are mutually exclusive")); } +#ifdef SVN_CL__OPTION_WITH_REVPROP_CAN_SET_PROPERTIES_IN_SVN_NAMESPACE + /* XXX This is incomplete, since we do not yet check for --force, nor + do all the commands that accept --with-revprop also accept --force. */ + + /* Check the spelling of the revision properties given by --with-revprop. */ + if (opt_state.revprop_table) + { + apr_hash_index_t *hi; + for (hi = apr_hash_first(pool, opt_state.revprop_table); + hi; hi = apr_hash_next(hi)) + { + SVN_ERR(svn_cl__check_svn_prop_name(apr_hash_this_key(hi), + TRUE, svn_cl__prop_use_use, + pool)); + } + } +#endif /* SVN_CL__OPTION_WITH_REVPROP_CAN_SET_PROPERTIES_IN_SVN_NAMESPACE */ + /* 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.) @@ -2473,29 +2627,31 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--message (-m) and --file (-F) " + "are mutually exclusive")); } - /* --trust-server-cert can only be used with --non-interactive */ - if (opt_state.trust_server_cert && !opt_state.non_interactive) + /* --trust-* options can only be used with --non-interactive */ + if (!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); + if (opt_state.trust_server_cert_unknown_ca + || opt_state.trust_server_cert_cn_mismatch + || opt_state.trust_server_cert_expired + || opt_state.trust_server_cert_not_yet_valid + || opt_state.trust_server_cert_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); } /* 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--diff-cmd and --internal-diff " + "are mutually exclusive")); } /* Ensure that 'revision_ranges' has at least one item, and make @@ -2521,18 +2677,13 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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 = 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); + + SVN_ERR(svn_config__get_default_config(&cfg_hash, pool)); } else - return EXIT_ERROR(err); + return err; } /* Relocation is infinite-depth only. */ @@ -2540,18 +2691,16 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { 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); + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("--relocate and --depth are mutually " + "exclusive")); } if (! descend) { - err = svn_error_create( + return svn_error_create( SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, _("--relocate and --non-recursive (-N) are mutually " "exclusive")); - return EXIT_ERROR(err); } } @@ -2565,8 +2714,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { 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); + return svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL); } } @@ -2593,8 +2741,6 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) } } - cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG); - /* Update the options in the config */ if (opt_state.config_options) { @@ -2604,6 +2750,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) "svn: ", "--config-option")); } + cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG); #if !defined(SVN_CL_NO_EXCLUSIVE_LOCK) { const char *exclusive_clients_option; @@ -2633,7 +2780,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* Create a client context object. */ command_baton.opt_state = &opt_state; - SVN_INT_ERR(svn_client_create_context2(&ctx, cfg_hash, pool)); + SVN_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 @@ -2669,19 +2816,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { if (subcommand->cmd_func != svn_cl__lock) { - err = svn_error_create( + return 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( + return 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); @@ -2689,27 +2835,26 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* If the -m argument is a file at all, that's probably not what the user intended. */ - if (dash_m_arg) + if (opt_state.message) { apr_finfo_t finfo; - if (apr_stat(&finfo, dash_m_arg, + if (apr_stat(&finfo, opt_state.message /* not converted to UTF-8 */, APR_FINFO_MIN, pool) == APR_SUCCESS) { if (subcommand->cmd_func != svn_cl__lock) { - err = svn_error_create + return 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 + return 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); } } } @@ -2729,10 +2874,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* 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); + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("--auto-props and --no-auto-props are " + "mutually exclusive")); } /* Update auto-props-enable option, and populate the MIME types map, @@ -2746,8 +2890,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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)); + SVN_ERR(svn_io_parse_mimetypes_file(&(ctx->mimetypes_map), + mimetypes_file, pool)); } if (opt_state.autoprops) @@ -2785,8 +2929,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) use_notifier = FALSE; if (use_notifier) { - SVN_INT_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, - conflict_stats, pool)); + SVN_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, + conflict_stats, pool)); } /* Set up our cancellation support. */ @@ -2816,35 +2960,41 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) #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)); + SVN_ERR(svn_cmdline_create_auth_baton2( + &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_unknown_ca, + opt_state.trust_server_cert_cn_mismatch, + opt_state.trust_server_cert_expired, + opt_state.trust_server_cert_not_yet_valid, + opt_state.trust_server_cert_other_failure, + 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, + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("--accept=%s incompatible with" " --non-interactive"), - SVN_CL__ACCEPT_EDIT)); - + 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, + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("--accept=%s incompatible with" " --non-interactive"), - SVN_CL__ACCEPT_LAUNCH)); + SVN_CL__ACCEPT_LAUNCH); + } /* The default action when we're non-interactive is to postpone * conflict resolution. */ @@ -2855,10 +3005,10 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* 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)); + SVN_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. */ @@ -2879,7 +3029,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) ctx->conflict_baton = NULL; ctx->conflict_func2 = svn_cl__conflict_func_interactive; - SVN_INT_ERR(svn_cl__get_conflict_func_interactive_baton( + SVN_ERR(svn_cl__get_conflict_func_interactive_baton( &b, opt_state.accept_which, ctx->config, opt_state.editor_cmd, conflict_stats, @@ -2896,10 +3046,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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)); + err = svn_error_quick_wrapf( + err, _("Try 'svn help %s' for more information"), + subcommand->name); } if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) { @@ -2952,27 +3101,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) "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); + return 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; - } + return SVN_NO_ERROR; } int main(int argc, const char *argv[]) { apr_pool_t *pool; - int exit_code; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; /* Initialize the app. */ if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS) @@ -2983,7 +3123,17 @@ main(int argc, const char *argv[]) */ pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); - exit_code = sub_main(argc, argv, pool); + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svn: "); + } svn_pool_destroy(pool); return exit_code; diff --git a/contrib/subversion/subversion/svn/unlock-cmd.c b/contrib/subversion/subversion/svn/unlock-cmd.c index 0f94d2a22..3818d1f24 100644 --- a/contrib/subversion/subversion/svn/unlock-cmd.c +++ b/contrib/subversion/subversion/svn/unlock-cmd.c @@ -39,6 +39,29 @@ /*** Code. ***/ +/* Baton for notify_unlock_handler */ +struct notify_unlock_baton_t +{ + void *inner_baton; + svn_wc_notify_func2_t inner_notify; + svn_boolean_t had_failure; +}; + +/* Implements svn_wc_notify_func2_t for svn_cl__unlock */ +static void +notify_unlock_handler(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *scratch_pool) +{ + struct notify_unlock_baton_t *nub = baton; + + if (notify->action == svn_wc_notify_failed_unlock) + nub->had_failure = TRUE; + + if (nub->inner_notify) + nub->inner_notify(nub->inner_baton, notify, scratch_pool); +} + /* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * @@ -49,6 +72,7 @@ svn_cl__unlock(apr_getopt_t *os, 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; + struct notify_unlock_baton_t nub; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, @@ -63,6 +87,18 @@ svn_cl__unlock(apr_getopt_t *os, SVN_ERR(svn_cl__assert_homogeneous_target_type(targets)); - return svn_error_trace( - svn_client_unlock(targets, opt_state->force, ctx, scratch_pool)); + nub.inner_notify = ctx->notify_func2; + nub.inner_baton = ctx->notify_baton2; + nub.had_failure = FALSE; + + ctx->notify_func2 = notify_unlock_handler; + ctx->notify_baton2 = &nub; + + SVN_ERR(svn_client_unlock(targets, opt_state->force, ctx, scratch_pool)); + + if (nub.had_failure) + return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, + _("One or more locks could not be released")); + + return SVN_NO_ERROR; } diff --git a/contrib/subversion/subversion/svn/util.c b/contrib/subversion/subversion/svn/util.c index 64db10168..fb017e9d4 100644 --- a/contrib/subversion/subversion/svn/util.c +++ b/contrib/subversion/subversion/svn/util.c @@ -78,8 +78,15 @@ svn_cl__print_commit_info(const svn_commit_info_t *commit_info, void *baton, apr_pool_t *pool) { + /* Be very careful with returning errors from this callback as those + will be returned as errors from editor->close_edit(...), which may + cause callers to assume that the commit itself failed. + + See log message of r1659867 and the svn_ra_get_commit_editor3 + documentation for details on error scenarios. */ + if (SVN_IS_VALID_REVNUM(commit_info->revision)) - SVN_ERR(svn_cmdline_printf(pool, _("\nCommitted revision %ld%s.\n"), + SVN_ERR(svn_cmdline_printf(pool, _("Committed revision %ld%s.\n"), commit_info->revision, commit_info->revision == 42 && getenv("SVN_I_LOVE_PANGALACTIC_GARGLE_BLASTERS") @@ -167,9 +174,9 @@ svn_cl__merge_file_externally(const char *base_path, * is OK to continue with the merge. * Any other exit code means there was a real problem. */ if (exitcode != 0 && exitcode != 1) - return svn_error_createf - (SVN_ERR_EXTERNAL_PROGRAM, NULL, - _("The external merge tool exited with exit code %d"), exitcode); + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("The external merge tool '%s' exited with exit code %d."), + merge_tool, exitcode); else if (remains_in_conflict) *remains_in_conflict = exitcode == 1; } @@ -200,7 +207,7 @@ svn_cl__make_log_msg_baton(void **baton, apr_hash_t *config, apr_pool_t *pool) { - struct log_msg_baton *lmb = apr_palloc(pool, sizeof(*lmb)); + struct log_msg_baton *lmb = apr_pcalloc(pool, sizeof(*lmb)); if (opt_state->filedata) { @@ -233,8 +240,10 @@ svn_cl__make_log_msg_baton(void **baton, SVN_CONFIG_OPTION_LOG_ENCODING, NULL); } + else + lmb->message_encoding = NULL; - lmb->base_dir = base_dir ? base_dir : ""; + lmb->base_dir = base_dir; lmb->tmpfile_left = NULL; lmb->config = config; lmb->keep_locks = opt_state->no_unlock; @@ -334,10 +343,12 @@ truncate_buffer_at_prefix(apr_size_t *new_len, static const char *prefixes[] = { "PR:", "Submitted by:", + "Reported by:", "Reviewed by:", "Approved by:", "Obtained from:", "MFC after:", + "MFH:", "Relnotes:", "Security:", "Sponsored by:", @@ -410,6 +421,7 @@ svn_cl__get_log_message(const char **log_msg, svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "PR:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Submitted by:\t" APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "Reported by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reviewed by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Approved by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Obtained from:\t" APR_EOL_STR); @@ -418,6 +430,7 @@ svn_cl__get_log_message(const char **log_msg, if (mfc_after != NULL) svn_stringbuf_appendcstr(default_msg, mfc_after); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "MFH:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Relnotes:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Security:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Sponsored by:\t"); @@ -434,8 +447,9 @@ svn_cl__get_log_message(const char **log_msg, svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Description of fields to fill in above: 76 columns --|" APR_EOL_STR); - svn_stringbuf_appendcstr(default_msg, "> PR: If a Bugzilla PR is affected by the change." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> PR: If and which Problem Report is related." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); + svn_stringbuf_appendcstr(default_msg, "> Reported by: If someone else reported the issue." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); @@ -451,23 +465,19 @@ svn_cl__get_log_message(const char **log_msg, *tmp_file = NULL; if (lmb->message) { - svn_stringbuf_t *log_msg_buf = svn_stringbuf_create(lmb->message, pool); - svn_string_t *log_msg_str = apr_pcalloc(pool, sizeof(*log_msg_str)); + svn_string_t *log_msg_str = svn_string_create(lmb->message, pool); - /* Trim incoming messages of the EOF marker text and the junk - that follows it. */ - truncate_buffer_at_prefix(&(log_msg_buf->len), log_msg_buf->data, - EDITOR_EOF_PREFIX); - cleanmsg(NULL, (char*)log_msg_buf->data); - - /* Make a string from a stringbuf, sharing the data allocation. */ - log_msg_str->data = log_msg_buf->data; - log_msg_str->len = log_msg_buf->len; - SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, FALSE, FALSE, + SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, NULL, NULL, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); + /* Strip off the EOF marker text and the junk that follows it. */ + truncate_buffer_at_prefix(&(log_msg_str->len), (char *)log_msg_str->data, + EDITOR_EOF_PREFIX); + + cleanmsg(&(log_msg_str->len), (char*)log_msg_str->data); + *log_msg = log_msg_str->data; return SVN_NO_ERROR; } @@ -497,14 +507,11 @@ svn_cl__get_log_message(const char **log_msg, if (! path) path = item->url; - else if (! *path) - path = "."; - - if (! svn_path_is_url(path) && lmb->base_dir) + else if (lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ - if (! path) + if (! path || !*path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) @@ -543,7 +550,8 @@ svn_cl__get_log_message(const char **log_msg, if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, - lmb->editor_cmd, lmb->base_dir, + lmb->editor_cmd, + lmb->base_dir ? lmb->base_dir : "", msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, @@ -575,7 +583,7 @@ svn_cl__get_log_message(const char **log_msg, if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); - /* Strip the prefix from the buffer. */ + /* Strip off the EOF marker text and the junk that follows it. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); @@ -681,8 +689,8 @@ svn_cl__error_checked_fputs(const char *string, FILE* stream) if (fputs(string, stream) == EOF) { - if (errno) - return svn_error_wrap_apr(errno, _("Write error")); + if (apr_get_os_error()) /* is errno on POSIX */ + return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); else return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); } @@ -747,7 +755,7 @@ svn_cl__xml_tagged_cdata(svn_stringbuf_t **sb, if (string) { svn_xml_make_open_tag(sb, pool, svn_xml_protect_pcdata, - tagname, NULL); + tagname, SVN_VA_NULL); svn_xml_escape_cdata_cstring(sb, string, pool); svn_xml_make_close_tag(sb, pool, tagname); } @@ -764,7 +772,7 @@ svn_cl__print_xml_commit(svn_stringbuf_t **sb, /* "" */ svn_xml_make_open_tag(sb, pool, svn_xml_normal, "commit", "revision", - apr_psprintf(pool, "%ld", revision), NULL); + apr_psprintf(pool, "%ld", revision), SVN_VA_NULL); /* "xx" */ if (author) @@ -785,7 +793,7 @@ svn_cl__print_xml_lock(svn_stringbuf_t **sb, apr_pool_t *pool) { /* "" */ - svn_xml_make_open_tag(sb, pool, svn_xml_normal, "lock", NULL); + svn_xml_make_open_tag(sb, pool, svn_xml_normal, "lock", SVN_VA_NULL); /* "xx" */ svn_cl__xml_tagged_cdata(sb, pool, "token", lock->token); @@ -820,7 +828,7 @@ svn_cl__xml_print_header(const char *tagname, svn_xml_make_header2(&sb, "UTF-8", pool); /* "" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, tagname, NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, tagname, SVN_VA_NULL); return svn_cl__error_checked_fputs(sb->data, stdout); } diff --git a/contrib/subversion/subversion/svn_private_config.h.in b/contrib/subversion/subversion/svn_private_config.h.in index 648c61466..3e9d5923b 100644 --- a/contrib/subversion/subversion/svn_private_config.h.in +++ b/contrib/subversion/subversion/svn_private_config.h.in @@ -22,6 +22,9 @@ /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT +/* Define to 1 if you have the `getpid' function. */ +#undef HAVE_GETPID + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -46,6 +49,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SERF_H +/* Define to 1 if you have the header file. */ +#undef HAVE_STDBOOL_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -115,15 +121,12 @@ /* 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 */ +/* Defined to build 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 @@ -202,6 +205,9 @@ /* Defined if libsvn_fs should link against libsvn_fs_fs */ #undef SVN_LIBSVN_FS_LINKS_FS_FS +/* Defined if libsvn_fs should link against libsvn_fs_x */ +#undef SVN_LIBSVN_FS_LINKS_FS_X + /* Defined to be the path to the installed locale dirs */ #undef SVN_LOCALE_DIR @@ -220,6 +226,9 @@ /* Defined if svn should try to load DSOs */ #undef SVN_USE_DSO +/* Defined to build with patched httpd 2.4 and working auth */ +#undef SVN_USE_FORCE_AUTHN + /* Define to empty if `const' does not conform to ANSI C. */ #undef const @@ -257,3 +266,27 @@ #define dgettext(domain, x) (x) #endif +/* compiler hints */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +# define SVN__PREDICT_FALSE(x) (__builtin_expect(x, 0)) +# define SVN__PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +# define SVN__PREDICT_FALSE(x) (x) +# define SVN__PREDICT_TRUE(x) (x) +#endif + +#if defined(SVN_DEBUG) +# define SVN__FORCE_INLINE +# define SVN__PREVENT_INLINE +#elif defined(__GNUC__) +# define SVN__FORCE_INLINE APR_INLINE __attribute__ ((always_inline)) +# define SVN__PREVENT_INLINE __attribute__ ((noinline)) +#else +# define SVN__FORCE_INLINE APR_INLINE +# define SVN__PREVENT_INLINE +#endif + +/* Macro used to specify that a variable is intentionally left unused. + Supresses compiler warnings about the variable being unused. */ +#define SVN_UNUSED(v) ( (void)(v) ) + diff --git a/contrib/subversion/subversion/svn_private_config.hw b/contrib/subversion/subversion/svn_private_config.hw index 61517f934..4ac6bad1d 100644 --- a/contrib/subversion/subversion/svn_private_config.hw +++ b/contrib/subversion/subversion/svn_private_config.hw @@ -48,7 +48,6 @@ #define SVN_FS_WANT_DB_MINOR 0 #define SVN_FS_WANT_DB_PATCH 14 - /* Path separator for local filesystem */ #define SVN_PATH_LOCAL_SEPARATOR '\\' @@ -58,6 +57,9 @@ /* Link fs fs library into the fs library */ #define SVN_LIBSVN_FS_LINKS_FS_FS +/* Link fs fs library into the fs library */ +#define SVN_LIBSVN_FS_LINKS_FS_X + /* Link local repos access library to client */ #define SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL @@ -101,6 +103,25 @@ #define dgettext(domain, x) (x) #endif +/* compiler hints as supported by MS VC */ +#if defined(SVN_DEBUG) +# define SVN__FORCE_INLINE +# define SVN__PREVENT_INLINE +#elif defined(_MSC_VER) +# define SVN__FORCE_INLINE __forceinline +# define SVN__PREVENT_INLINE __declspec(noinline) +#else +# define SVN__FORCE_INLINE APR_INLINE +# define SVN__PREVENT_INLINE +#endif + +#define SVN__PREDICT_TRUE(x) (x) +#define SVN__PREDICT_FALSE(x) (x) + +/* Macro used to specify that a variable is intentionally left unused. + Supresses compiler warnings about the variable being unused. */ +#define SVN_UNUSED(v) ( (void)(v) ) + #endif /* SVN_PRIVATE_CONFIG_HW */ /* Inclusion of Berkeley DB header */ diff --git a/contrib/subversion/subversion/svnadmin/svnadmin.c b/contrib/subversion/subversion/svnadmin/svnadmin.c index cfbcf660a..ec98c6bb1 100644 --- a/contrib/subversion/subversion/svnadmin/svnadmin.c +++ b/contrib/subversion/subversion/svnadmin/svnadmin.c @@ -39,19 +39,26 @@ #include "svn_cache_config.h" #include "svn_version.h" #include "svn_props.h" +#include "svn_sorts.h" #include "svn_time.h" #include "svn_user.h" #include "svn_xml.h" +#include "private/svn_cmdline_private.h" #include "private/svn_opt_private.h" +#include "private/svn_sorts_private.h" #include "private/svn_subr_private.h" -#include "private/svn_cmdline_private.h" #include "svn_private_config.h" /*** Code. ***/ +/* FSFS format 7's "block-read" feature performs poorly with small caches. + * Enable it only if caches above this threshold have been configured. + * The current threshold is 64MB. */ +#define BLOCK_READ_CACHE_THRESHOLD (0x40 * 0x100000) + /* A flag to see if we've been cancelled by the client or not. */ static volatile sig_atomic_t cancelled = FALSE; @@ -100,7 +107,7 @@ warning_func(void *baton, { if (! err) return; - svn_handle_error2(err, stderr, FALSE, "svnadmin: "); + svn_handle_warning2(stderr, err, "svnadmin: "); } @@ -111,6 +118,10 @@ open_repos(svn_repos_t **repos, const char *path, apr_pool_t *pool) { + /* Enable the "block-read" feature (where it applies)? */ + svn_boolean_t use_block_read + = svn_cache_config_get()->cache_size > BLOCK_READ_CACHE_THRESHOLD; + /* 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"); @@ -118,9 +129,11 @@ open_repos(svn_repos_t **repos, 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)); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, + use_block_read ? "1" : "0"); /* now, open the requested repository */ - SVN_ERR(svn_repos_open2(repos, path, fs_config, pool)); + SVN_ERR(svn_repos_open3(repos, path, fs_config, pool, pool)); svn_fs_set_warning_func(svn_repos_fs(*repos), warning_func, NULL); return SVN_NO_ERROR; } @@ -150,11 +163,13 @@ check_lib_versions(void) static svn_opt_subcommand_t subcommand_crashtest, subcommand_create, + subcommand_delrevprop, subcommand_deltify, subcommand_dump, subcommand_freeze, subcommand_help, subcommand_hotcopy, + subcommand_info, subcommand_load, subcommand_list_dblogs, subcommand_list_unused_dblogs, @@ -176,6 +191,7 @@ enum svnadmin__cmdline_options_t { svnadmin__version = SVN_OPT_FIRST_LONGOPT_ID, svnadmin__incremental, + svnadmin__keep_going, svnadmin__deltas, svnadmin__ignore_uuid, svnadmin__force_uuid, @@ -186,6 +202,7 @@ enum svnadmin__cmdline_options_t svnadmin__config_dir, svnadmin__bypass_hooks, svnadmin__bypass_prop_validation, + svnadmin__ignore_dates, svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook, svnadmin__use_pre_revprop_change_hook, @@ -195,7 +212,9 @@ enum svnadmin__cmdline_options_t svnadmin__pre_1_4_compatible, svnadmin__pre_1_5_compatible, svnadmin__pre_1_6_compatible, - svnadmin__compatible_version + svnadmin__compatible_version, + svnadmin__check_normalization, + svnadmin__metadata_only }; /* Option codes and descriptions. @@ -231,8 +250,11 @@ static const apr_getopt_option_t options_table[] = {"bypass-prop-validation", svnadmin__bypass_prop_validation, 0, N_("bypass property validation logic")}, + {"ignore-dates", svnadmin__ignore_dates, 0, + N_("ignore revision datestamps found in the stream")}, + {"quiet", 'q', 0, - N_("no progress (only errors) to stderr")}, + N_("no progress (only errors to stderr)")}, {"ignore-uuid", svnadmin__ignore_uuid, 0, N_("ignore any repos UUID found in the stream")}, @@ -241,7 +263,9 @@ static const apr_getopt_option_t options_table[] = 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'")}, + N_("type of repository:\n" + " 'fsfs' (default), 'bdb' or 'fsx'\n" + " CAUTION: FSX is for EXPERIMENTAL use only!")}, {"parent-dir", svnadmin__parent_dir, 1, N_("load at specified directory in repository")}, @@ -284,6 +308,9 @@ static const apr_getopt_option_t options_table[] = {"pre-1.6-compatible", svnadmin__pre_1_6_compatible, 0, N_("deprecated; see --compatible-version")}, + {"keep-going", svnadmin__keep_going, 0, + N_("continue verification after detecting a corruption")}, + {"memory-cache-size", 'M', 1, N_("size of the extra in-memory cache in MB used to\n" " minimize redundant operations. Default: 16.\n" @@ -295,6 +322,17 @@ static const apr_getopt_option_t options_table[] = {"file", 'F', 1, N_("read repository paths from file ARG")}, + {"check-normalization", svnadmin__check_normalization, 0, + N_("report any names within the same directory or\n" + " svn:mergeinfo property value that differ only\n" + " in character representation, but are otherwise\n" + " identical")}, + + {"metadata-only", svnadmin__metadata_only, 0, + N_("verify metadata only (ignored for BDB),\n" + " checking against external corruption in\n" + " Subversion 1.9+ format repositories.\n")}, + {NULL} }; @@ -319,6 +357,19 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = svnadmin__pre_1_6_compatible } }, + {"delrevprop", subcommand_delrevprop, {0}, N_ + ("usage: 1. svnadmin delrevprop REPOS_PATH -r REVISION NAME\n" + " 2. svnadmin delrevprop REPO_PATH -t TXN NAME\n\n" + "1. Delete the property NAME on revision REVISION.\n\n" + "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" + "trigger the revision property-related hooks (for example, if you want\n" + "an email notification sent from your post-revprop-change hook).\n\n" + "NOTE: Revision properties are not versioned, so this command will\n" + "irreversibly destroy the previous value of the property.\n\n" + "2. Delete the property NAME on transaction TXN.\n"), + {'r', 't', svnadmin__use_pre_revprop_change_hook, + svnadmin__use_post_revprop_change_hook} }, + {"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" @@ -361,7 +412,12 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = "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} }, + {svnadmin__clean_logs, svnadmin__incremental, 'q'} }, + + {"info", subcommand_info, {0}, N_ + ("usage: svnadmin info REPOS_PATH\n\n" + "Print information about the repository at REPOS_PATH.\n"), + {0} }, {"list-dblogs", subcommand_list_dblogs, {0}, N_ ("usage: svnadmin list-dblogs REPOS_PATH\n\n" @@ -384,6 +440,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = "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__ignore_dates, svnadmin__use_pre_commit_hook, svnadmin__use_post_commit_hook, svnadmin__parent_dir, svnadmin__bypass_prop_validation, 'M'} }, @@ -409,7 +466,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = ("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'} }, + {'q', 'M'} }, {"recover", subcommand_recover, {0}, N_ ("usage: svnadmin recover REPOS_PATH\n\n" @@ -442,14 +499,16 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {'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" + ("usage: 1. svnadmin setrevprop REPOS_PATH -r REVISION NAME FILE\n" + " 2. svnadmin setrevprop REPOS_PATH -t TXN NAME FILE\n\n" + "1. Set the property NAME on revision REVISION to the contents of FILE.\n\n" + "Use --use-pre-revprop-change-hook/--use-post-revprop-change-hook to\n" + "trigger the revision property-related hooks (for example, if you want\n" + "an email 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, + "overwrite the previous value of the property.\n\n" + "2. Set the property NAME on transaction TXN to the contents of FILE.\n"), + {'r', 't', svnadmin__use_pre_revprop_change_hook, svnadmin__use_post_revprop_change_hook} }, {"setuuid", subcommand_setuuid, {0}, N_ @@ -482,7 +541,8 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {"verify", subcommand_verify, {0}, N_ ("usage: svnadmin verify REPOS_PATH\n\n" "Verify the data stored in the repository.\n"), - {'t', 'r', 'q', 'M'} }, + {'t', 'r', 'q', svnadmin__keep_going, 'M', + svnadmin__check_normalization, svnadmin__metadata_only} }, { NULL, NULL, {0}, NULL, {0} } }; @@ -493,9 +553,6 @@ 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 */ @@ -513,11 +570,15 @@ struct svnadmin_opt_state svn_boolean_t clean_logs; /* --clean-logs */ svn_boolean_t bypass_hooks; /* --bypass-hooks */ svn_boolean_t wait; /* --wait */ + svn_boolean_t keep_going; /* --keep-going */ + svn_boolean_t check_normalization; /* --check-normalization */ + svn_boolean_t metadata_only; /* --metadata-only */ svn_boolean_t bypass_prop_validation; /* --bypass-prop-validation */ + svn_boolean_t ignore_dates; /* --ignore-dates */ 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; + const char *parent_dir; /* --parent-dir */ svn_stringbuf_t *filedata; /* --file */ const char *config_dir; /* Overriding Configuration Directory */ @@ -564,7 +625,7 @@ target_arg_to_dirent(const char **dirent, 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); + _("Path '%s' is not a local path"), path); *dirent = svn_dirent_internal_style(path, pool); return SVN_NO_ERROR; } @@ -593,10 +654,10 @@ parse_args(apr_array_header_t **args, if ((min_expected >= 0) && (num_args < min_expected)) return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, - "Not enough arguments"); + _("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"); + _("Too many arguments")); if (args) { *args = apr_array_make(pool, num_args, sizeof(const char *)); @@ -611,6 +672,17 @@ parse_args(apr_array_header_t **args, } +/* This implements 'svn_error_malfunction_handler_t. */ +static svn_error_t * +crashtest_malfunction_handler(svn_boolean_t can_return, + const char *file, + int line, + const char *expr) +{ + abort(); + return SVN_NO_ERROR; /* Not reached. */ +} + /* This implements `svn_opt_subcommand_t'. */ static svn_error_t * subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool) @@ -618,7 +690,14 @@ subcommand_crashtest(apr_getopt_t *os, void *baton, apr_pool_t *pool) struct svnadmin_opt_state *opt_state = baton; svn_repos_t *repos; + (void)svn_error_set_malfunction_handler(crashtest_malfunction_handler); SVN_ERR(open_repos(&repos, opt_state->repository_path, pool)); + SVN_ERR(svn_cmdline_printf(pool, + _("Successfully opened repository '%s'.\n" + "Will now crash to simulate a crashing " + "server process.\n"), + svn_dirent_local_style(opt_state->repository_path, + pool))); SVN_ERR_MALFUNCTION(); /* merely silence a compiler warning (this will never be executed) */ @@ -662,17 +741,6 @@ subcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool) 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)) @@ -683,18 +751,40 @@ subcommand_create(apr_getopt_t *os, void *baton, apr_pool_t *pool) 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"); + /* In 1.9, we figured out that we didn't have to keep extending this + madness indefinitely. */ + svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, + apr_psprintf(pool, "%d.%d.%d%s%s", + opt_state->compatible_version->major, + opt_state->compatible_version->minor, + opt_state->compatible_version->patch, + opt_state->compatible_version->tag + ? "-" : "", + opt_state->compatible_version->tag + ? opt_state->compatible_version->tag : "")); } - 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))) + if (opt_state->compatible_version) { - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Repositories compatible with 1.0.x must use " - "--fs-type=bdb")); + if (! 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")); + } + + if (! svn_version__at_least(opt_state->compatible_version, 1, 9, 0) + && opt_state->fs_type && !strcmp(opt_state->fs_type, SVN_FS_TYPE_FSX)) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Repositories compatible with 1.8.x or " + "earlier cannot use --fs-type=%s"), + SVN_FS_TYPE_FSX); + } } SVN_ERR(svn_repos_create(&repos, opt_state->repository_path, @@ -756,16 +846,87 @@ subcommand_deltify(apr_getopt_t *os, void *baton, apr_pool_t *pool) return SVN_NO_ERROR; } +/* Structure for errors encountered during 'svnadmin verify --keep-going'. */ +struct verification_error +{ + svn_revnum_t rev; + svn_error_t *err; +}; + +/* Pool cleanup function to clear an svn_error_t *. */ +static apr_status_t +err_cleanup(void *data) +{ + svn_error_t *err = data; + + svn_error_clear(err); + + return APR_SUCCESS; +} + +struct repos_verify_callback_baton +{ + /* Should we continue after receiving a first verification error? */ + svn_boolean_t keep_going; + + /* List of errors encountered during 'svnadmin verify --keep-going'. */ + apr_array_header_t *error_summary; + + /* Pool for data collected during callback invocations. */ + apr_pool_t *result_pool; +}; + +/* Implementation of svn_repos_verify_callback_t to handle errors coming + from svn_repos_verify_fs3(). */ +static svn_error_t * +repos_verify_callback(void *baton, + svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool) +{ + struct repos_verify_callback_baton *b = baton; + + if (revision == SVN_INVALID_REVNUM) + { + SVN_ERR(svn_cmdline_fputs(_("* Error verifying repository metadata.\n"), + stderr, scratch_pool)); + } + else + { + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, + _("* Error verifying revision %ld.\n"), + revision)); + } + + if (b->keep_going) + { + struct verification_error *verr; + + svn_handle_error2(verify_err, stderr, FALSE, "svnadmin: "); + + /* Remember the error in B->ERROR_SUMMARY. */ + verr = apr_palloc(b->result_pool, sizeof(*verr)); + verr->rev = revision; + verr->err = svn_error_dup(verify_err); + apr_pool_cleanup_register(b->result_pool, verr->err, err_cleanup, + apr_pool_cleanup_null); + APR_ARRAY_PUSH(b->error_summary, struct verification_error *) = verr; + + return SVN_NO_ERROR; + } + else + return svn_error_trace(svn_error_dup(verify_err)); +} /* 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() */ + response stream for svn_repos_dump_fs2(), svn_repos_verify_fs(), + svn_repos_hotcopy3() and others. */ 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) { @@ -789,7 +950,7 @@ repos_notify_handler(void *baton, case svn_repos_notify_verify_rev_structure: if (notify->revision == SVN_INVALID_REVNUM) - svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + svn_error_clear(svn_stream_puts(feedback_stream, _("* Verifying repository metadata ...\n"))); else svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, @@ -877,13 +1038,11 @@ repos_notify_handler(void *baton, return; case svn_repos_notify_load_node_done: - svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, - "%s", _(" done.\n"))); + svn_error_clear(svn_stream_puts(feedback_stream, _(" done.\n"))); return; case svn_repos_notify_load_copied_node: - len = 9; - svn_error_clear(svn_stream_write(feedback_stream, "COPIED...", &len)); + svn_error_clear(svn_stream_puts(feedback_stream, "COPIED...")); return; case svn_repos_notify_load_txn_start: @@ -911,7 +1070,7 @@ repos_notify_handler(void *baton, return; case svn_repos_notify_recover_start: - svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + svn_error_clear(svn_stream_puts(feedback_stream, _("Repository lock acquired.\n" "Please wait; recovering the" " repository may take some time...\n"))); @@ -924,6 +1083,49 @@ repos_notify_handler(void *baton, " repository may take some time...\n"))); return; + case svn_repos_notify_pack_revprops: + { + const char *shardstr = apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + notify->shard); + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("Packed revision properties in shard %s\n"), + shardstr)); + return; + } + + case svn_repos_notify_cleanup_revprops: + { + const char *shardstr = apr_psprintf(scratch_pool, + "%" APR_INT64_T_FMT, + notify->shard); + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("Removed non-packed revision properties" + " in shard %s\n"), + shardstr)); + return; + } + + case svn_repos_notify_format_bumped: + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("Bumped repository format to %ld\n"), + notify->revision)); + return; + + case svn_repos_notify_hotcopy_rev_range: + if (notify->start_revision == notify->end_revision) + { + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("* Copied revision %ld.\n"), + notify->start_revision)); + } + else + { + svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, + _("* Copied revisions from %ld to %ld.\n"), + notify->start_revision, notify->end_revision)); + } + default: return; } @@ -980,7 +1182,7 @@ subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) 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; + svn_stream_t *feedback_stream = NULL; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); @@ -1014,12 +1216,12 @@ subcommand_dump(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Progress feedback goes to STDERR, unless they asked to suppress it. */ if (! opt_state->quiet) - progress_stream = recode_stream_create(stderr, pool); + feedback_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)); + feedback_stream, check_cancel, NULL, pool)); return SVN_NO_ERROR; } @@ -1079,8 +1281,10 @@ subcommand_freeze(apr_getopt_t *os, void *baton, apr_pool_t *pool) } else { + const char *utf8; /* All repositories in filedata. */ - paths = svn_cstring_split(opt_state->filedata->data, "\n", FALSE, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8, opt_state->filedata->data, pool)); + paths = svn_cstring_split(utf8, "\r\n", FALSE, pool); } b.command = APR_ARRAY_IDX(args, 0, const char *); @@ -1106,6 +1310,7 @@ 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" + "Subversion repository administration tool.\n" "Type 'svnadmin help ' for help on a specific subcommand.\n" "Type 'svnadmin --version' to see the program version and FS modules.\n" "\n" @@ -1165,7 +1370,8 @@ subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) 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; + svn_stream_t *stdin_stream; + svn_stream_t *feedback_stream = NULL; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); @@ -1199,15 +1405,16 @@ subcommand_load(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ if (! opt_state->quiet) - stdout_stream = recode_stream_create(stdout, pool); + feedback_stream = recode_stream_create(stdout, pool); - err = svn_repos_load_fs4(repos, stdin_stream, lower, upper, + err = svn_repos_load_fs5(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->ignore_dates, opt_state->quiet ? NULL : repos_notify_handler, - stdout_stream, check_cancel, NULL, pool); + feedback_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 " @@ -1254,12 +1461,12 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) svn_repos_t *repos; svn_error_t *err; struct svnadmin_opt_state *opt_state = baton; - svn_stream_t *stdout_stream; + svn_stream_t *feedback_stream = NULL; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); + SVN_ERR(svn_stream_for_stdout(&feedback_stream, pool)); /* Restore default signal handlers until after we have acquired the * exclusive lock so that the user interrupt before we actually @@ -1267,7 +1474,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) setup_cancellation_signals(SIG_DFL); err = svn_repos_recover4(opt_state->repository_path, TRUE, - repos_notify_handler, stdout_stream, + repos_notify_handler, feedback_stream, check_cancel, NULL, pool); if (err) { @@ -1285,7 +1492,7 @@ subcommand_recover(apr_getopt_t *os, void *baton, apr_pool_t *pool) " 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, + repos_notify_handler, feedback_stream, check_cancel, NULL, pool)); } @@ -1423,30 +1630,47 @@ subcommand_rmtxns(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* 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. */ + OPT_STATE->txn_id, OPT_STATE->use_pre_revprop_change_hook and + OPT_STATE->use_post_revprop_change_hook to be set appropriately. + If FILENAME is NULL, delete property PROP_NAME. */ 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_string_t *prop_value; + + if (filename) + { + svn_stringbuf_t *file_contents; - SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool)); + SVN_ERR(svn_stringbuf_from_file2(&file_contents, filename, pool)); - prop_value->data = file_contents->data; - prop_value->len = file_contents->len; + prop_value = svn_string_create_empty(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)); + SVN_ERR(svn_subst_translate_string2(&prop_value, NULL, NULL, prop_value, + NULL, FALSE, pool, pool)); + } + else + { + prop_value = NULL; + } /* 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( + if (opt_state->txn_id) + { + svn_fs_t *fs = svn_repos_fs(repos); + svn_fs_txn_t *txn; + + SVN_ERR(svn_fs_open_txn(&txn, fs, opt_state->txn_id, pool)); + SVN_ERR(svn_fs_change_txn_prop(txn, prop_name, prop_value, pool)); + } + else + 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, @@ -1471,7 +1695,21 @@ subcommand_setrevprop(apr_getopt_t *os, void *baton, apr_pool_t *pool) 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) + if (opt_state->txn_id) + { + if (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")); + + if (opt_state->use_pre_revprop_change_hook + || opt_state->use_post_revprop_change_hook) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Calling hooks is incompatible with " + "--transaction (-t)")); + } + else 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) @@ -1540,7 +1778,7 @@ 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; + svn_stream_t *feedback_stream = NULL; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); @@ -1549,11 +1787,11 @@ subcommand_pack(apr_getopt_t *os, void *baton, apr_pool_t *pool) /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ if (! opt_state->quiet) - progress_stream = recode_stream_create(stdout, pool); + feedback_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)); + feedback_stream, check_cancel, NULL, pool)); } @@ -1565,7 +1803,8 @@ subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool) svn_repos_t *repos; svn_fs_t *fs; svn_revnum_t youngest, lower, upper; - svn_stream_t *progress_stream = NULL; + svn_stream_t *feedback_stream = NULL; + struct repos_verify_callback_baton verify_baton = { 0 }; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); @@ -1609,13 +1848,93 @@ subcommand_verify(apr_getopt_t *os, void *baton, apr_pool_t *pool) upper = lower; } - if (! opt_state->quiet) - progress_stream = recode_stream_create(stderr, pool); + if (!opt_state->quiet) + feedback_stream = recode_stream_create(stdout, pool); + + verify_baton.keep_going = opt_state->keep_going; + verify_baton.error_summary = + apr_array_make(pool, 0, sizeof(struct verification_error *)); + verify_baton.result_pool = pool; + + SVN_ERR(svn_repos_verify_fs3(repos, lower, upper, + opt_state->check_normalization, + opt_state->metadata_only, + !opt_state->quiet + ? repos_notify_handler : NULL, + feedback_stream, + repos_verify_callback, &verify_baton, + check_cancel, NULL, pool)); + + /* Show the --keep-going error summary. */ + if (!opt_state->quiet + && opt_state->keep_going + && verify_baton.error_summary->nelts > 0) + { + int rev_maxlength; + svn_revnum_t end_revnum; + apr_pool_t *iterpool; + int i; + + svn_error_clear( + svn_stream_puts(feedback_stream, + _("\n-----Summary of corrupt revisions-----\n"))); + + /* The standard column width for the revision number is 6 characters. + If the revision number can potentially be larger (i.e. if end_revnum + is larger than 1000000), we increase the column width as needed. */ + rev_maxlength = 6; + end_revnum = APR_ARRAY_IDX(verify_baton.error_summary, + verify_baton.error_summary->nelts - 1, + struct verification_error *)->rev; + while (end_revnum >= 1000000) + { + rev_maxlength++; + end_revnum = end_revnum / 10; + } + + iterpool = svn_pool_create(pool); + for (i = 0; i < verify_baton.error_summary->nelts; i++) + { + struct verification_error *verr; + svn_error_t *err; + const char *rev_str; + + svn_pool_clear(iterpool); + + verr = APR_ARRAY_IDX(verify_baton.error_summary, i, + struct verification_error *); + + if (verr->rev != SVN_INVALID_REVNUM) + { + rev_str = apr_psprintf(iterpool, "r%ld", verr->rev); + rev_str = apr_psprintf(iterpool, "%*s", rev_maxlength, rev_str); + for (err = svn_error_purge_tracing(verr->err); + err != SVN_NO_ERROR; err = err->child) + { + char buf[512]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); + svn_error_clear(svn_stream_printf(feedback_stream, iterpool, + "%s: E%06d: %s\n", + rev_str, err->apr_err, + message)); + } + } + } + + svn_pool_destroy(iterpool); + } + + if (verify_baton.error_summary->nelts > 0) + { + return svn_error_createf(SVN_ERR_CL_REPOS_VERIFY_FAILED, NULL, + _("Failed to verify repository '%s'"), + svn_dirent_local_style( + opt_state->repository_path, pool)); + } - return svn_repos_verify_fs2(repos, lower, upper, - !opt_state->quiet - ? repos_notify_handler : NULL, - progress_stream, check_cancel, NULL, pool); + return SVN_NO_ERROR; } /* This implements `svn_opt_subcommand_t'. */ @@ -1623,6 +1942,7 @@ svn_error_t * subcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *pool) { struct svnadmin_opt_state *opt_state = baton; + svn_stream_t *feedback_stream = NULL; apr_array_header_t *targets; const char *new_repos_path; @@ -1631,9 +1951,136 @@ subcommand_hotcopy(apr_getopt_t *os, void *baton, apr_pool_t *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, + /* Progress feedback goes to STDOUT, unless they asked to suppress it. */ + if (! opt_state->quiet) + feedback_stream = recode_stream_create(stdout, pool); + + return svn_repos_hotcopy3(opt_state->repository_path, new_repos_path, opt_state->clean_logs, opt_state->incremental, - check_cancel, NULL, pool); + !opt_state->quiet ? repos_notify_handler : NULL, + feedback_stream, check_cancel, NULL, pool); +} + +svn_error_t * +subcommand_info(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; + int fs_format; + const char *uuid; + + /* 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_cmdline_printf(pool, _("Path: %s\n"), + svn_dirent_local_style(svn_repos_path(repos, pool), + pool))); + + SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); + SVN_ERR(svn_cmdline_printf(pool, _("UUID: %s\n"), uuid)); + { + int repos_format, minor; + svn_version_t *repos_version, *fs_version; + SVN_ERR(svn_repos_info_format(&repos_format, &repos_version, + repos, pool, pool)); + SVN_ERR(svn_cmdline_printf(pool, _("Repository Format: %d\n"), + repos_format)); + + SVN_ERR(svn_fs_info_format(&fs_format, &fs_version, + fs, pool, pool)); + /* fs_format will be printed later. */ + + SVN_ERR_ASSERT(repos_version->major == SVN_VER_MAJOR); + SVN_ERR_ASSERT(fs_version->major == SVN_VER_MAJOR); + SVN_ERR_ASSERT(repos_version->patch == 0); + SVN_ERR_ASSERT(fs_version->patch == 0); + + minor = (repos_version->minor > fs_version->minor) + ? repos_version->minor : fs_version->minor; + SVN_ERR(svn_cmdline_printf(pool, _("Compatible With Version: %d.%d.0\n"), + SVN_VER_MAJOR, minor)); + } + + { + apr_hash_t *capabilities_set; + apr_array_header_t *capabilities; + int i; + + SVN_ERR(svn_repos_capabilities(&capabilities_set, repos, pool, pool)); + capabilities = svn_sort__hash(capabilities_set, + svn_sort_compare_items_lexically, + pool); + + for (i = 0; i < capabilities->nelts; i++) + { + svn_sort__item_t *item = &APR_ARRAY_IDX(capabilities, i, + svn_sort__item_t); + const char *capability = item->key; + SVN_ERR(svn_cmdline_printf(pool, _("Repository Capability: %s\n"), + capability)); + } + } + + { + const svn_fs_info_placeholder_t *info; + + SVN_ERR(svn_fs_info(&info, fs, pool, pool)); + SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Type: %s\n"), + info->fs_type)); + SVN_ERR(svn_cmdline_printf(pool, _("Filesystem Format: %d\n"), + fs_format)); + if (!strcmp(info->fs_type, SVN_FS_TYPE_FSFS)) + { + const svn_fs_fsfs_info_t *fsfs_info = (const void *)info; + svn_revnum_t youngest; + SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); + + if (fsfs_info->shard_size) + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: yes\n"))); + else + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Sharded: no\n"))); + + if (fsfs_info->shard_size) + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shard Size: %d\n"), + fsfs_info->shard_size)); + + /* Print packing statistics, if enabled on the FS. */ + if (fsfs_info->shard_size) + { + const int shard_size = fsfs_info->shard_size; + const long shards_packed = fsfs_info->min_unpacked_rev / shard_size; + const long shards_full = (youngest + 1) / shard_size; + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Shards Packed: %ld/%ld\n"), + shards_packed, shards_full)); + } + + if (fsfs_info->log_addressing) + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: yes\n"))); + else + SVN_ERR(svn_cmdline_printf(pool, _("FSFS Logical Addressing: no\n"))); + } + } + + { + apr_array_header_t *files; + int i; + + SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool)); + for (i = 0; i < files->nelts; i++) + SVN_ERR(svn_cmdline_printf(pool, _("Configuration File: %s\n"), + svn_dirent_local_style( + APR_ARRAY_IDX(files, i, const char *), + pool))); + } + + /* 'svn info' prints an extra newline here, to support multiple targets. + We'll do the same. */ + SVN_ERR(svn_cmdline_printf(pool, "\n")); + + return SVN_NO_ERROR; } /* This implements `svn_opt_subcommand_t'. */ @@ -1709,6 +2156,7 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) const char *fs_path = "/"; apr_hash_t *locks; apr_hash_index_t *hi; + apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(svn_opt__args_to_target_array(&targets, os, apr_array_make(pool, 0, @@ -1729,24 +2177,28 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *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); + const char *path = apr_hash_this_key(hi); + svn_lock_t *lock = apr_hash_this_val(hi); int comment_lines = 0; - cr_date = svn_time_to_human_cstring(lock->creation_date, pool); + svn_pool_clear(iterpool); + + SVN_ERR(check_cancel(NULL)); + + cr_date = svn_time_to_human_cstring(lock->creation_date, iterpool); if (lock->expiration_date) - exp_date = svn_time_to_human_cstring(lock->expiration_date, pool); + exp_date = svn_time_to_human_cstring(lock->expiration_date, iterpool); 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, + SVN_ERR(svn_cmdline_printf(iterpool, _("Path: %s\n"), path)); + SVN_ERR(svn_cmdline_printf(iterpool, _("UUID Token: %s\n"), lock->token)); + SVN_ERR(svn_cmdline_printf(iterpool, _("Owner: %s\n"), lock->owner)); + SVN_ERR(svn_cmdline_printf(iterpool, _("Created: %s\n"), cr_date)); + SVN_ERR(svn_cmdline_printf(iterpool, _("Expires: %s\n"), exp_date)); + SVN_ERR(svn_cmdline_printf(iterpool, Q_("Comment (%i line):\n%s\n\n", "Comment (%i lines):\n%s\n\n", comment_lines), @@ -1754,6 +2206,8 @@ subcommand_lslocks(apr_getopt_t *os, void *baton, apr_pool_t *pool) lock->comment ? lock->comment : "")); } + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; } @@ -1888,18 +2342,18 @@ 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; + svn_stream_t *feedback_stream = NULL; /* Expect no more arguments. */ SVN_ERR(parse_args(NULL, os, 0, 0, pool)); - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); + SVN_ERR(svn_stream_for_stdout(&feedback_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); + repos_notify_handler, feedback_stream, pool); if (err) { if (APR_STATUS_IS_EAGAIN(err->apr_err)) @@ -1917,7 +2371,7 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool) " 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, + repos_notify_handler, feedback_stream, pool)); } else if (err->apr_err == SVN_ERR_FS_UNSUPPORTED_UPGRADE) @@ -1941,26 +2395,53 @@ subcommand_upgrade(apr_getopt_t *os, void *baton, apr_pool_t *pool) } +/* This implements `svn_opt_subcommand_t'. */ +static svn_error_t * +subcommand_delrevprop(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; + + /* Expect one more argument: NAME */ + SVN_ERR(parse_args(&args, os, 1, 1, pool)); + prop_name = APR_ARRAY_IDX(args, 0, const char *); + + if (opt_state->txn_id) + { + if (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")); + + if (opt_state->use_pre_revprop_change_hook + || opt_state->use_post_revprop_change_hook) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Calling hooks is incompatible with " + "--transaction (-t)")); + } + else 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, NULL, opt_state, pool); +} + + /** 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) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err; apr_status_t apr_err; @@ -1976,15 +2457,16 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); /* Check library versions */ - SVN_INT_ERR(check_lib_versions()); + SVN_ERR(check_lib_versions()); /* Initialize the FS library. */ - SVN_INT_ERR(svn_fs_initialize(pool)); + SVN_ERR(svn_fs_initialize(pool)); if (argc <= 1) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Initialize opt_state. */ @@ -1993,7 +2475,7 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.memory_cache_size = svn_cache_config_get()->cache_size; /* Parse options. */ - SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; @@ -2008,8 +2490,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) break; else if (apr_err) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -2020,23 +2503,19 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { 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); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments encountered; " + "try '-r N:M' instead of '-r N -r M'")); } 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); + SVN_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, + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error in revision argument '%s'"), utf8_opt_arg); - return EXIT_ERROR(err); } } break; @@ -2056,8 +2535,8 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) = 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), + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_stringbuf_from_file2(&(opt_state.filedata), utf8_opt_arg, pool)); dash_F_arg = TRUE; case svnadmin__version: @@ -2076,13 +2555,19 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) opt_state.uuid_action = svn_repos_load_uuid_force; break; case svnadmin__pre_1_4_compatible: - opt_state.pre_1_4_compatible = TRUE; + opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); + opt_state.compatible_version->major = 1; + opt_state.compatible_version->minor = 3; break; case svnadmin__pre_1_5_compatible: - opt_state.pre_1_5_compatible = TRUE; + opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); + opt_state.compatible_version->major = 1; + opt_state.compatible_version->minor = 4; break; case svnadmin__pre_1_6_compatible: - opt_state.pre_1_6_compatible = TRUE; + opt_state.compatible_version = apr_pcalloc(pool, sizeof(svn_version_t)); + opt_state.compatible_version->major = 1; + opt_state.compatible_version->minor = 5; break; case svnadmin__compatible_version: { @@ -2092,16 +2577,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) /* Parse the version string which carries our target compatibility. */ - SVN_INT_ERR(svn_version__parse_version_string(&compatible_version, + SVN_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); + return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Cannot create pre-1.0-compatible " + "repositories")); } /* We can't create repository with a version newer than what @@ -2111,22 +2595,30 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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); + return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Cannot guarantee compatibility " + "beyond the current running version " + "(%s)"), + SVN_VER_NUM); } opt_state.compatible_version = compatible_version; } break; + case svnadmin__keep_going: + opt_state.keep_going = TRUE; + break; + case svnadmin__check_normalization: + opt_state.check_normalization = TRUE; + break; + case svnadmin__metadata_only: + opt_state.metadata_only = TRUE; + break; case svnadmin__fs_type: - SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.fs_type, opt_arg, pool)); + SVN_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, + SVN_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); @@ -2155,11 +2647,14 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) case svnadmin__bypass_prop_validation: opt_state.bypass_prop_validation = TRUE; break; + case svnadmin__ignore_dates: + opt_state.ignore_dates = 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)); + SVN_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; @@ -2168,8 +2663,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) break; default: { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } /* close `switch' */ } /* close `while' */ @@ -2202,8 +2698,9 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) { svn_error_clear(svn_cmdline_fprintf(stderr, pool, _("subcommand argument required\n"))); - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -2213,14 +2710,15 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) if (subcommand == NULL) { const char *first_arg_utf8; - SVN_INT_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, + SVN_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; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -2235,23 +2733,17 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) if (os->ind >= os->argc) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Repository argument required")); - return EXIT_ERROR(err); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Repository argument required")); } - if ((err = svn_utf_cstring_to_utf8(&repos_path, - os->argv[os->ind++], pool))) - { - return EXIT_ERROR(err); - } + SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool)); 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); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is a URL when it should be a " + "local path"), repos_path); } opt_state.repository_path = svn_dirent_internal_style(repos_path, pool); @@ -2277,13 +2769,14 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) pool); svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); + SVN_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; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } @@ -2325,26 +2818,18 @@ sub_main(int argc, const char *argv[], apr_pool_t *pool) 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; + return err; } + + return SVN_NO_ERROR; } int main(int argc, const char *argv[]) { apr_pool_t *pool; - int exit_code; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; /* Initialize the app. */ if (svn_cmdline_init("svnadmin", stderr) != EXIT_SUCCESS) @@ -2355,7 +2840,17 @@ main(int argc, const char *argv[]) */ pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); - exit_code = sub_main(argc, argv, pool); + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnadmin: "); + } svn_pool_destroy(pool); return exit_code; diff --git a/contrib/subversion/subversion/svnbench/cl.h b/contrib/subversion/subversion/svnbench/cl.h new file mode 100644 index 000000000..5b15fe5bb --- /dev/null +++ b/contrib/subversion/subversion/svnbench/cl.h @@ -0,0 +1,203 @@ +/* + * cl.h: shared stuff in the command line program + * + * ==================================================================== + * 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_CL_H +#define SVN_CL_H + +/*** Includes. ***/ + +#include + +#include "svn_client.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*** Command dispatch. ***/ + +/* Hold results of option processing that are shared by multiple + commands. */ +typedef struct svn_cl__opt_state_t +{ + /* An array of svn_opt_revision_range_t *'s representing revisions + ranges indicated on the command-line via the -r and -c options. + For each range in the list, if only one revision was provided + (-rN), its 'end' member remains 'svn_opt_revision_unspecified'. + This array always has at least one element, even if that is a + null range in which both ends are 'svn_opt_revision_unspecified'. */ + apr_array_header_t *revision_ranges; + + /* These are simply a copy of the range start and end values present + in the first item of the revision_ranges list. */ + svn_opt_revision_t start_revision; + svn_opt_revision_t end_revision; + + /* Flag which is only set if the '-c' option was used. */ + svn_boolean_t used_change_arg; + + /* Flag which is only set if the '-r' option was used. */ + svn_boolean_t used_revision_arg; + + /* Max number of log messages to get back from svn_client_log2. */ + int limit; + + /* After option processing is done, reflects the switch actually + given on the command line, or svn_depth_unknown if none. */ + svn_depth_t depth; + + svn_boolean_t quiet; /* sssh...avoid unnecessary output */ + svn_boolean_t non_interactive; /* do no interactive prompting */ + svn_boolean_t version; /* print version information */ + svn_boolean_t verbose; /* be verbose */ + svn_boolean_t strict; /* do strictly what was requested */ + const char *encoding; /* the locale/encoding of the data*/ + svn_boolean_t help; /* print usage message */ + const char *auth_username; /* auth username */ /* UTF-8! */ + const char *auth_password; /* auth password */ /* UTF-8! */ + apr_array_header_t *targets; /* target list from file */ /* UTF-8! */ + svn_boolean_t no_auth_cache; /* do not cache authentication information */ + svn_boolean_t stop_on_copy; /* don't cross copies during processing */ + const char *config_dir; /* over-riding configuration directory */ + apr_array_header_t *config_options; /* over-riding configuration options */ + svn_boolean_t all_revprops; /* retrieve all revprops */ + svn_boolean_t no_revprops; /* retrieve no revprops */ + apr_hash_t *revprop_table; /* table of revision properties to get/set */ + svn_boolean_t use_merge_history; /* use/display extra merge information */ + /* trust server SSL certs that would otherwise be rejected as "untrusted" */ + svn_boolean_t trust_server_cert_unknown_ca; + svn_boolean_t trust_server_cert_cn_mismatch; + svn_boolean_t trust_server_cert_expired; + svn_boolean_t trust_server_cert_not_yet_valid; + svn_boolean_t trust_server_cert_other_failure; +} svn_cl__opt_state_t; + + +typedef struct svn_cl__cmd_baton_t +{ + svn_cl__opt_state_t *opt_state; + svn_client_ctx_t *ctx; +} svn_cl__cmd_baton_t; + + +/* Declare all the command procedures */ +svn_opt_subcommand_t + svn_cl__help, + svn_cl__null_blame, + svn_cl__null_export, + svn_cl__null_list, + svn_cl__null_log, + svn_cl__null_info; + + +/* See definition in main.c for documentation. */ +extern const svn_opt_subcommand_desc2_t svn_cl__cmd_table[]; + +/* See definition in main.c for documentation. */ +extern const int svn_cl__global_options[]; + +/* See definition in main.c for documentation. */ +extern const apr_getopt_option_t svn_cl__options[]; + + +/* A helper for the many subcommands that wish to merely warn when + * invoked on an unversioned, nonexistent, or otherwise innocuously + * errorful resource. Meant to be wrapped with SVN_ERR(). + * + * If ERR is null, return SVN_NO_ERROR. + * + * Else if ERR->apr_err is one of the error codes supplied in varargs, + * then handle ERR as a warning (unless QUIET is true), clear ERR, and + * return SVN_NO_ERROR, and push the value of ERR->apr_err into the + * ERRORS_SEEN array, if ERRORS_SEEN is not NULL. + * + * Else return ERR. + * + * Typically, error codes like SVN_ERR_UNVERSIONED_RESOURCE, + * SVN_ERR_ENTRY_NOT_FOUND, etc, are supplied in varargs. Don't + * forget to terminate the argument list with 0 (or APR_SUCCESS). + */ +svn_error_t * +svn_cl__try(svn_error_t *err, + apr_array_header_t *errors_seen, + svn_boolean_t quiet, + ...); + + +/* Our cancellation callback. */ +svn_error_t * +svn_cl__check_cancel(void *baton); + + + +/*** Notification functions to display results on the terminal. */ + +/* Set *NOTIFY_FUNC_P and *NOTIFY_BATON_P to a notifier/baton for all + * operations, allocated in POOL. + */ +svn_error_t * +svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p, + void **notify_baton_p, + apr_pool_t *pool); + +/* Make the notifier for use with BATON print the appropriate summary + * line at the end of the output. + */ +svn_error_t * +svn_cl__notifier_mark_export(void *baton); + +/* Like svn_client_args_to_target_array() but, if the only error is that some + * arguments are reserved file names, then print warning messages for those + * targets, store the rest of the targets in TARGETS_P and return success. */ +svn_error_t * +svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets_p, + apr_getopt_t *os, + const apr_array_header_t *known_targets, + svn_client_ctx_t *ctx, + svn_boolean_t keep_dest_origpath_on_truepath_collision, + apr_pool_t *pool); + +/* Return an error if TARGET is a URL; otherwise return SVN_NO_ERROR. */ +svn_error_t * +svn_cl__check_target_is_local_path(const char *target); + +/* Return a copy of PATH, converted to the local path style, skipping + * PARENT_PATH if it is non-null and is a parent of or equal to PATH. + * + * This function assumes PARENT_PATH and PATH are both absolute "dirents" + * or both relative "dirents". */ +const char * +svn_cl__local_style_skip_ancestor(const char *parent_path, + const char *path, + apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_CL_H */ diff --git a/contrib/subversion/subversion/svnbench/help-cmd.c b/contrib/subversion/subversion/svnbench/help-cmd.c new file mode 100644 index 000000000..ead4861ad --- /dev/null +++ b/contrib/subversion/subversion/svnbench/help-cmd.c @@ -0,0 +1,90 @@ +/* + * help-cmd.c -- Provide help + * + * ==================================================================== + * 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_string.h" +#include "svn_error.h" +#include "svn_version.h" +#include "cl.h" + +#include "svn_private_config.h" + + +/*** Code. ***/ + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__help(apr_getopt_t *os, + void *baton, + apr_pool_t *pool) +{ + svn_cl__opt_state_t *opt_state; + + char help_header[] = + N_("usage: svnbench [options] [args]\n" + "Subversion benchmarking tool.\n" + "Type 'svnbench help ' for help on a specific subcommand.\n" + "Type 'svnbench --version' to see the program version and RA modules\n" + " or 'svnbench --version --quiet' to see just the version number.\n" + "\n" + "Most subcommands take file and/or directory arguments, recursing\n" + "on the directories. If no arguments are supplied to such a\n" + "command, it recurses on the current directory (inclusive) by default.\n" + "\n" + "Available subcommands:\n"); + + char help_footer[] = + N_("Subversion is a tool for version control.\n" + "For additional information, see http://subversion.apache.org/\n"); + + const char *ra_desc_start + = _("The following repository access (RA) modules are available:\n\n"); + + svn_stringbuf_t *version_footer; + + if (baton) + opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; + else + opt_state = NULL; + + version_footer = svn_stringbuf_create(ra_desc_start, pool); + SVN_ERR(svn_ra_print_modules(version_footer, pool)); + + return svn_opt_print_help4(os, + "svnbench", /* ### erm, derive somehow? */ + opt_state ? opt_state->version : FALSE, + opt_state ? opt_state->quiet : FALSE, + opt_state ? opt_state->verbose : FALSE, + version_footer->data, + help_header, /* already gettext()'d */ + svn_cl__cmd_table, + svn_cl__options, + svn_cl__global_options, + _(help_footer), + pool); +} diff --git a/contrib/subversion/subversion/svnbench/notify.c b/contrib/subversion/subversion/svnbench/notify.c new file mode 100644 index 000000000..b8ca2efbc --- /dev/null +++ b/contrib/subversion/subversion/svnbench/notify.c @@ -0,0 +1,1045 @@ +/* + * notify.c: feedback handlers for cmdline 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. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#define APR_WANT_STDIO +#define APR_WANT_STRFUNC +#include + +#include "svn_cmdline.h" +#include "svn_pools.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_sorts.h" +#include "cl.h" + +#include "svn_private_config.h" + + +/* Baton for notify and friends. */ +struct notify_baton +{ + svn_boolean_t received_some_change; + svn_boolean_t is_checkout; + svn_boolean_t is_export; + svn_boolean_t is_wc_to_repos_copy; + svn_boolean_t sent_first_txdelta; + svn_boolean_t in_external; + svn_boolean_t had_print_error; /* Used to not keep printing error messages + when we've already had one print error. */ + + /* Conflict stats for update and merge. */ + unsigned int text_conflicts; + unsigned int prop_conflicts; + unsigned int tree_conflicts; + unsigned int skipped_paths; + apr_hash_t *conflicted_paths; + + /* The cwd, for use in decomposing absolute paths. */ + const char *path_prefix; +}; + + +/* Add a conflicted path to the list of conflicted paths stored + * in the notify baton. */ +static void +add_conflicted_path(struct notify_baton *nb, const char *path) +{ + apr_hash_set(nb->conflicted_paths, + apr_pstrdup(apr_hash_pool_get(nb->conflicted_paths), path), + APR_HASH_KEY_STRING, ""); +} + +/* This implements `svn_wc_notify_func2_t'. + * NOTE: This function can't fail, so we just ignore any print errors. */ +static void +notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) +{ + struct notify_baton *nb = baton; + char statchar_buf[5] = " "; + const char *path_local; + svn_error_t *err; + + if (n->url) + path_local = n->url; + else + { + if (n->path_prefix) + path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path, + pool); + else /* skip nb->path_prefix, if it's non-null */ + path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path, + pool); + } + + switch (n->action) + { + case svn_wc_notify_skip: + nb->skipped_paths++; + if (n->content_state == svn_wc_notify_state_missing) + { + if ((err = svn_cmdline_printf + (pool, _("Skipped missing target: '%s'\n"), + path_local))) + goto print_error; + } + else if (n->content_state == svn_wc_notify_state_source_missing) + { + if ((err = svn_cmdline_printf + (pool, _("Skipped target: '%s' -- copy-source is missing\n"), + path_local))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf + (pool, _("Skipped '%s'\n"), path_local))) + goto print_error; + } + break; + case svn_wc_notify_update_skip_obstruction: + nb->skipped_paths++; + if ((err = svn_cmdline_printf( + pool, _("Skipped '%s' -- An obstructing working copy was found\n"), + path_local))) + goto print_error; + break; + case svn_wc_notify_update_skip_working_only: + nb->skipped_paths++; + if ((err = svn_cmdline_printf( + pool, _("Skipped '%s' -- Has no versioned parent\n"), + path_local))) + goto print_error; + break; + case svn_wc_notify_update_skip_access_denied: + nb->skipped_paths++; + if ((err = svn_cmdline_printf( + pool, _("Skipped '%s' -- Access denied\n"), + path_local))) + goto print_error; + break; + case svn_wc_notify_skip_conflicted: + nb->skipped_paths++; + if ((err = svn_cmdline_printf( + pool, _("Skipped '%s' -- Node remains in conflict\n"), + path_local))) + goto print_error; + break; + case svn_wc_notify_update_delete: + case svn_wc_notify_exclude: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, "D %s\n", path_local))) + goto print_error; + break; + case svn_wc_notify_update_broken_lock: + if ((err = svn_cmdline_printf(pool, "B %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_update_external_removed: + nb->received_some_change = TRUE; + if (n->err && n->err->message) + { + if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n", + path_local, n->err->message))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n", + path_local))) + goto print_error; + } + break; + + case svn_wc_notify_left_local_modifications: + if ((err = svn_cmdline_printf(pool, "Left local modifications as '%s'\n", + path_local))) + goto print_error; + break; + + case svn_wc_notify_update_replace: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, "R %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_update_add: + nb->received_some_change = TRUE; + if (n->content_state == svn_wc_notify_state_conflicted) + { + nb->text_conflicts++; + add_conflicted_path(nb, n->path); + if ((err = svn_cmdline_printf(pool, "C %s\n", path_local))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf(pool, "A %s\n", path_local))) + goto print_error; + } + break; + + case svn_wc_notify_exists: + nb->received_some_change = TRUE; + if (n->content_state == svn_wc_notify_state_conflicted) + { + nb->text_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[0] = 'C'; + } + else + statchar_buf[0] = 'E'; + + if (n->prop_state == svn_wc_notify_state_conflicted) + { + nb->prop_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[1] = 'C'; + } + else if (n->prop_state == svn_wc_notify_state_merged) + statchar_buf[1] = 'G'; + + if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local))) + goto print_error; + break; + + case svn_wc_notify_restore: + if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_revert: + if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_failed_revert: + if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- " + "try updating instead.\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_resolved: + if ((err = svn_cmdline_printf(pool, + _("Resolved conflicted state of '%s'\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_add: + /* We *should* only get the MIME_TYPE if PATH is a file. If we + do get it, and the mime-type is not textual, note that this + is a binary addition. */ + if (n->mime_type && (svn_mime_type_is_binary(n->mime_type))) + { + if ((err = svn_cmdline_printf(pool, "A (bin) %s\n", + path_local))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf(pool, "A %s\n", + path_local))) + goto print_error; + } + break; + + case svn_wc_notify_delete: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, "D %s\n", + path_local))) + goto print_error; + break; + + case svn_wc_notify_patch: + { + nb->received_some_change = TRUE; + if (n->content_state == svn_wc_notify_state_conflicted) + { + nb->text_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[0] = 'C'; + } + else if (n->kind == svn_node_file) + { + if (n->content_state == svn_wc_notify_state_merged) + statchar_buf[0] = 'G'; + else if (n->content_state == svn_wc_notify_state_changed) + statchar_buf[0] = 'U'; + } + + if (n->prop_state == svn_wc_notify_state_conflicted) + { + nb->prop_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[1] = 'C'; + } + else if (n->prop_state == svn_wc_notify_state_changed) + statchar_buf[1] = 'U'; + + if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ') + { + if ((err = svn_cmdline_printf(pool, "%s %s\n", + statchar_buf, path_local))) + goto print_error; + } + } + break; + + case svn_wc_notify_patch_applied_hunk: + nb->received_some_change = TRUE; + if (n->hunk_original_start != n->hunk_matched_line) + { + apr_uint64_t off; + const char *s; + const char *minus; + + if (n->hunk_matched_line > n->hunk_original_start) + { + off = n->hunk_matched_line - n->hunk_original_start; + minus = ""; + } + else + { + off = n->hunk_original_start - n->hunk_matched_line; + minus = "-"; + } + + /* ### We're creating the localized strings without + * ### APR_INT64_T_FMT since it isn't translator-friendly */ + if (n->hunk_fuzz) + { + + if (n->prop_name) + { + s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " + "with offset %s"); + + err = svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT + " and fuzz %lu (%s)\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->hunk_fuzz, + n->prop_name); + } + else + { + s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " + "with offset %s"); + + err = svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT + " and fuzz %lu\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->hunk_fuzz); + } + + if (err) + goto print_error; + } + else + { + + if (n->prop_name) + { + s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " + "with offset %s"); + err = svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT" (%s)\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off, n->prop_name); + } + else + { + s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " + "with offset %s"); + err = svn_cmdline_printf(pool, + apr_pstrcat(pool, s, + "%"APR_UINT64_T_FMT"\n", + SVN_VA_NULL), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + minus, off); + } + + if (err) + goto print_error; + } + } + else if (n->hunk_fuzz) + { + if (n->prop_name) + err = svn_cmdline_printf(pool, + _("> applied hunk ## -%lu,%lu +%lu,%lu ## " + "with fuzz %lu (%s)\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->hunk_fuzz, + n->prop_name); + else + err = svn_cmdline_printf(pool, + _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " + "with fuzz %lu\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->hunk_fuzz); + if (err) + goto print_error; + + } + break; + + case svn_wc_notify_patch_rejected_hunk: + nb->received_some_change = TRUE; + + if (n->prop_name) + err = svn_cmdline_printf(pool, + _("> rejected hunk " + "## -%lu,%lu +%lu,%lu ## (%s)\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->prop_name); + else + err = svn_cmdline_printf(pool, + _("> rejected hunk " + "@@ -%lu,%lu +%lu,%lu @@\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length); + if (err) + goto print_error; + break; + + case svn_wc_notify_patch_hunk_already_applied: + nb->received_some_change = TRUE; + if (n->prop_name) + err = svn_cmdline_printf(pool, + _("> hunk " + "## -%lu,%lu +%lu,%lu ## " + "already applied (%s)\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length, + n->prop_name); + else + err = svn_cmdline_printf(pool, + _("> hunk " + "@@ -%lu,%lu +%lu,%lu @@ " + "already applied\n"), + n->hunk_original_start, + n->hunk_original_length, + n->hunk_modified_start, + n->hunk_modified_length); + if (err) + goto print_error; + break; + + case svn_wc_notify_update_update: + case svn_wc_notify_merge_record_info: + { + if (n->content_state == svn_wc_notify_state_conflicted) + { + nb->text_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[0] = 'C'; + } + else if (n->kind == svn_node_file) + { + if (n->content_state == svn_wc_notify_state_merged) + statchar_buf[0] = 'G'; + else if (n->content_state == svn_wc_notify_state_changed) + statchar_buf[0] = 'U'; + } + + if (n->prop_state == svn_wc_notify_state_conflicted) + { + nb->prop_conflicts++; + add_conflicted_path(nb, n->path); + statchar_buf[1] = 'C'; + } + else if (n->prop_state == svn_wc_notify_state_merged) + statchar_buf[1] = 'G'; + else if (n->prop_state == svn_wc_notify_state_changed) + statchar_buf[1] = 'U'; + + if (n->lock_state == svn_wc_notify_lock_state_unlocked) + statchar_buf[2] = 'B'; + + if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ') + nb->received_some_change = TRUE; + + if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ' + || statchar_buf[2] != ' ') + { + if ((err = svn_cmdline_printf(pool, "%s %s\n", + statchar_buf, path_local))) + goto print_error; + } + } + break; + + case svn_wc_notify_update_external: + /* Remember that we're now "inside" an externals definition. */ + nb->in_external = TRUE; + + /* Currently this is used for checkouts and switches too. If we + want different output, we'll have to add new actions. */ + if ((err = svn_cmdline_printf(pool, + _("\nFetching external item into '%s':\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_failed_external: + /* If we are currently inside the handling of an externals + definition, then we can simply present n->err as a warning + and feel confident that after this, we aren't handling that + externals definition any longer. */ + if (nb->in_external) + { + svn_handle_warning2(stderr, n->err, "svn: "); + nb->in_external = FALSE; + if ((err = svn_cmdline_printf(pool, "\n"))) + goto print_error; + } + /* Otherwise, we'll just print two warnings. Why? Because + svn_handle_warning2() only shows the single "best message", + but we have two pretty important ones: that the external at + '/some/path' didn't pan out, and then the more specific + reason why (from n->err). */ + else + { + svn_error_t *warn_err = + svn_error_createf(SVN_ERR_BASE, NULL, + _("Error handling externals definition for '%s':"), + path_local); + svn_handle_warning2(stderr, warn_err, "svn: "); + svn_error_clear(warn_err); + svn_handle_warning2(stderr, n->err, "svn: "); + } + break; + + case svn_wc_notify_update_started: + if (! (nb->in_external || + nb->is_checkout || + nb->is_export)) + { + if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"), + path_local))) + goto print_error; + } + break; + + case svn_wc_notify_update_completed: + { + if (SVN_IS_VALID_REVNUM(n->revision)) + { + if (nb->is_export) + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("Exported external at revision %ld.\n") + : _("Exported revision %ld.\n"), + n->revision))) + goto print_error; + } + else if (nb->is_checkout) + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("Checked out external at revision %ld.\n") + : _("Checked out revision %ld.\n"), + n->revision))) + goto print_error; + } + else + { + if (nb->received_some_change) + { + nb->received_some_change = FALSE; + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("Updated external to revision %ld.\n") + : _("Updated to revision %ld.\n"), + n->revision))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("External at revision %ld.\n") + : _("At revision %ld.\n"), + n->revision))) + goto print_error; + } + } + } + else /* no revision */ + { + if (nb->is_export) + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("External export complete.\n") + : _("Export complete.\n")))) + goto print_error; + } + else if (nb->is_checkout) + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("External checkout complete.\n") + : _("Checkout complete.\n")))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf + (pool, nb->in_external + ? _("External update complete.\n") + : _("Update complete.\n")))) + goto print_error; + } + } + } + + if (nb->in_external) + { + nb->in_external = FALSE; + if ((err = svn_cmdline_printf(pool, "\n"))) + goto print_error; + } + break; + + case svn_wc_notify_status_external: + if ((err = svn_cmdline_printf + (pool, _("\nPerforming status on external item at '%s':\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_status_completed: + if (SVN_IS_VALID_REVNUM(n->revision)) + if ((err = svn_cmdline_printf(pool, + _("Status against revision: %6ld\n"), + n->revision))) + goto print_error; + break; + + case svn_wc_notify_commit_modified: + /* xgettext: Align the %s's on this and the following 4 messages */ + if ((err = svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Sending copy of %s\n") + : _("Sending %s\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_commit_added: + case svn_wc_notify_commit_copied: + if (n->mime_type && svn_mime_type_is_binary(n->mime_type)) + { + if ((err = svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Adding copy of (bin) %s\n") + : _("Adding (bin) %s\n"), + path_local))) + goto print_error; + } + else + { + if ((err = svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Adding copy of %s\n") + : _("Adding %s\n"), + path_local))) + goto print_error; + } + break; + + case svn_wc_notify_commit_deleted: + if ((err = svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Deleting copy of %s\n") + : _("Deleting %s\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_commit_replaced: + case svn_wc_notify_commit_copied_replaced: + if ((err = svn_cmdline_printf(pool, + nb->is_wc_to_repos_copy + ? _("Replacing copy of %s\n") + : _("Replacing %s\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_commit_postfix_txdelta: + if (! nb->sent_first_txdelta) + { + nb->sent_first_txdelta = TRUE; + if ((err = svn_cmdline_printf(pool, + _("Transmitting file data ")))) + goto print_error; + } + + if ((err = svn_cmdline_printf(pool, "."))) + goto print_error; + break; + + case svn_wc_notify_locked: + if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), + path_local, n->lock->owner))) + goto print_error; + break; + + case svn_wc_notify_unlocked: + if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_failed_lock: + case svn_wc_notify_failed_unlock: + svn_handle_warning2(stderr, n->err, "svn: "); + break; + + case svn_wc_notify_changelist_set: + if ((err = svn_cmdline_printf(pool, "A [%s] %s\n", + n->changelist_name, path_local))) + goto print_error; + break; + + case svn_wc_notify_changelist_clear: + case svn_wc_notify_changelist_moved: + if ((err = svn_cmdline_printf(pool, + "D [%s] %s\n", + n->changelist_name, path_local))) + goto print_error; + break; + + case svn_wc_notify_merge_begin: + if (n->merge_range == NULL) + err = svn_cmdline_printf(pool, + _("--- Merging differences between " + "repository URLs into '%s':\n"), + path_local); + else if (n->merge_range->start == n->merge_range->end - 1 + || n->merge_range->start == n->merge_range->end) + err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"), + n->merge_range->end, path_local); + else if (n->merge_range->start - 1 == n->merge_range->end) + err = svn_cmdline_printf(pool, + _("--- Reverse-merging r%ld into '%s':\n"), + n->merge_range->start, path_local); + else if (n->merge_range->start < n->merge_range->end) + err = svn_cmdline_printf(pool, + _("--- Merging r%ld through r%ld into " + "'%s':\n"), + n->merge_range->start + 1, + n->merge_range->end, path_local); + else /* n->merge_range->start > n->merge_range->end - 1 */ + err = svn_cmdline_printf(pool, + _("--- Reverse-merging r%ld through r%ld " + "into '%s':\n"), + n->merge_range->start, + n->merge_range->end + 1, path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_merge_record_info_begin: + if (!n->merge_range) + { + err = svn_cmdline_printf(pool, + _("--- Recording mergeinfo for merge " + "between repository URLs into '%s':\n"), + path_local); + } + else + { + if (n->merge_range->start == n->merge_range->end - 1 + || n->merge_range->start == n->merge_range->end) + err = svn_cmdline_printf( + pool, + _("--- Recording mergeinfo for merge of r%ld into '%s':\n"), + n->merge_range->end, path_local); + else if (n->merge_range->start - 1 == n->merge_range->end) + err = svn_cmdline_printf( + pool, + _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"), + n->merge_range->start, path_local); + else if (n->merge_range->start < n->merge_range->end) + err = svn_cmdline_printf( + pool, + _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"), + n->merge_range->start + 1, n->merge_range->end, path_local); + else /* n->merge_range->start > n->merge_range->end - 1 */ + err = svn_cmdline_printf( + pool, + _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"), + n->merge_range->start, n->merge_range->end + 1, path_local); + } + + if (err) + goto print_error; + break; + + case svn_wc_notify_merge_elide_info: + if ((err = svn_cmdline_printf(pool, + _("--- Eliding mergeinfo from '%s':\n"), + path_local))) + goto print_error; + break; + + case svn_wc_notify_foreign_merge_begin: + if (n->merge_range == NULL) + err = svn_cmdline_printf(pool, + _("--- Merging differences between " + "foreign repository URLs into '%s':\n"), + path_local); + else if (n->merge_range->start == n->merge_range->end - 1 + || n->merge_range->start == n->merge_range->end) + err = svn_cmdline_printf(pool, + _("--- Merging (from foreign repository) " + "r%ld into '%s':\n"), + n->merge_range->end, path_local); + else if (n->merge_range->start - 1 == n->merge_range->end) + err = svn_cmdline_printf(pool, + _("--- Reverse-merging (from foreign " + "repository) r%ld into '%s':\n"), + n->merge_range->start, path_local); + else if (n->merge_range->start < n->merge_range->end) + err = svn_cmdline_printf(pool, + _("--- Merging (from foreign repository) " + "r%ld through r%ld into '%s':\n"), + n->merge_range->start + 1, + n->merge_range->end, path_local); + else /* n->merge_range->start > n->merge_range->end - 1 */ + err = svn_cmdline_printf(pool, + _("--- Reverse-merging (from foreign " + "repository) r%ld through r%ld into " + "'%s':\n"), + n->merge_range->start, + n->merge_range->end + 1, path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_tree_conflict: + nb->tree_conflicts++; + add_conflicted_path(nb, n->path); + if ((err = svn_cmdline_printf(pool, " C %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_update_shadowed_add: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, " A %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_update_shadowed_update: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, " U %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_update_shadowed_delete: + nb->received_some_change = TRUE; + if ((err = svn_cmdline_printf(pool, " D %s\n", path_local))) + goto print_error; + break; + + case svn_wc_notify_property_modified: + case svn_wc_notify_property_added: + err = svn_cmdline_printf(pool, + _("property '%s' set on '%s'\n"), + n->prop_name, path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_property_deleted: + err = svn_cmdline_printf(pool, + _("property '%s' deleted from '%s'.\n"), + n->prop_name, path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_property_deleted_nonexistent: + err = svn_cmdline_printf(pool, + _("Attempting to delete nonexistent " + "property '%s' on '%s'\n"), n->prop_name, + path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_revprop_set: + err = svn_cmdline_printf(pool, + _("property '%s' set on repository revision %ld\n"), + n->prop_name, n->revision); + if (err) + goto print_error; + break; + + case svn_wc_notify_revprop_deleted: + err = svn_cmdline_printf(pool, + _("property '%s' deleted from repository revision %ld\n"), + n->prop_name, n->revision); + if (err) + goto print_error; + break; + + case svn_wc_notify_upgraded_path: + err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_url_redirect: + err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"), + n->url); + if (err) + goto print_error; + break; + + case svn_wc_notify_path_nonexistent: + err = svn_cmdline_printf(pool, _("'%s' is not under version control"), + path_local); + if (err) + goto print_error; + break; + + case svn_wc_notify_conflict_resolver_starting: + /* Once all operations invoke the interactive conflict resolution after + * they've completed, we can run svn_cl__print_conflict_stats() here. */ + break; + + case svn_wc_notify_conflict_resolver_done: + break; + + default: + break; + } + + if ((err = svn_cmdline_fflush(stdout))) + goto print_error; + + return; + + print_error: + /* If we had no errors before, print this error to stderr. Else, don't print + anything. The user already knows there were some output errors, + so there is no point in flooding her with an error per notification. */ + if (!nb->had_print_error) + { + nb->had_print_error = TRUE; + /* Issue #3014: + * Don't print anything on broken pipes. The pipe was likely + * closed by the process at the other end. We expect that + * process to perform error reporting as necessary. + * + * ### This assumes that there is only one error in a chain for + * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */ + if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR) + svn_handle_error2(err, stderr, FALSE, "svn: "); + } + svn_error_clear(err); +} + + +svn_error_t * +svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p, + void **notify_baton_p, + apr_pool_t *pool) +{ + struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb)); + + nb->received_some_change = FALSE; + nb->sent_first_txdelta = FALSE; + nb->is_checkout = FALSE; + nb->is_export = FALSE; + nb->is_wc_to_repos_copy = FALSE; + nb->in_external = FALSE; + nb->had_print_error = FALSE; + nb->text_conflicts = 0; + nb->prop_conflicts = 0; + nb->tree_conflicts = 0; + nb->skipped_paths = 0; + nb->conflicted_paths = apr_hash_make(pool); + SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool)); + + *notify_func_p = notify; + *notify_baton_p = nb; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_cl__notifier_mark_export(void *baton) +{ + struct notify_baton *nb = baton; + + nb->is_export = TRUE; + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnbench/null-blame-cmd.c b/contrib/subversion/subversion/svnbench/null-blame-cmd.c new file mode 100644 index 000000000..74d87dc26 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/null-blame-cmd.c @@ -0,0 +1,276 @@ +/* + * null-blame-cmd.c -- Subversion export command + * + * ==================================================================== + * 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_client.h" +#include "svn_cmdline.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_pools.h" +#include "svn_sorts.h" +#include "cl.h" + +#include "svn_private_config.h" +#include "private/svn_string_private.h" +#include "private/svn_client_private.h" + +struct file_rev_baton { + apr_int64_t byte_count; + apr_int64_t delta_count; + apr_int64_t rev_count; +}; + +/* Implements svn_txdelta_window_handler_t */ +static svn_error_t * +delta_handler(svn_txdelta_window_t *window, void *baton) +{ + struct file_rev_baton *frb = baton; + + if (window != NULL) + frb->byte_count += window->tview_len; + + return SVN_NO_ERROR; +} + +/* Implementes svn_file_rev_handler_t */ +static svn_error_t * +file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, + apr_hash_t *rev_props, + svn_boolean_t merged_revision, + svn_txdelta_window_handler_t *content_delta_handler, + void **content_delta_baton, + apr_array_header_t *prop_diffs, + apr_pool_t *pool) +{ + struct file_rev_baton *frb = baton; + + frb->rev_count++; + + if (content_delta_handler) + { + *content_delta_handler = delta_handler; + *content_delta_baton = baton; + frb->delta_count++; + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +bench_null_blame(const char *target, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *start, + const svn_opt_revision_t *end, + svn_boolean_t include_merged_revisions, + svn_boolean_t quiet, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + struct file_rev_baton frb = { 0, 0, 0}; + svn_ra_session_t *ra_session; + svn_revnum_t start_revnum, end_revnum; + svn_boolean_t backwards; + const char *target_abspath_or_url; + + if (start->kind == svn_opt_revision_unspecified + || end->kind == svn_opt_revision_unspecified) + return svn_error_create + (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); + + if (svn_path_is_url(target)) + target_abspath_or_url = target; + else + SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool)); + + + /* Get an RA plugin for this filesystem object. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, + target, NULL, peg_revision, + peg_revision, + ctx, pool)); + + SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx, + target_abspath_or_url, ra_session, + start, pool)); + + SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx, + target_abspath_or_url, ra_session, + end, pool)); + + { + svn_client__pathrev_t *loc; + svn_opt_revision_t younger_end; + younger_end.kind = svn_opt_revision_number; + younger_end.value.number = MAX(start_revnum, end_revnum); + + SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session, + target, peg_revision, + &younger_end, + ctx, pool)); + + /* Make the session point to the real URL. */ + SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool)); + } + + backwards = (start_revnum > end_revnum); + + /* Collect all blame information. + We need to ensure that we get one revision before the start_rev, + if available so that we can know what was actually changed in the start + revision. */ + SVN_ERR(svn_ra_get_file_revs2(ra_session, "", + backwards ? start_revnum + : MAX(0, start_revnum-1), + end_revnum, + include_merged_revisions, + file_rev_handler, &frb, pool)); + + if (!quiet) + SVN_ERR(svn_cmdline_printf(pool, + _("%15s revisions\n" + "%15s deltas\n" + "%15s bytes in deltas\n"), + svn__ui64toa_sep(frb.rev_count, ',', pool), + svn__ui64toa_sep(frb.delta_count, ',', pool), + svn__ui64toa_sep(frb.byte_count, ',', pool))); + + return SVN_NO_ERROR; +} + + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_blame(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_pool_t *iterpool; + apr_array_header_t *targets; + int i; + svn_boolean_t end_revision_unspecified = FALSE; + svn_boolean_t seen_nonexistent_target = FALSE; + + SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, + opt_state->targets, + ctx, FALSE, pool)); + + /* Blame needs a file on which to operate. */ + if (! targets->nelts) + return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); + + if (opt_state->end_revision.kind == svn_opt_revision_unspecified) + { + if (opt_state->start_revision.kind != svn_opt_revision_unspecified) + { + /* In the case that -rX was specified, we actually want to set the + range to be -r1:X. */ + + opt_state->end_revision = opt_state->start_revision; + opt_state->start_revision.kind = svn_opt_revision_number; + opt_state->start_revision.value.number = 1; + } + else + end_revision_unspecified = TRUE; + } + + if (opt_state->start_revision.kind == svn_opt_revision_unspecified) + { + opt_state->start_revision.kind = svn_opt_revision_number; + opt_state->start_revision.value.number = 1; + } + + /* The final conclusion from issue #2431 is that blame info + is client output (unlike 'svn cat' which plainly cats the file), + so the EOL style should be the platform local one. + */ + iterpool = svn_pool_create(pool); + + for (i = 0; i < targets->nelts; i++) + { + svn_error_t *err; + const char *target = APR_ARRAY_IDX(targets, i, const char *); + const char *parsed_path; + svn_opt_revision_t peg_revision; + + svn_pool_clear(iterpool); + SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); + + /* Check for a peg revision. */ + SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target, + iterpool)); + + if (end_revision_unspecified) + { + if (peg_revision.kind != svn_opt_revision_unspecified) + opt_state->end_revision = peg_revision; + else if (svn_path_is_url(target)) + opt_state->end_revision.kind = svn_opt_revision_head; + else + opt_state->end_revision.kind = svn_opt_revision_working; + } + + err = bench_null_blame(parsed_path, + &peg_revision, + &opt_state->start_revision, + &opt_state->end_revision, + opt_state->use_merge_history, + opt_state->quiet, + ctx, + iterpool); + + if (err) + { + if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND || + err->apr_err == SVN_ERR_ENTRY_NOT_FOUND || + err->apr_err == SVN_ERR_FS_NOT_FILE || + err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_handle_warning2(stderr, err, "svn: "); + svn_error_clear(err); + err = NULL; + seen_nonexistent_target = TRUE; + } + else + { + return svn_error_trace(err); + } + } + } + svn_pool_destroy(iterpool); + + if (seen_nonexistent_target) + return svn_error_create( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("Could not perform blame on all targets because some " + "targets don't exist")); + else + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnbench/null-export-cmd.c b/contrib/subversion/subversion/svnbench/null-export-cmd.c new file mode 100644 index 000000000..8220bfbe9 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/null-export-cmd.c @@ -0,0 +1,346 @@ +/* + * export-cmd.c -- Subversion export command + * + * ==================================================================== + * 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_client.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_cmdline.h" +#include "cl.h" + +#include "svn_private_config.h" +#include "private/svn_string_private.h" +#include "private/svn_client_private.h" + +/*** The export editor code. ***/ + +/* ---------------------------------------------------------------------- */ + +/*** A dedicated 'export' editor, which does no .svn/ accounting. ***/ + +typedef struct edit_baton_t +{ + apr_int64_t file_count; + apr_int64_t dir_count; + apr_int64_t byte_count; + apr_int64_t prop_count; + apr_int64_t prop_byte_count; +} edit_baton_t; + +static svn_error_t * +set_target_revision(void *edit_baton, + svn_revnum_t target_revision, + apr_pool_t *pool) +{ + return SVN_NO_ERROR; +} + + +/* Just ensure that the main export directory exists. */ +static svn_error_t * +open_root(void *edit_baton, + svn_revnum_t base_revision, + apr_pool_t *pool, + void **root_baton) +{ + *root_baton = edit_baton; + return SVN_NO_ERROR; +} + + +/* Ensure the directory exists, and send feedback. */ +static svn_error_t * +add_directory(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *pool, + void **baton) +{ + edit_baton_t *eb = parent_baton; + eb->dir_count++; + + *baton = parent_baton; + return SVN_NO_ERROR; +} + + +/* Build a file baton. */ +static svn_error_t * +add_file(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *pool, + void **baton) +{ + edit_baton_t *eb = parent_baton; + eb->file_count++; + + *baton = parent_baton; + return SVN_NO_ERROR; +} + +static svn_error_t * +window_handler(svn_txdelta_window_t *window, void *baton) +{ + edit_baton_t *eb = baton; + if (window != NULL) + eb->byte_count += window->tview_len; + + return SVN_NO_ERROR; +} + +/* Write incoming data into the tmpfile stream */ + +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) +{ + *handler_baton = file_baton; + *handler = window_handler; + + 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) +{ + edit_baton_t *eb = file_baton; + eb->prop_count++; + eb->prop_byte_count += value->len; + + 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) +{ + edit_baton_t *eb = dir_baton; + eb->prop_count++; + + return SVN_NO_ERROR; +} + +static svn_error_t * +close_file(void *file_baton, + const char *text_checksum, + apr_pool_t *pool) +{ + return SVN_NO_ERROR; +} + + +/*** Public Interfaces ***/ + +static svn_error_t * +bench_null_export(svn_revnum_t *result_rev, + const char *from_path_or_url, + svn_opt_revision_t *peg_revision, + svn_opt_revision_t *revision, + svn_depth_t depth, + void *baton, + svn_client_ctx_t *ctx, + svn_boolean_t quiet, + apr_pool_t *pool) +{ + svn_revnum_t edit_revision = SVN_INVALID_REVNUM; + svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url); + + SVN_ERR_ASSERT(peg_revision != NULL); + SVN_ERR_ASSERT(revision != NULL); + + if (peg_revision->kind == svn_opt_revision_unspecified) + peg_revision->kind = svn_path_is_url(from_path_or_url) + ? svn_opt_revision_head + : svn_opt_revision_working; + + if (revision->kind == svn_opt_revision_unspecified) + revision = peg_revision; + + if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)) + { + svn_client__pathrev_t *loc; + svn_ra_session_t *ra_session; + svn_node_kind_t kind; + + /* Get the RA connection. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, + from_path_or_url, NULL, + peg_revision, + revision, ctx, pool)); + + SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool)); + + if (kind == svn_node_file) + { + apr_hash_t *props; + + /* Since you cannot actually root an editor at a file, we + * manually drive a few functions of our editor. */ + + /* Step outside the editor-likeness for a moment, to actually talk + * to the repository. */ + /* ### note: the stream will not be closed */ + SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, + svn_stream_empty(pool), + NULL, &props, pool)); + } + else if (kind == svn_node_dir) + { + void *edit_baton = NULL; + const svn_delta_editor_t *export_editor = NULL; + const svn_ra_reporter3_t *reporter; + void *report_baton; + + svn_delta_editor_t *editor = svn_delta_default_editor(pool); + + editor->set_target_revision = set_target_revision; + editor->open_root = open_root; + editor->add_directory = add_directory; + editor->add_file = add_file; + editor->apply_textdelta = apply_textdelta; + editor->close_file = close_file; + editor->change_file_prop = change_file_prop; + editor->change_dir_prop = change_dir_prop; + + /* for ra_svn, we don't need an editior in quiet mode */ + if (!quiet || strncmp(loc->repos_root_url, "svn:", 4)) + SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func, + ctx->cancel_baton, + editor, + baton, + &export_editor, + &edit_baton, + pool)); + + /* Manufacture a basic 'report' to the update reporter. */ + SVN_ERR(svn_ra_do_update3(ra_session, + &reporter, &report_baton, + loc->rev, + "", /* no sub-target */ + depth, + FALSE, /* don't want copyfrom-args */ + FALSE, /* don't want ignore_ancestry */ + export_editor, edit_baton, + pool, pool)); + + SVN_ERR(reporter->set_path(report_baton, "", loc->rev, + /* Depth is irrelevant, as we're + passing start_empty=TRUE anyway. */ + svn_depth_infinity, + TRUE, /* "help, my dir is empty!" */ + NULL, pool)); + + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + else if (kind == svn_node_none) + { + return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, + _("URL '%s' doesn't exist"), + from_path_or_url); + } + /* kind == svn_node_unknown not handled */ + } + + + if (result_rev) + *result_rev = edit_revision; + + return SVN_NO_ERROR; +} + + +/*** Code. ***/ + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_export(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; + const char *from; + apr_array_header_t *targets; + svn_error_t *err; + svn_opt_revision_t peg_revision; + const char *truefrom; + edit_baton_t eb = { 0 }; + + SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, + opt_state->targets, + ctx, FALSE, pool)); + + /* We want exactly 1 or 2 targets for this subcommand. */ + if (targets->nelts < 1) + return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); + if (targets->nelts > 2) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL); + + /* The first target is the `from' path. */ + from = APR_ARRAY_IDX(targets, 0, const char *); + + /* Get the peg revision if present. */ + SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool)); + + if (opt_state->depth == svn_depth_unknown) + opt_state->depth = svn_depth_infinity; + + /* Do the export. */ + err = bench_null_export(NULL, truefrom, &peg_revision, + &(opt_state->start_revision), + opt_state->depth, + &eb, + ctx, opt_state->quiet, pool); + + if (!opt_state->quiet) + SVN_ERR(svn_cmdline_printf(pool, + _("%15s directories\n" + "%15s files\n" + "%15s bytes in files\n" + "%15s properties\n" + "%15s bytes in properties\n"), + svn__ui64toa_sep(eb.dir_count, ',', pool), + svn__ui64toa_sep(eb.file_count, ',', pool), + svn__ui64toa_sep(eb.byte_count, ',', pool), + svn__ui64toa_sep(eb.prop_count, ',', pool), + svn__ui64toa_sep(eb.prop_byte_count, ',', pool))); + + return svn_error_trace(err); +} diff --git a/contrib/subversion/subversion/svnbench/null-info-cmd.c b/contrib/subversion/subversion/svnbench/null-info-cmd.c new file mode 100644 index 000000000..c9b8710a8 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/null-info-cmd.c @@ -0,0 +1,287 @@ +/* + * info-cmd.c -- Display information about a resource + * + * ==================================================================== + * 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_string.h" +#include "svn_cmdline.h" +#include "svn_wc.h" +#include "svn_pools.h" +#include "svn_error_codes.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_time.h" +#include "svn_xml.h" +#include "cl.h" + +#include "svn_private_config.h" +#include "private/svn_client_private.h" + + +/*** Code. ***/ + +/* The dirent fields we care about for our calls to svn_ra_get_dir2. */ +#define DIRENT_FIELDS (SVN_DIRENT_KIND | \ + SVN_DIRENT_CREATED_REV | \ + SVN_DIRENT_TIME | \ + SVN_DIRENT_LAST_AUTHOR) + +/* Helper func for recursively fetching svn_dirent_t's from a remote + directory and pushing them at an info-receiver callback. + + DEPTH is the depth starting at DIR, even though RECEIVER is never + invoked on DIR: if DEPTH is svn_depth_immediates, then increment + *COUNTER on all children of DIR, but none of their children; if + svn_depth_files, then increment *COUNTER on file children of DIR but + not on subdirectories; if svn_depth_infinity, recurse fully. + DIR is a relpath, relative to the root of RA_SESSION. +*/ +static svn_error_t * +push_dir_info(svn_ra_session_t *ra_session, + const svn_client__pathrev_t *pathrev, + const char *dir, + int *counter, + svn_depth_t depth, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + apr_hash_t *tmpdirents; + apr_hash_index_t *hi; + apr_pool_t *subpool = svn_pool_create(pool); + + SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL, + dir, pathrev->rev, DIRENT_FIELDS, pool)); + + for (hi = apr_hash_first(pool, tmpdirents); hi; hi = apr_hash_next(hi)) + { + const char *path; + const char *name = apr_hash_this_key(hi); + svn_dirent_t *the_ent = apr_hash_this_val(hi); + svn_client__pathrev_t *child_pathrev; + + svn_pool_clear(subpool); + + if (ctx->cancel_func) + SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); + + path = svn_relpath_join(dir, name, subpool); + child_pathrev = svn_client__pathrev_join_relpath(pathrev, name, subpool); + + if (depth >= svn_depth_immediates + || (depth == svn_depth_files && the_ent->kind == svn_node_file)) + ++(*counter); + + if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir) + SVN_ERR(push_dir_info(ra_session, child_pathrev, path, + counter, depth, ctx, subpool)); + } + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + +/* Stripped-down version of svn_client_info3 */ +static svn_error_t * +client_info(const char *abspath_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + svn_boolean_t fetch_excluded, + svn_boolean_t fetch_actual_only, + const apr_array_header_t *changelists, + int *counter, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_client__pathrev_t *pathrev; + svn_lock_t *lock; + const char *base_name; + svn_dirent_t *the_ent; + svn_error_t *err; + + if (depth == svn_depth_unknown) + depth = svn_depth_empty; + + /* Go repository digging instead. */ + + /* Trace rename history (starting at path_or_url@peg_revision) and + return RA session to the possibly-renamed URL as it exists in REVISION. + The ra_session returned will be anchored on this "final" URL. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, + abspath_or_url, NULL, peg_revision, + revision, ctx, pool)); + + svn_uri_split(NULL, &base_name, pathrev->url, pool); + + /* Get the dirent for the URL itself. */ + SVN_ERR(svn_ra_stat(ra_session, "", pathrev->rev, &the_ent, pool)); + + if (! the_ent) + return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, + _("URL '%s' non-existent in revision %ld"), + pathrev->url, pathrev->rev); + + /* check for locks */ + err = svn_ra_get_lock(ra_session, &lock, "", pool); + + /* An old mod_dav_svn will always work; there's nothing wrong with + doing a PROPFIND for a property named "DAV:supportedlock". But + an old svnserve will error. */ + if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) + { + svn_error_clear(err); + lock = NULL; + } + else if (err) + return svn_error_trace(err); + + /* Push the URL's dirent (and lock) at the callback.*/ + ++(*counter); + + /* Possibly recurse, using the original RA session. */ + if (depth > svn_depth_empty && (the_ent->kind == svn_node_dir)) + { + apr_hash_t *locks; + + if (peg_revision->kind == svn_opt_revision_head) + { + err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool); + + /* Catch specific errors thrown by old mod_dav_svn or svnserve. */ + if (err && + (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED + || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) + svn_error_clear(err); + else if (err) + return svn_error_trace(err); + } + + SVN_ERR(push_dir_info(ra_session, pathrev, "", + counter, depth, ctx, pool)); + } + + return SVN_NO_ERROR; +} + + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_info(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 = NULL; + apr_pool_t *subpool = svn_pool_create(pool); + int i; + svn_error_t *err; + svn_boolean_t seen_nonexistent_target = FALSE; + svn_opt_revision_t peg_revision; + const char *path_prefix; + + 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->depth == svn_depth_unknown) + opt_state->depth = svn_depth_empty; + + SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool)); + + for (i = 0; i < targets->nelts; i++) + { + const char *truepath; + const char *target = APR_ARRAY_IDX(targets, i, const char *); + int received_count = 0; + + 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 no peg-rev was attached to a URL target, then assume HEAD. */ + if (svn_path_is_url(truepath)) + { + if (peg_revision.kind == svn_opt_revision_unspecified) + peg_revision.kind = svn_opt_revision_head; + } + else + { + SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool)); + } + + err = client_info(truepath, + &peg_revision, &(opt_state->start_revision), + opt_state->depth, TRUE, TRUE, + NULL, + &received_count, + 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_RA_ILLEGAL_URL) + { + svn_handle_warning2(stderr, err, "svnbench: "); + svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n")); + } + else + { + return svn_error_trace(err); + } + + svn_error_clear(err); + err = NULL; + seen_nonexistent_target = TRUE; + } + else + { + SVN_ERR(svn_cmdline_printf(pool, _("Number of status notifications " + "received: %d\n"), + received_count)); + } + } + svn_pool_destroy(subpool); + + if (seen_nonexistent_target) + return svn_error_create( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("Could not display info for all targets because some " + "targets don't exist")); + else + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnbench/null-list-cmd.c b/contrib/subversion/subversion/svnbench/null-list-cmd.c new file mode 100644 index 000000000..3f1920924 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/null-list-cmd.c @@ -0,0 +1,169 @@ +/* + * 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" +#include "private/svn_string_private.h" + + + +/* Baton used when printing directory entries. */ +struct print_baton { + svn_boolean_t verbose; + apr_int64_t directories; + apr_int64_t files; + apr_int64_t locks; + svn_client_ctx_t *ctx; +}; + +/* 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 *pool) +{ + struct print_baton *pb = baton; + + if (pb->ctx->cancel_func) + SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton)); + + if (dirent->kind == svn_node_dir) + pb->directories++; + if (dirent->kind == svn_node_file) + pb->files++; + if (lock) + pb->locks++; + + return SVN_NO_ERROR; +} + + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_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 = { FALSE }; + svn_boolean_t seen_nonexistent_target = FALSE; + svn_error_t *err; + + 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->verbose) + dirent_fields = SVN_DIRENT_ALL; + else + dirent_fields = SVN_DIRENT_KIND; /* the only thing we actually need... */ + + pb.ctx = ctx; + pb.verbose = opt_state->verbose; + + if (opt_state->depth == svn_depth_unknown) + opt_state->depth = svn_depth_immediates; + + /* 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; + + 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)); + + err = svn_client_list3(truepath, &peg_revision, + &(opt_state->start_revision), + opt_state->depth, + dirent_fields, + opt_state->verbose, + FALSE, /* include externals */ + 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, "svnbench: "); + else + return svn_error_trace(err); + + svn_error_clear(err); + err = NULL; + seen_nonexistent_target = TRUE; + } + else if (!opt_state->quiet) + SVN_ERR(svn_cmdline_printf(pool, + _("%15s directories\n" + "%15s files\n" + "%15s locks\n"), + svn__ui64toa_sep(pb.directories, ',', pool), + svn__ui64toa_sep(pb.files, ',', pool), + svn__ui64toa_sep(pb.locks, ',', pool))); + } + + svn_pool_destroy(subpool); + + if (seen_nonexistent_target) + return svn_error_create( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("Could not list all targets because some targets don't exist")); + else + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnbench/null-log-cmd.c b/contrib/subversion/subversion/svnbench/null-log-cmd.c new file mode 100644 index 000000000..e8f9734f6 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/null-log-cmd.c @@ -0,0 +1,243 @@ +/* + * log-cmd.c -- Display 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 +#define APR_WANT_STDIO +#include + +#include "svn_cmdline.h" +#include "svn_compat.h" +#include "svn_path.h" +#include "svn_props.h" + +#include "cl.h" + +#include "svn_private_config.h" +#include "private/svn_string_private.h" + + +/*** Code. ***/ + +/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */ +struct log_receiver_baton +{ + /* Client context. */ + svn_client_ctx_t *ctx; + + /* Level of merge revision nesting */ + apr_size_t merge_depth; + + /* collect counters? */ + svn_boolean_t quiet; + + /* total revision counters */ + apr_int64_t revisions; + apr_int64_t changes; + apr_int64_t message_lines; + + /* part that came from merges */ + apr_int64_t merges; + apr_int64_t merged_revs; + apr_int64_t merged_changes; + apr_int64_t merged_message_lines; +}; + + +/* Implement `svn_log_entry_receiver_t', printing the logs in + * a human-readable and machine-parseable format. + * + * BATON is of type `struct log_receiver_baton'. + */ +static svn_error_t * +log_entry_receiver(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool) +{ + struct log_receiver_baton *lb = baton; + const char *author; + const char *date; + const char *message; + + if (lb->ctx->cancel_func) + SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton)); + + if (! SVN_IS_VALID_REVNUM(log_entry->revision)) + { + lb->merge_depth--; + return SVN_NO_ERROR; + } + + /* if we don't want counters, we are done */ + if (lb->quiet) + return SVN_NO_ERROR; + + /* extract the message and do all the other counting */ + svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops); + if (log_entry->revision == 0 && message == NULL) + return SVN_NO_ERROR; + + lb->revisions++; + if (lb->merge_depth) + lb->merged_revs++; + + if (message != NULL) + { + int count = svn_cstring_count_newlines(message) + 1; + lb->message_lines += count; + if (lb->merge_depth) + lb->merged_message_lines += count; + } + + if (log_entry->changed_paths2) + { + unsigned count = apr_hash_count(log_entry->changed_paths2); + lb->changes += count; + if (lb->merge_depth) + lb->merged_changes += count; + } + + if (log_entry->has_children) + { + lb->merge_depth++; + lb->merges++; + } + + return SVN_NO_ERROR; +} + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_log(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; + struct log_receiver_baton lb = { 0 }; + const char *target; + int i; + apr_array_header_t *revprops; + svn_opt_revision_t target_peg_revision; + const char *target_path_or_url; + + 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); + + /* Determine if they really want a two-revision range. */ + if (opt_state->used_change_arg) + { + if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1) + { + return svn_error_create + (SVN_ERR_CLIENT_BAD_REVISION, NULL, + _("-c and -r are mutually exclusive")); + } + for (i = 0; i < opt_state->revision_ranges->nelts; i++) + { + svn_opt_revision_range_t *range; + range = APR_ARRAY_IDX(opt_state->revision_ranges, i, + svn_opt_revision_range_t *); + if (range->start.value.number < range->end.value.number) + range->start.value.number++; + else + range->end.value.number++; + } + } + + /* Parse the first target into path-or-url and peg revision. */ + target = APR_ARRAY_IDX(targets, 0, const char *); + SVN_ERR(svn_opt_parse_path(&target_peg_revision, &target_path_or_url, + target, pool)); + if (target_peg_revision.kind == svn_opt_revision_unspecified) + target_peg_revision.kind = (svn_path_is_url(target) + ? svn_opt_revision_head + : svn_opt_revision_working); + APR_ARRAY_IDX(targets, 0, const char *) = target_path_or_url; + + if (svn_path_is_url(target)) + { + for (i = 1; i < targets->nelts; i++) + { + target = APR_ARRAY_IDX(targets, i, const char *); + + if (svn_path_is_url(target) || target[0] == '/') + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Only relative paths can be specified" + " after a URL for 'svnbench log', " + "but '%s' is not a relative path"), + target); + } + } + + lb.ctx = ctx; + lb.quiet = opt_state->quiet; + + revprops = apr_array_make(pool, 3, sizeof(char *)); + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR; + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; + if (!opt_state->quiet) + APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; + SVN_ERR(svn_client_log5(targets, + &target_peg_revision, + opt_state->revision_ranges, + opt_state->limit, + opt_state->verbose, + opt_state->stop_on_copy, + opt_state->use_merge_history, + revprops, + log_entry_receiver, + &lb, + ctx, + pool)); + + if (!opt_state->quiet) + { + if (opt_state->use_merge_history) + SVN_ERR(svn_cmdline_printf(pool, + _("%15s revisions, %15s merged in %s merges\n" + "%15s msg lines, %15s in merged revisions\n" + "%15s changes, %15s in merged revisions\n"), + svn__ui64toa_sep(lb.revisions, ',', pool), + svn__ui64toa_sep(lb.merged_revs, ',', pool), + svn__ui64toa_sep(lb.merges, ',', pool), + svn__ui64toa_sep(lb.message_lines, ',', pool), + svn__ui64toa_sep(lb.merged_message_lines, ',', pool), + svn__ui64toa_sep(lb.changes, ',', pool), + svn__ui64toa_sep(lb.merged_changes, ',', pool))); + else + SVN_ERR(svn_cmdline_printf(pool, + _("%15s revisions\n" + "%15s msg lines\n" + "%15s changes\n"), + svn__ui64toa_sep(lb.revisions, ',', pool), + svn__ui64toa_sep(lb.message_lines, ',', pool), + svn__ui64toa_sep(lb.changes, ',', pool))); + } + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnbench/svnbench.c b/contrib/subversion/subversion/svnbench/svnbench.c new file mode 100644 index 000000000..bd67e0b35 --- /dev/null +++ b/contrib/subversion/subversion/svnbench/svnbench.c @@ -0,0 +1,1005 @@ +/* + * svnbench.c: Subversion benchmark 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. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include +#include + +#include + +#include "svn_cmdline.h" +#include "svn_dirent_uri.h" +#include "svn_pools.h" +#include "svn_utf.h" +#include "svn_version.h" + +#include "cl.h" + +#include "private/svn_opt_private.h" +#include "private/svn_cmdline_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_config_dir, + opt_config_options, + opt_depth, + opt_no_auth_cache, + opt_non_interactive, + opt_stop_on_copy, + opt_strict, + opt_targets, + opt_version, + opt_with_revprop, + opt_with_all_revprops, + opt_with_no_revprops, + opt_trust_server_cert, + opt_trust_server_cert_failures, + opt_changelist +} 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[] = +{ + {"help", 'h', 0, N_("show help on a subcommand")}, + {NULL, '?', 0, N_("show help on a subcommand")}, + {"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")}, + {"version", opt_version, 0, N_("show program version information")}, + {"verbose", 'v', 0, N_("print extra information")}, + {"username", opt_auth_username, 1, N_("specify a username ARG")}, + {"password", opt_auth_password, 1, N_("specify a password ARG")}, + {"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')")}, + {"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-auth-cache", opt_no_auth_cache, 0, + N_("do not cache authentication tokens")}, + {"trust-server-cert", opt_trust_server_cert, 0, + N_("deprecated; same as\n" + " " + "--trust-server-cert-failures=unknown-ca")}, + {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, + N_("with --non-interactive, accept SSL server\n" + " " + "certificates with failures; ARG is comma-separated\n" + " " + "list of 'unknown-ca' (Unknown Authority),\n" + " " + "'cn-mismatch' (Hostname mismatch), 'expired'\n" + " " + "(Expired certificate), 'not-yet-valid' (Not yet\n" + " " + "valid certificate) and 'other' (all other not\n" + " " + "separately classified certificate errors).")}, + {"non-interactive", opt_non_interactive, 0, + N_("do no interactive prompting")}, + {"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")}, + {"limit", 'l', 1, N_("maximum number of log entries")}, + {"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")}, + {"use-merge-history", 'g', 0, + N_("use/display additional information from merge\n" + " " + "history")}, + + /* Long-opt Aliases + * + * These have NULL desriptions, but an option code that matches some + * other option (whose description should probably mention its aliases). + */ + + {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_trust_server_cert, opt_trust_server_cert_failures, + opt_config_dir, opt_config_options, 0 +}; + +const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] = +{ + { "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 "-?". */ + + { "null-blame", svn_cl__null_blame, {0}, N_ + ("Fetch all versions of a file in a batch.\n" + "usage: null-blame [-rM:N] TARGET[@REV]...\n" + "\n" + " With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the last revision at or before rN that changed or added the line,\n" + " looking back no further than rM.\n" + "\n" + " With a reverse revision range '-r M:N' where M > N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the next revision after rN that changed or deleted the line,\n" + " looking forward no further than rM.\n" + "\n" + " If specified, REV determines in which revision the target is first\n" + " looked up.\n" + "\n" + " Write the annotated result to standard output.\n"), + {'r', 'g'} }, + + { "null-export", svn_cl__null_export, {0}, N_ + ("Create an unversioned copy of a tree.\n" + "usage: null-export [-r REV] URL[@PEGREV]\n" + "\n" + " Exports a clean directory tree from the repository specified by\n" + " URL, at revision REV if it is given, otherwise at HEAD.\n" + "\n" + " If specified, PEGREV determines in which revision the target is first\n" + " looked up.\n"), + {'r', 'q', 'N', opt_depth} }, + + { "null-list", svn_cl__null_list, {"ls"}, N_ + ("List directory entries in the repository.\n" + "usage: null-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 fetched 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', 'q', 'R', opt_depth} }, + + { "null-log", svn_cl__null_log, {0}, N_ + ("Fetch the log messages for a set of revision(s) and/or path(s).\n" + "usage: 1. null-log [PATH][@REV]\n" + " 2. null-log URL[@REV] [PATH...]\n" + "\n" + " 1. Fetch 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. Fetch 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"), + {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, + 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,}, + {{opt_with_revprop, N_("retrieve revision property ARG")}, + {'c', N_("the change made in revision ARG")}} }, + + { "null-info", svn_cl__null_info, {0}, N_ + ("Display information about a local or remote item.\n" + "usage: null-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_changelist} + }, + + { 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 }, + { 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; +} + + +/*** Main. ***/ + +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, 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; + 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 use_notifier = TRUE; + + received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); + + /* Check library versions */ + SVN_ERR(check_lib_versions()); + +#if defined(WIN32) || defined(__CYGWIN__) + /* Set the working copy administrative directory name. */ + if (getenv("SVN_ASP_DOT_NET_HACK")) + { + SVN_ERR(svn_wc_set_adm_dir("_svn", pool)); + } +#endif + + /* Initialize the RA library. */ + SVN_ERR(svn_ra_initialize(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; + + /* No args? Show usage. */ + if (argc <= 1) + { + SVN_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* Else, parse options. */ + SVN_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_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* 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) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Non-numeric limit argument given")); + } + if (opt_state.limit <= 0) + { + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Argument to --limit must be positive")); + } + } + break; + case 'c': + { + apr_array_header_t *change_revs = + svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, pool); + + 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) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, + NULL, + _("Negative number in range (%s)" + " not supported with -c"), + change_str); + } + s = end + 1; + while (*s == 'r') + s++; + changeno_end = strtol(s, &end, 10); + } + if (end == change_str || *end != '\0') + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Non-numeric change argument (%s) " + "given to -c"), change_str); + } + + if (changeno == 0) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("There is no change 0")); + } + + 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_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + return svn_error_createf + (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Syntax error in revision argument '%s'"), + utf8_opt_arg); + } + break; + case 'v': + opt_state.verbose = TRUE; + break; + case 'h': + case '?': + opt_state.help = TRUE; + break; + case 'q': + opt_state.quiet = TRUE; + 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_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool)); + SVN_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 '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 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 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_version: + opt_state.version = TRUE; + break; + case opt_auth_username: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_username, + opt_arg, pool)); + break; + case opt_auth_password: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_password, + opt_arg, pool)); + break; + case opt_stop_on_copy: + opt_state.stop_on_copy = TRUE; + break; + case opt_strict: + opt_state.strict = 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_trust_server_cert: /* backwards compat to 1.8 */ + opt_state.trust_server_cert_unknown_ca = TRUE; + break; + case opt_trust_server_cert_failures: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &opt_state.trust_server_cert_unknown_ca, + &opt_state.trust_server_cert_cn_mismatch, + &opt_state.trust_server_cert_expired, + &opt_state.trust_server_cert_not_yet_valid, + &opt_state.trust_server_cert_other_failure, + utf8_opt_arg, pool)); + break; + case opt_config_dir: + { + const char *path_utf8; + SVN_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_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(opt_state.config_options, + opt_arg, "svnbench: ", pool)); + 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_ERR(svn_opt_parse_revprop(&opt_state.revprop_table, + opt_arg, pool)); + break; + case 'g': + opt_state.use_merge_history = TRUE; + break; + default: + /* Hmmm. Perhaps this would be a good place to squirrel away + opts that commands like svn diff might need. Hmmm indeed. */ + break; + } + } + + /* ### 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_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_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + 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_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_ERR(svn_cl__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + } + + /* 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_ERR(svn_cl__help(NULL, NULL, pool)); + else + svn_error_clear + (svn_cmdline_fprintf + (stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n" + "Type 'svnbench help %s' for usage.\n"), + subcommand->name, optstr, subcommand->name)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + + /* Only merge and log support multiple revisions/revision ranges. */ + if (subcommand->cmd_func != svn_cl__null_log) + { + if (opt_state.revision_ranges->nelts > 1) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments " + "encountered; can't specify -c twice, " + "or both -c and -r")); + } + } + + /* Disallow simultaneous use of both --with-all-revprops and + --with-no-revprops. */ + if (opt_state.all_revprops && opt_state.no_revprops) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-all-revprops and --with-no-revprops " + "are mutually exclusive")); + } + + /* Disallow simultaneous use of both --with-revprop and + --with-no-revprops. */ + if (opt_state.revprop_table && opt_state.no_revprops) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--with-revprop and --with-no-revprops " + "are mutually exclusive")); + } + + /* --trust-* options can only be used with --non-interactive */ + if (!opt_state.non_interactive) + { + if (opt_state.trust_server_cert_unknown_ca + || opt_state.trust_server_cert_cn_mismatch + || opt_state.trust_server_cert_expired + || opt_state.trust_server_cert_not_yet_valid + || opt_state.trust_server_cert_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); + } + + /* 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; + + /* Create a client context object. */ + command_baton.opt_state = &opt_state; + SVN_ERR(svn_client_create_context2(&ctx, NULL, pool)); + command_baton.ctx = ctx; + + /* Only a few commands can accept a revision range; the rest can take at + most one revision number. */ + if (subcommand->cmd_func != svn_cl__null_blame + && subcommand->cmd_func != svn_cl__null_log) + { + if (opt_state.end_revision.kind != svn_opt_revision_unspecified) + { + return svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL); + } + } + + /* -N has a different meaning depending on the command */ + if (!descend) + opt_state.depth = svn_depth_files; + + err = svn_config_get_config(&(ctx->config), + 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_handle_warning2(stderr, err, "svn: "); + svn_error_clear(err); + } + else + return err; + } + + cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, + APR_HASH_KEY_STRING); + + /* Update the options in the config */ + if (opt_state.config_options) + { + svn_error_clear( + svn_cmdline__apply_config_options(ctx->config, + opt_state.config_options, + "svn: ", "--config-option")); + } + + /* 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 (use_notifier) + { + SVN_ERR(svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, + 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_ERR(svn_cmdline_create_auth_baton2( + &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_unknown_ca, + opt_state.trust_server_cert_cn_mismatch, + opt_state.trust_server_cert_expired, + opt_state.trust_server_cert_not_yet_valid, + opt_state.trust_server_cert_other_failure, + cfg_config, + ctx->cancel_func, + ctx->cancel_baton, + pool)); + + ctx->auth_baton = ab; + + /* The new svn behavior is to postpone everything until after the operation + completed */ + ctx->conflict_func = NULL; + ctx->conflict_baton = NULL; + ctx->conflict_func2 = NULL; + ctx->conflict_baton2 = NULL; + + /* 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_wrapf( + err, _("Try 'svnbench 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")); + } + + /* 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)")); + } + + return err; + } + + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnbench", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnbench: "); + } + + svn_pool_destroy(pool); + return exit_code; +} diff --git a/contrib/subversion/subversion/svnbench/util.c b/contrib/subversion/subversion/svnbench/util.c new file mode 100644 index 000000000..2aedde63a --- /dev/null +++ b/contrib/subversion/subversion/svnbench/util.c @@ -0,0 +1,92 @@ +/* + * util.c: Subversion command line client utility functions. Any + * functions that need to be shared across subcommands should be put + * in here. + * + * ==================================================================== + * 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 +#include +#include + +#include "svn_private_config.h" +#include "svn_error.h" +#include "svn_path.h" + +#include "cl.h" + + + +svn_error_t * +svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets, + apr_getopt_t *os, + const apr_array_header_t *known_targets, + svn_client_ctx_t *ctx, + svn_boolean_t keep_last_origpath_on_truepath_collision, + apr_pool_t *pool) +{ + svn_error_t *err = svn_client_args_to_target_array2(targets, + os, + known_targets, + ctx, + keep_last_origpath_on_truepath_collision, + pool); + if (err) + { + if (err->apr_err == SVN_ERR_RESERVED_FILENAME_SPECIFIED) + { + svn_handle_error2(err, stderr, FALSE, "svn: Skipping argument: "); + svn_error_clear(err); + } + else + return svn_error_trace(err); + } + return SVN_NO_ERROR; +} + +svn_error_t * +svn_cl__check_target_is_local_path(const char *target) +{ + if (svn_path_is_url(target)) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is not a local path"), target); + return SVN_NO_ERROR; +} + +const char * +svn_cl__local_style_skip_ancestor(const char *parent_path, + const char *path, + apr_pool_t *pool) +{ + const char *relpath = NULL; + + if (parent_path) + relpath = svn_dirent_skip_ancestor(parent_path, path); + + return svn_dirent_local_style(relpath ? relpath : path, pool); +} + diff --git a/contrib/subversion/subversion/svndumpfilter/svndumpfilter.c b/contrib/subversion/subversion/svndumpfilter/svndumpfilter.c index 0e5bbc690..922482b67 100644 --- a/contrib/subversion/subversion/svndumpfilter/svndumpfilter.c +++ b/contrib/subversion/subversion/svndumpfilter/svndumpfilter.c @@ -43,9 +43,10 @@ #include "svn_mergeinfo.h" #include "svn_version.h" +#include "private/svn_repos_private.h" #include "private/svn_mergeinfo_private.h" #include "private/svn_cmdline_private.h" -#include "private/svn_subr_private.h" +#include "private/svn_sorts_private.h" #ifdef _WIN32 typedef apr_status_t (__stdcall *open_fn_t)(apr_file_t **, apr_pool_t *); @@ -240,7 +241,6 @@ struct revision_baton_t /* 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; @@ -253,7 +253,7 @@ struct revision_baton_t svn_revnum_t rev_actual; /* Pointers to dumpfile data. */ - svn_stringbuf_t *header; + apr_hash_t *original_headers; apr_hash_t *props; }; @@ -278,7 +278,7 @@ struct node_baton_t svn_filesize_t tcl; /* Pointers to dumpfile data. */ - svn_stringbuf_t *header; + svn_repos__dumpfile_headers_t *headers; svn_stringbuf_t *props; /* Expect deltas? */ @@ -287,6 +287,8 @@ struct node_baton_t /* We might need the node path in a parse error message. */ char *node_path; + + apr_pool_t *node_pool; }; @@ -310,6 +312,24 @@ magic_header_record(int version, void *parse_baton, apr_pool_t *pool) } +/* Return a deep copy of a (char * -> char *) hash. */ +static apr_hash_t * +headers_dup(apr_hash_t *headers, + apr_pool_t *pool) +{ + apr_hash_t *new_hash = apr_hash_make(pool); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi)) + { + const char *key = apr_hash_this_key(hi); + const char *val = apr_hash_this_val(hi); + + svn_hash_sets(new_hash, apr_pstrdup(pool, key), apr_pstrdup(pool, val)); + } + return new_hash; +} + /* New revision: set up revision_baton, decide if we skip it. */ static svn_error_t * new_revision_record(void **revision_baton, @@ -318,21 +338,16 @@ new_revision_record(void **revision_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); + rb->original_headers = headers_dup(headers, pool); rev_orig = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER); rb->rev_orig = SVN_STR_TO_REV(rev_orig); @@ -342,28 +357,6 @@ new_revision_record(void **revision_baton, 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; } @@ -375,12 +368,8 @@ new_revision_record(void **revision_baton, 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; @@ -398,7 +387,6 @@ output_revision(struct revision_baton_t *rb) && (! 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)); @@ -407,39 +395,6 @@ output_revision(struct revision_baton_t *rb) "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 @@ -459,10 +414,12 @@ output_revision(struct revision_baton_t *rb) 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))); + SVN_ERR(svn_repos__dump_revision_record(rb->pb->out_stream, + rb->rev_actual, + rb->original_headers, + rb->props, + FALSE /*props_section_always*/, + subpool)); /* Stash the oldest original rev not dropped. */ if (rb->rev_orig > 0 @@ -544,6 +501,7 @@ new_node_record(void **node_baton, *node_baton = apr_palloc(pool, sizeof(struct node_baton_t)); nb = *node_baton; nb->rb = rev_baton; + nb->node_pool = pool; pb = nb->rb->pb; node_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH); @@ -551,9 +509,9 @@ new_node_record(void **node_baton, /* Ensure that paths start with a leading '/'. */ if (node_path[0] != '/') - node_path = apr_pstrcat(pool, "/", node_path, (char *)NULL); + node_path = apr_pstrcat(pool, "/", node_path, SVN_VA_NULL); if (copyfrom_path && copyfrom_path[0] != '/') - copyfrom_path = apr_pstrcat(pool, "/", copyfrom_path, (char *)NULL); + copyfrom_path = apr_pstrcat(pool, "/", copyfrom_path, SVN_VA_NULL); nb->do_skip = skip_path(node_path, pb->prefixes, pb->do_exclude, pb->glob); @@ -615,7 +573,7 @@ new_node_record(void **node_baton, nb->has_text_delta = FALSE; nb->writing_begun = FALSE; nb->tcl = tcl ? svn__atoui64(tcl) : 0; - nb->header = svn_stringbuf_create_empty(pool); + nb->headers = svn_repos__dumpfile_headers_create(pool); nb->props = svn_stringbuf_create_empty(pool); nb->node_path = apr_pstrdup(pool, node_path); @@ -627,23 +585,20 @@ new_node_record(void **node_baton, /* 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)); + svn_repos__dumpfile_header_push( + nb->headers, 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)); + svn_repos__dumpfile_header_push( + nb->headers, 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)); + svn_repos__dumpfile_header_push( + nb->headers, SVN_REPOS_DUMPFILE_NODE_ACTION, action); else return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0, _("Missing Node-action for path '%s'"), @@ -651,8 +606,8 @@ new_node_record(void **node_baton, 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); + const char *key = apr_hash_this_key(hi); + const char *val = apr_hash_this_val(hi); if ((!strcmp(key, SVN_REPOS_DUMPFILE_PROP_DELTA)) && (!strcmp(val, "true"))) @@ -690,18 +645,14 @@ new_node_record(void **node_baton, 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)); + svn_repos__dumpfile_header_pushf( + nb->headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, + "%ld", 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)); + svn_repos__dumpfile_header_push(nb->headers, key, val); } } @@ -709,65 +660,6 @@ new_node_record(void **node_baton, } -/* 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 @@ -794,6 +686,13 @@ adjust_mergeinfo(svn_string_t **final_val, const svn_string_t *initial_val, want filtered. If the oldest rev is r0 then there is nothing to filter. */ + + /* ### This seems to cater only for use cases where the revisions being + processed are not following on from revisions that will already + exist in the destination repository. If the revisions being + processed do follow on, then we might want to keep the mergeinfo + that refers to those older revisions. */ + if (rb->pb->skip_missing_merge_sources && rb->pb->oldest_original_rev > 0) SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &mergeinfo, mergeinfo, @@ -802,8 +701,8 @@ adjust_mergeinfo(svn_string_t **final_val, const svn_string_t *initial_val, 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); + const char *merge_source = apr_hash_this_key(hi); + svn_rangelist_t *rangelist = apr_hash_this_val(hi); struct parse_baton_t *pb = rb->pb; /* Determine whether the merge_source is a part of the prefix. */ @@ -867,7 +766,6 @@ set_revision_property(void *revision_baton, 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)); @@ -886,6 +784,9 @@ set_node_property(void *node_baton, if (nb->do_skip) return SVN_NO_ERROR; + /* Try to detect if a delta-mode property occurs unexpectedly. HAS_PROPS + can be false here only if the parser didn't call remove_node_props(), + so this may indicate a bug rather than bad data. */ if (! (nb->has_props || nb->has_prop_delta)) return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL, _("Delta property block detected, but deltas " @@ -931,14 +832,17 @@ delete_node_property(void *node_baton, const char *name) } +/* The parser calls this method if the node record has a non-delta + * property content section, before any calls to set_node_property(). + * If the node record uses property deltas, this is not called. + */ 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. */ + rather that it has a property content section. */ nb->has_props = TRUE; return SVN_NO_ERROR; @@ -954,7 +858,20 @@ set_fulltext(svn_stream_t **stream, void *node_baton) { nb->has_text = TRUE; if (! nb->writing_begun) - SVN_ERR(output_node(nb)); + { + nb->writing_begun = TRUE; + if (nb->has_props) + { + svn_stringbuf_appendcstr(nb->props, "PROPS-END\n"); + } + SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream, + nb->headers, + nb->has_props ? nb->props : NULL, + nb->has_text, + nb->tcl, + TRUE /*content_length_always*/, + nb->node_pool)); + } *stream = nb->rb->pb->out_stream; } @@ -975,7 +892,20 @@ close_node(void *node_baton) /* If the node was not flushed already to output its text, do it now. */ if (! nb->writing_begun) - SVN_ERR(output_node(nb)); + { + nb->writing_begun = TRUE; + if (nb->has_props) + { + svn_stringbuf_appendcstr(nb->props, "PROPS-END\n"); + } + SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream, + nb->headers, + nb->has_props ? nb->props : NULL, + nb->has_text, + nb->tcl, + TRUE /*content_length_always*/, + nb->node_pool)); + } /* put an end to node. */ SVN_ERR(svn_stream_write(nb->rb->pb->out_stream, "\n\n", &len)); @@ -999,7 +929,7 @@ close_revision(void *revision_baton) /* Filtering vtable */ -svn_repos_parse_fns3_t filtering_vtable = +static svn_repos_parse_fns3_t filtering_vtable = { magic_header_record, uuid_record, @@ -1175,6 +1105,7 @@ 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" + "Subversion repository dump filtering tool.\n" "Type 'svndumpfilter help ' for help on a " "specific subcommand.\n" "Type 'svndumpfilter --version' to see the program version.\n" @@ -1301,12 +1232,11 @@ do_filter(apr_getopt_t *os, hi; hi = apr_hash_next(hi)) { - const svn_revnum_t *revnum = svn__apr_hash_index_key(hi); + const svn_revnum_t *revnum = apr_hash_this_key(hi); APR_ARRAY_PUSH(keys, svn_revnum_t) = *revnum; } - qsort(keys->elts, keys->nelts, - keys->elt_size, svn_sort_compare_revisions); + svn_sort__array(keys, svn_sort_compare_revisions); for (i = 0; i < keys->nelts; i++) { svn_revnum_t this_key; @@ -1345,11 +1275,11 @@ do_filter(apr_getopt_t *os, hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); APR_ARRAY_PUSH(keys, const char *) = path; } - qsort(keys->elts, keys->nelts, keys->elt_size, svn_sort_compare_paths); + svn_sort__array(keys, svn_sort_compare_paths); for (i = 0; i < keys->nelts; i++) { svn_pool_clear(subpool); @@ -1383,12 +1313,16 @@ subcommand_include(apr_getopt_t *os, void *baton, apr_pool_t *pool) /** Main. **/ -int -main(int argc, const char *argv[]) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { 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; @@ -1397,33 +1331,19 @@ main(int argc, const char *argv[]) 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: "); + SVN_ERR(check_lib_versions()); 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: "); + SVN_ERR(svn_fs_initialize(pool)); if (argc <= 1) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Initialize opt_state. */ @@ -1432,9 +1352,7 @@ main(int argc, const char *argv[]) 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: "); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; while (1) @@ -1447,9 +1365,9 @@ main(int argc, const char *argv[]) break; else if (apr_err) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -1490,9 +1408,9 @@ main(int argc, const char *argv[]) break; default: { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } /* close `switch' */ } /* close `while' */ @@ -1501,10 +1419,10 @@ main(int argc, const char *argv[]) --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: "); + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, + NULL, + _("--drop-empty-revs cannot be used with " + "--drop-all-empty-revs")); } /* If the user asked for help, then the rest of the arguments are @@ -1536,9 +1454,9 @@ main(int argc, const char *argv[]) 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; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -1548,18 +1466,16 @@ main(int argc, const char *argv[]) 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_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)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -1579,10 +1495,10 @@ main(int argc, const char *argv[]) /* 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)); + SVN_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); + prefix = apr_pstrcat(pool, "/", prefix, SVN_VA_NULL); APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix; } @@ -1597,12 +1513,12 @@ main(int argc, const char *argv[]) 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_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)); + SVN_ERR(svn_stringbuf_from_file2(&buffer, utf8_targets_file, + pool)); + SVN_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool)); targets = apr_array_append(pool, svn_cstring_split(buffer_utf8->data, "\n\r", @@ -1613,7 +1529,7 @@ main(int argc, const char *argv[]) { const char *prefix = APR_ARRAY_IDX(targets, i, const char *); if (prefix[0] != '/') - prefix = apr_pstrcat(pool, "/", prefix, (char *)NULL); + prefix = apr_pstrcat(pool, "/", prefix, SVN_VA_NULL); APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix; } } @@ -1623,8 +1539,8 @@ main(int argc, const char *argv[]) svn_error_clear(svn_cmdline_fprintf (stderr, pool, _("\nError: no prefixes supplied.\n"))); - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } @@ -1649,15 +1565,15 @@ main(int argc, const char *argv[]) pool); svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); + SVN_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; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } @@ -1674,14 +1590,40 @@ main(int argc, const char *argv[]) _("Try 'svndumpfilter help' for more " "info")); } - return svn_cmdline_handle_exit_error(err, pool, "svndumpfilter: "); + return err; } - 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; + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svndumpfilter: "); } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/contrib/subversion/subversion/svnfsfs/dump-index-cmd.c b/contrib/subversion/subversion/svnfsfs/dump-index-cmd.c new file mode 100644 index 000000000..5598c985c --- /dev/null +++ b/contrib/subversion/subversion/svnfsfs/dump-index-cmd.c @@ -0,0 +1,106 @@ +/* dump-index-cmd.c -- implements the dump-index sub-command. + * + * ==================================================================== + * 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_BYTEFUNC + +#include "svn_dirent_uri.h" +#include "svn_pools.h" +#include "private/svn_fs_fs_private.h" + +#include "svnfsfs.h" + +/* Return the 8 digit hex string for FNVV1, allocated in POOL. + */ +static const char * +fnv1_to_string(apr_uint32_t fnv1, + apr_pool_t *pool) +{ + /* Construct a checksum object containing FNV1. */ + svn_checksum_t checksum = { NULL, svn_checksum_fnv1a_32 }; + apr_uint32_t digest = htonl(fnv1); + checksum.digest = (const unsigned char *)&digest; + + /* Convert the digest to hex. */ + return svn_checksum_to_cstring_display(&checksum, pool); +} + +/* Map svn_fs_fs__p2l_entry_t.type to C string. */ +static const char *item_type_str[] + = {"none ", "frep ", "drep ", "fprop", "dprop", "node ", "chgs ", "rep "}; + +/* Implements svn_fs_fs__dump_index_func_t as printing one table row + * containing the fields of ENTRY to the console. + */ +static svn_error_t * +dump_index_entry(const svn_fs_fs__p2l_entry_t *entry, + void *baton, + apr_pool_t *scratch_pool) +{ + const char *type_str + = entry->type < (sizeof(item_type_str) / sizeof(item_type_str[0])) + ? item_type_str[entry->type] + : "???"; + + printf("%12" APR_UINT64_T_HEX_FMT " %12" APR_UINT64_T_HEX_FMT + " %s %9ld %8" APR_UINT64_T_FMT " %s\n", + (apr_uint64_t)entry->offset, (apr_uint64_t)entry->size, + type_str, entry->item.revision, entry->item.number, + fnv1_to_string(entry->fnv1_checksum, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Read the repository at PATH beginning with revision START_REVISION and + * return the result in *FS. Allocate caches with MEMSIZE bytes total + * capacity. Use POOL for non-cache allocations. + */ +static svn_error_t * +dump_index(const char *path, + svn_revnum_t revision, + apr_pool_t *pool) +{ + svn_fs_t *fs; + + /* Check repository type and open it. */ + SVN_ERR(open_fs(&fs, path, pool)); + + /* Write header line. */ + printf(" Start Length Type Revision Item Checksum\n"); + + /* Dump the whole index contents */ + SVN_ERR(svn_fs_fs__dump_index(fs, revision, dump_index_entry, NULL, + check_cancel, NULL, pool)); + + return SVN_NO_ERROR; +} + +/* This implements `svn_opt_subcommand_t'. */ +svn_error_t * +subcommand__dump_index(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svnfsfs__opt_state *opt_state = baton; + + SVN_ERR(dump_index(opt_state->repository_path, + opt_state->start_revision.value.number, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnfsfs/load-index-cmd.c b/contrib/subversion/subversion/svnfsfs/load-index-cmd.c new file mode 100644 index 000000000..4df86bd09 --- /dev/null +++ b/contrib/subversion/subversion/svnfsfs/load-index-cmd.c @@ -0,0 +1,193 @@ +/* load-index-cmd.c -- implements the dump-index sub-command. + * + * ==================================================================== + * 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_ctype.h" +#include "svn_dirent_uri.h" +#include "svn_io.h" +#include "svn_pools.h" + +#include "private/svn_fs_fs_private.h" +#include "private/svn_sorts_private.h" + +#include "svn_private_config.h" + +#include "svnfsfs.h" + +/* Map svn_fs_fs__p2l_entry_t.type to C string. */ +static const char *item_type_str[] + = {"none", "frep", "drep", "fprop", "dprop", "node", "chgs", "rep"}; + +/* Reverse lookup in ITEM_TYPE_STR: Set *TYPE to the index that contains STR. + * Return an error for invalid strings. */ +static svn_error_t * +str_to_item_type(unsigned *type, + const char *str) +{ + unsigned i; + for (i = 0; i < sizeof(item_type_str) / sizeof(item_type_str[0]); ++i) + if (strcmp(item_type_str[i], str) == 0) + { + *type = i; + return SVN_NO_ERROR; + } + + return svn_error_createf(SVN_ERR_BAD_TOKEN, NULL, + _("Unknown item type '%s'"), str); +} + +/* Parse the string given as const char * at IDX in TOKENS and return its + * value in *VALUE_P. Assume that the string an integer with base RADIX. + * Check for index overflows and non-hex chars. + */ +static svn_error_t * +token_to_i64(apr_int64_t *value_p, + apr_array_header_t *tokens, + int idx, + int radix) +{ + const char *hex; + char *end; + apr_int64_t value; + + /* Tell the user when there is not enough information. */ + SVN_ERR_ASSERT(idx >= 0); + if (tokens->nelts <= idx) + return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, + _("%i columns needed, %i provided"), + idx + 1, tokens->nelts); + + /* hex -> int conversion */ + hex = APR_ARRAY_IDX(tokens, idx, const char *); + value = apr_strtoi64(hex, &end, radix); + + /* Has the whole token be parsed without error? */ + if (errno || *end != '\0') + return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, + _("%s is not a value HEX string"), hex); + + *value_p = value; + return SVN_NO_ERROR; +} + +/* Parse the P2L entry given as space separated values in LINE and return it + * in *ENTRY. Ignore extra columns. Allocate the result in RESULT_POOL and + * use SCRATCH_POOL for temporaries. + */ +static svn_error_t * +parse_index_line(svn_fs_fs__p2l_entry_t **entry, + svn_stringbuf_t *line, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *tokens = svn_cstring_split(line->data, " ", TRUE, + scratch_pool); + svn_fs_fs__p2l_entry_t *result = apr_pcalloc(result_pool, sizeof(*result)); + apr_int64_t value; + + /* Parse the hex columns. */ + SVN_ERR(token_to_i64(&value, tokens, 0, 16)); + result->offset = (apr_off_t)value; + SVN_ERR(token_to_i64(&value, tokens, 1, 16)); + result->size = (apr_off_t)value; + + /* Parse the rightmost colum that we care of. */ + SVN_ERR(token_to_i64(&value, tokens, 4, 10)); + result->item.number = (apr_uint64_t)value; + + /* We now know that there were at least 5 columns. + * Parse the non-hex columns without index check. */ + SVN_ERR(str_to_item_type(&result->type, + APR_ARRAY_IDX(tokens, 2, const char *))); + SVN_ERR(svn_revnum_parse(&result->item.revision, + APR_ARRAY_IDX(tokens, 3, const char *), NULL)); + + *entry = result; + return SVN_NO_ERROR; +} + +/* Parse the space separated P2L index table from INPUT, one entry per line. + * Rewrite the respective index files in PATH. Allocate from POOL. */ +static svn_error_t * +load_index(const char *path, + svn_stream_t *input, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_revnum_t revision = SVN_INVALID_REVNUM; + apr_array_header_t *entries = apr_array_make(pool, 16, sizeof(void*)); + apr_pool_t *iterpool = svn_pool_create(pool); + + /* Check repository type and open it. */ + SVN_ERR(open_fs(&fs, path, pool)); + + while (TRUE) + { + svn_stringbuf_t *line; + svn_fs_fs__p2l_entry_t *entry; + svn_boolean_t eol; + + /* Get the next line from the input and stop if there is none. */ + svn_pool_clear(iterpool); + svn_stream_readline(input, &line, "\n", &eol, iterpool); + if (eol) + break; + + /* Skip header line(s). They contain the sub-string [Ss]tart. */ + if (strstr(line->data, "tart")) + continue; + + /* Ignore empty lines (mostly trailing ones but we don't really care). + */ + svn_stringbuf_strip_whitespace(line); + if (line->len == 0) + continue; + + /* Parse the entry and append it to ENTRIES. */ + SVN_ERR(parse_index_line(&entry, line, pool, iterpool)); + APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = entry; + + /* There should be at least one item that is not empty. + * Get a revision from (probably inside) the respective shard. */ + if ( revision == SVN_INVALID_REVNUM + && entry->item.revision != SVN_INVALID_REVNUM) + revision = entry->item.revision; + } + + /* Rewrite the indexes. */ + SVN_ERR(svn_fs_fs__load_index(fs, revision, entries, iterpool)); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* This implements `svn_opt_subcommand_t'. */ +svn_error_t * +subcommand__load_index(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svnfsfs__opt_state *opt_state = baton; + svn_stream_t *input; + + SVN_ERR(svn_stream_for_stdin(&input, pool)); + SVN_ERR(load_index(opt_state->repository_path, input, pool)); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnfsfs/stats-cmd.c b/contrib/subversion/subversion/svnfsfs/stats-cmd.c new file mode 100644 index 000000000..6e820cb61 --- /dev/null +++ b/contrib/subversion/subversion/svnfsfs/stats-cmd.c @@ -0,0 +1,509 @@ +/* stats-cmd.c -- implements the size stats sub-command. + * + * ==================================================================== + * 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 + +#include "svn_fs.h" +#include "svn_pools.h" +#include "svn_sorts.h" + +#include "private/svn_sorts_private.h" +#include "private/svn_string_private.h" +#include "private/svn_fs_fs_private.h" + +#include "svn_private_config.h" +#include "svnfsfs.h" + +/* Return the string, allocated in RESULT_POOL, describing the value 2**I. + */ +static const char * +print_two_power(int i, + apr_pool_t *result_pool) +{ + /* These are the SI prefixes for base-1000, the binary ones with base-1024 + are too clumsy and require appending B for "byte" to be intelligible, + e.g. "MiB". + + Therefore, we ignore the official standard and revert to the traditional + contextual use were the base-1000 prefixes are understood as base-1024 + when it came to data sizes. + */ + const char *si_prefixes = " kMGTPEZY"; + + int number = (i >= 0) ? (1 << (i % 10)) : 0; + int thousands = (i >= 0) ? (i / 10) : 0; + + char si_prefix = (thousands < strlen(si_prefixes)) + ? si_prefixes[thousands] + : '?'; + + if (si_prefix == ' ') + return apr_psprintf(result_pool, "%d", number); + + return apr_psprintf(result_pool, "%d%c", number, si_prefix); +} + +/* Print statistics for the given group of representations to console. + * Use POOL for allocations. + */ +static void +print_rep_stats(svn_fs_fs__representation_stats_t *stats, + apr_pool_t *pool) +{ + printf(_("%20s bytes in %12s reps\n" + "%20s bytes in %12s shared reps\n" + "%20s bytes expanded size\n" + "%20s bytes expanded shared size\n" + "%20s bytes with rep-sharing off\n" + "%20s shared references\n"), + svn__ui64toa_sep(stats->total.packed_size, ',', pool), + svn__ui64toa_sep(stats->total.count, ',', pool), + svn__ui64toa_sep(stats->shared.packed_size, ',', pool), + svn__ui64toa_sep(stats->shared.count, ',', pool), + svn__ui64toa_sep(stats->total.expanded_size, ',', pool), + svn__ui64toa_sep(stats->shared.expanded_size, ',', pool), + svn__ui64toa_sep(stats->expanded_size, ',', pool), + svn__ui64toa_sep(stats->references - stats->total.count, ',', pool)); +} + +/* Print the (used) contents of CHANGES. Use POOL for allocations. + */ +static void +print_largest_reps(svn_fs_fs__largest_changes_t *changes, + apr_pool_t *pool) +{ + apr_size_t i; + for (i = 0; i < changes->count && changes->changes[i]->size; ++i) + printf(_("%12s r%-8ld %s\n"), + svn__ui64toa_sep(changes->changes[i]->size, ',', pool), + changes->changes[i]->revision, + changes->changes[i]->path->data); +} + +/* Print the non-zero section of HISTOGRAM to console. + * Use POOL for allocations. + */ +static void +print_histogram(svn_fs_fs__histogram_t *histogram, + apr_pool_t *pool) +{ + int first = 0; + int last = 63; + int i; + + /* identify non-zero range */ + while (last > 0 && histogram->lines[last].count == 0) + --last; + + while (first <= last && histogram->lines[first].count == 0) + ++first; + + /* display histogram lines */ + for (i = last; i >= first; --i) + printf(_(" %4s .. < %-4s %19s (%2d%%) bytes in %12s (%2d%%) items\n"), + print_two_power(i-1, pool), print_two_power(i, pool), + svn__ui64toa_sep(histogram->lines[i].sum, ',', pool), + (int)(histogram->lines[i].sum * 100 / histogram->total.sum), + svn__ui64toa_sep(histogram->lines[i].count, ',', pool), + (int)(histogram->lines[i].count * 100 / histogram->total.count)); +} + +/* COMPARISON_FUNC for svn_sort__hash. + * Sort extension_info_t values by total count in descending order. + */ +static int +compare_count(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; + apr_int64_t diff = lhs->node_histogram.total.count + - rhs->node_histogram.total.count; + + return diff > 0 ? -1 : (diff < 0 ? 1 : 0); +} + +/* COMPARISON_FUNC for svn_sort__hash. + * Sort extension_info_t values by total uncompressed size in descending order. + */ +static int +compare_node_size(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; + apr_int64_t diff = lhs->node_histogram.total.sum + - rhs->node_histogram.total.sum; + + return diff > 0 ? -1 : (diff < 0 ? 1 : 0); +} + +/* COMPARISON_FUNC for svn_sort__hash. + * Sort extension_info_t values by total prep count in descending order. + */ +static int +compare_rep_size(const svn_sort__item_t *a, + const svn_sort__item_t *b) +{ + const svn_fs_fs__extension_info_t *lhs = a->value; + const svn_fs_fs__extension_info_t *rhs = b->value; + apr_int64_t diff = lhs->rep_histogram.total.sum + - rhs->rep_histogram.total.sum; + + return diff > 0 ? -1 : (diff < 0 ? 1 : 0); +} + +/* Return an array of extension_info_t* for the (up to) 16 most prominent + * extensions in STATS according to the sort criterion COMPARISON_FUNC. + * Allocate results in POOL. + */ +static apr_array_header_t * +get_by_extensions(svn_fs_fs__stats_t *stats, + int (*comparison_func)(const svn_sort__item_t *, + const svn_sort__item_t *), + apr_pool_t *pool) +{ + /* sort all data by extension */ + apr_array_header_t *sorted + = svn_sort__hash(stats->by_extension, comparison_func, pool); + + /* select the top (first) 16 entries */ + int count = MIN(sorted->nelts, 16); + apr_array_header_t *result + = apr_array_make(pool, count, sizeof(svn_fs_fs__extension_info_t*)); + int i; + + for (i = 0; i < count; ++i) + APR_ARRAY_PUSH(result, svn_fs_fs__extension_info_t*) + = APR_ARRAY_IDX(sorted, i, svn_sort__item_t).value; + + return result; +} + +/* Add all extension_info_t* entries of TO_ADD not already in TARGET to + * TARGET. + */ +static void +merge_by_extension(apr_array_header_t *target, + apr_array_header_t *to_add) +{ + int i, k, count; + + count = target->nelts; + for (i = 0; i < to_add->nelts; ++i) + { + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(to_add, i, svn_fs_fs__extension_info_t *); + for (k = 0; k < count; ++k) + if (info == APR_ARRAY_IDX(target, k, svn_fs_fs__extension_info_t *)) + break; + + if (k == count) + APR_ARRAY_PUSH(target, svn_fs_fs__extension_info_t*) = info; + } +} + +/* Print the (up to) 16 extensions in STATS with the most changes. + * Use POOL for allocations. + */ +static void +print_extensions_by_changes(svn_fs_fs__stats_t *stats, + apr_pool_t *pool) +{ + apr_array_header_t *data = get_by_extensions(stats, compare_count, pool); + apr_int64_t sum = 0; + int i; + + for (i = 0; i < data->nelts; ++i) + { + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); + + /* If there are elements, then their count cannot be 0. */ + assert(stats->file_histogram.total.count); + + sum += info->node_histogram.total.count; + printf(_("%11s %20s (%2d%%) representations\n"), + info->extension, + svn__ui64toa_sep(info->node_histogram.total.count, ',', pool), + (int)(info->node_histogram.total.count * 100 / + stats->file_histogram.total.count)); + } + + if (stats->file_histogram.total.count) + { + printf(_("%11s %20s (%2d%%) representations\n"), + "(others)", + svn__ui64toa_sep(stats->file_histogram.total.count - sum, ',', + pool), + (int)((stats->file_histogram.total.count - sum) * 100 / + stats->file_histogram.total.count)); + } +} + +/* Calculate a percentage, handling edge cases. */ +static int +get_percentage(apr_uint64_t part, + apr_uint64_t total) +{ + /* This include total == 0. */ + if (part >= total) + return 100; + + /* Standard case. */ + return (int)(part * 100.0 / total); +} + +/* Print the (up to) 16 extensions in STATS with the largest total size of + * changed file content. Use POOL for allocations. + */ +static void +print_extensions_by_nodes(svn_fs_fs__stats_t *stats, + apr_pool_t *pool) +{ + apr_array_header_t *data = get_by_extensions(stats, compare_node_size, pool); + apr_int64_t sum = 0; + int i; + + for (i = 0; i < data->nelts; ++i) + { + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); + sum += info->node_histogram.total.sum; + printf(_("%11s %20s (%2d%%) bytes\n"), + info->extension, + svn__ui64toa_sep(info->node_histogram.total.sum, ',', pool), + get_percentage(info->node_histogram.total.sum, + stats->file_histogram.total.sum)); + } + + if (stats->file_histogram.total.sum > sum) + { + /* Total sum can't be zero here. */ + printf(_("%11s %20s (%2d%%) bytes\n"), + "(others)", + svn__ui64toa_sep(stats->file_histogram.total.sum - sum, ',', + pool), + get_percentage(stats->file_histogram.total.sum - sum, + stats->file_histogram.total.sum)); + } +} + +/* Print the (up to) 16 extensions in STATS with the largest total size of + * changed file content. Use POOL for allocations. + */ +static void +print_extensions_by_reps(svn_fs_fs__stats_t *stats, + apr_pool_t *pool) +{ + apr_array_header_t *data = get_by_extensions(stats, compare_rep_size, pool); + apr_int64_t sum = 0; + int i; + + for (i = 0; i < data->nelts; ++i) + { + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); + sum += info->rep_histogram.total.sum; + printf(_("%11s %20s (%2d%%) bytes\n"), + info->extension, + svn__ui64toa_sep(info->rep_histogram.total.sum, ',', pool), + get_percentage(info->rep_histogram.total.sum, + stats->rep_size_histogram.total.sum)); + } + + if (stats->rep_size_histogram.total.sum > sum) + { + /* Total sum can't be zero here. */ + printf(_("%11s %20s (%2d%%) bytes\n"), + "(others)", + svn__ui64toa_sep(stats->rep_size_histogram.total.sum - sum, ',', + pool), + get_percentage(stats->rep_size_histogram.total.sum - sum, + stats->rep_size_histogram.total.sum)); + } +} + +/* Print per-extension histograms for the most frequent extensions in STATS. + * Use POOL for allocations. */ +static void +print_histograms_by_extension(svn_fs_fs__stats_t *stats, + apr_pool_t *pool) +{ + apr_array_header_t *data = get_by_extensions(stats, compare_count, pool); + int i; + + merge_by_extension(data, get_by_extensions(stats, compare_node_size, pool)); + merge_by_extension(data, get_by_extensions(stats, compare_rep_size, pool)); + + for (i = 0; i < data->nelts; ++i) + { + svn_fs_fs__extension_info_t *info + = APR_ARRAY_IDX(data, i, svn_fs_fs__extension_info_t *); + printf("\nHistogram of '%s' file sizes:\n", info->extension); + print_histogram(&info->node_histogram, pool); + printf("\nHistogram of '%s' file representation sizes:\n", + info->extension); + print_histogram(&info->rep_histogram, pool); + } +} + +/* Print the contents of STATS to the console. + * Use POOL for allocations. + */ +static void +print_stats(svn_fs_fs__stats_t *stats, + apr_pool_t *pool) +{ + /* print results */ + printf("\nGlobal statistics:\n"); + printf(_("%20s bytes in %12s revisions\n" + "%20s bytes in %12s changes\n" + "%20s bytes in %12s node revision records\n" + "%20s bytes in %12s representations\n" + "%20s bytes expanded representation size\n" + "%20s bytes with rep-sharing off\n"), + svn__ui64toa_sep(stats->total_size, ',', pool), + svn__ui64toa_sep(stats->revision_count, ',', pool), + svn__ui64toa_sep(stats->change_len, ',', pool), + svn__ui64toa_sep(stats->change_count, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.expanded_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.expanded_size, ',', pool)); + + printf("\nNoderev statistics:\n"); + printf(_("%20s bytes in %12s nodes total\n" + "%20s bytes in %12s directory noderevs\n" + "%20s bytes in %12s file noderevs\n"), + svn__ui64toa_sep(stats->total_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->total_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->dir_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->dir_node_stats.count, ',', pool), + svn__ui64toa_sep(stats->file_node_stats.size, ',', pool), + svn__ui64toa_sep(stats->file_node_stats.count, ',', pool)); + + printf("\nRepresentation statistics:\n"); + printf(_("%20s bytes in %12s representations total\n" + "%20s bytes in %12s directory representations\n" + "%20s bytes in %12s file representations\n" + "%20s bytes in %12s representations of added file nodes\n" + "%20s bytes in %12s directory property representations\n" + "%20s bytes in %12s file property representations\n" + "%20s bytes in header & footer overhead\n"), + svn__ui64toa_sep(stats->total_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->total_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->dir_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->dir_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->file_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->file_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->added_rep_size_histogram.total.sum, ',', + pool), + svn__ui64toa_sep(stats->added_rep_size_histogram.total.count, ',', + pool), + svn__ui64toa_sep(stats->dir_prop_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->dir_prop_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->file_prop_rep_stats.total.packed_size, ',', + pool), + svn__ui64toa_sep(stats->file_prop_rep_stats.total.count, ',', pool), + svn__ui64toa_sep(stats->total_rep_stats.total.overhead_size, ',', + pool)); + + printf("\nDirectory representation statistics:\n"); + print_rep_stats(&stats->dir_rep_stats, pool); + printf("\nFile representation statistics:\n"); + print_rep_stats(&stats->file_rep_stats, pool); + printf("\nDirectory property representation statistics:\n"); + print_rep_stats(&stats->dir_prop_rep_stats, pool); + printf("\nFile property representation statistics:\n"); + print_rep_stats(&stats->file_prop_rep_stats, pool); + + printf("\nLargest representations:\n"); + print_largest_reps(stats->largest_changes, pool); + printf("\nExtensions by number of representations:\n"); + print_extensions_by_changes(stats, pool); + printf("\nExtensions by size of changed files:\n"); + print_extensions_by_nodes(stats, pool); + printf("\nExtensions by size of representations:\n"); + print_extensions_by_reps(stats, pool); + + printf("\nHistogram of expanded node sizes:\n"); + print_histogram(&stats->node_size_histogram, pool); + printf("\nHistogram of representation sizes:\n"); + print_histogram(&stats->rep_size_histogram, pool); + printf("\nHistogram of file sizes:\n"); + print_histogram(&stats->file_histogram, pool); + printf("\nHistogram of file representation sizes:\n"); + print_histogram(&stats->file_rep_histogram, pool); + printf("\nHistogram of file property sizes:\n"); + print_histogram(&stats->file_prop_histogram, pool); + printf("\nHistogram of file property representation sizes:\n"); + print_histogram(&stats->file_prop_rep_histogram, pool); + printf("\nHistogram of directory sizes:\n"); + print_histogram(&stats->dir_histogram, pool); + printf("\nHistogram of directory representation sizes:\n"); + print_histogram(&stats->dir_rep_histogram, pool); + printf("\nHistogram of directory property sizes:\n"); + print_histogram(&stats->dir_prop_histogram, pool); + printf("\nHistogram of directory property representation sizes:\n"); + print_histogram(&stats->dir_prop_rep_histogram, pool); + + print_histograms_by_extension(stats, pool); +} + +/* Our progress function simply prints the REVISION number and makes it + * appear immediately. + */ +static void +print_progress(svn_revnum_t revision, + void *baton, + apr_pool_t *pool) +{ + printf("%8ld", revision); + fflush(stdout); +} + +/* This implements `svn_opt_subcommand_t'. */ +svn_error_t * +subcommand__stats(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svnfsfs__opt_state *opt_state = baton; + svn_fs_fs__stats_t *stats; + svn_fs_t *fs; + + printf("Reading revisions\n"); + SVN_ERR(open_fs(&fs, opt_state->repository_path, pool)); + SVN_ERR(svn_fs_fs__get_stats(&stats, fs, print_progress, NULL, + check_cancel, NULL, pool, pool)); + + print_stats(stats, pool); + + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnfsfs/svnfsfs.c b/contrib/subversion/subversion/svnfsfs/svnfsfs.c new file mode 100644 index 000000000..f8b1e4b19 --- /dev/null +++ b/contrib/subversion/subversion/svnfsfs/svnfsfs.c @@ -0,0 +1,541 @@ +/* + * svnfsfs.c: FSFS repository manipulation 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 + +#include "svn_pools.h" +#include "svn_cmdline.h" +#include "svn_opt.h" +#include "svn_utf.h" +#include "svn_path.h" +#include "svn_dirent_uri.h" +#include "svn_repos.h" +#include "svn_cache_config.h" +#include "svn_version.h" + +#include "private/svn_cmdline_private.h" + +#include "svn_private_config.h" + +#include "svnfsfs.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 +} + + +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_warning2(stderr, err, "svnfsfs: "); +} + + +/* 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. **/ + +enum svnfsfs__cmdline_options_t + { + svnfsfs__version = SVN_OPT_FIRST_LONGOPT_ID + }; + +/* 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", svnfsfs__version, 0, + N_("show program version information")}, + + {"quiet", 'q', 0, + N_("no progress (only errors to stderr)")}, + + {"revision", 'r', 1, + N_("specify revision number ARG (or X:Y range)")}, + + {"memory-cache-size", 'M', 1, + N_("size of the extra in-memory cache in MB used to\n" + " minimize redundant operations. Default: 16.")}, + + {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[] = +{ + {"help", subcommand__help, {"?", "h"}, N_ + ("usage: svnfsfs help [SUBCOMMAND...]\n\n" + "Describe the usage of this program or its subcommands.\n"), + {0} }, + + {"dump-index", subcommand__dump_index, {0}, N_ + ("usage: svnfsfs dump-index REPOS_PATH -r REV\n\n" + "Dump the index contents for the revision / pack file containing revision REV\n" + "to console. This is only available for FSFS format 7 (SVN 1.9+) repositories.\n" + "The table produced contains a header in the first line followed by one line\n" + "per index entry, ordered by location in the revision / pack file. Columns:\n\n" + " * Byte offset (hex) at which the item starts\n" + " * Length (hex) of the item in bytes\n" + " * Item type (string) is one of the following:\n\n" + " none ... Unused section. File contents shall be NULs.\n" + " frep ... File representation.\n" + " drep ... Directory representation.\n" + " fprop .. File property.\n" + " dprop .. Directory property.\n" + " node ... Node revision.\n" + " chgs ... Changed paths list.\n" + " rep .... Representation of unknown type. Should not be used.\n" + " ??? .... Invalid. Index data is corrupt.\n\n" + " The distinction between frep, drep, fprop and dprop is a mere internal\n" + " classification used for various optimizations and does not affect the\n" + " operational correctness.\n\n" + " * Revision that the item belongs to (decimal)\n" + " * Item number (decimal) within that revision\n" + " * Modified FNV1a checksum (8 hex digits)\n"), + {'r', 'M'} }, + + {"load-index", subcommand__load_index, {0}, N_ + ("usage: svnfsfs load-index REPOS_PATH\n\n" + "Read index contents from console. The format is the same as produced by the\n" + "dump-index command, except that checksum as well as header are optional and will\n" + "be ignored. The data must cover the full revision / pack file; the revision\n" + "number is automatically extracted from input stream. No ordering is required.\n"), + {'M'} }, + + {"stats", subcommand__stats, {0}, N_ + ("usage: svnfsfs stats REPOS_PATH\n\n" + "Write object size statistics to console.\n"), + {'M'} }, + + { NULL, NULL, {0}, NULL, {0} } +}; + + +svn_error_t * +open_fs(svn_fs_t **fs, + const char *path, + apr_pool_t *pool) +{ + const char *fs_type; + + /* Verify that we can handle the repository type. */ + path = svn_dirent_join(path, "db", pool); + SVN_ERR(svn_fs_type(&fs_type, path, pool)); + if (strcmp(fs_type, SVN_FS_TYPE_FSFS)) + return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_TYPE, NULL, + _("%s repositories are not supported"), + fs_type); + + /* Now open it. */ + SVN_ERR(svn_fs_open2(fs, path, NULL, pool, pool)); + svn_fs_set_warning_func(*fs, warning_func, NULL); + + return SVN_NO_ERROR; +} + +/* This implements `svn_opt_subcommand_t'. */ +svn_error_t * +subcommand__help(apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + svnfsfs__opt_state *opt_state = baton; + const char *header = + _("general usage: svnfsfs SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n" + "Subversion FSFS repository manipulation tool.\n" + "Type 'svnfsfs help ' for help on a specific subcommand.\n" + "Type 'svnfsfs --version' to see the program version.\n" + "\n" + "Available subcommands:\n"); + + SVN_ERR(svn_opt_print_help4(os, "svnfsfs", + 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; +} + + +/** Main. **/ + +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, 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; + svnfsfs__opt_state opt_state = { 0 }; + apr_getopt_t *os; + int opt_id; + apr_array_header_t *received_opts; + int i; + + received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); + + /* Check library versions */ + SVN_ERR(check_lib_versions()); + + /* Initialize the FS library. */ + SVN_ERR(svn_fs_initialize(pool)); + + if (argc <= 1) + { + SVN_ERR(subcommand__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* 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_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_ERR(subcommand__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + + /* 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) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments encountered; " + "try '-r N:M' instead of '-r N -r M'")); + } + if (svn_opt_parse_revision(&(opt_state.start_revision), + &(opt_state.end_revision), + opt_arg, pool) != 0) + { + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Syntax error in revision argument '%s'"), + utf8_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 svnfsfs__version: + opt_state.version = TRUE; + break; + default: + { + SVN_ERR(subcommand__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } /* 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}, "", + {svnfsfs__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_ERR(subcommand__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + 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_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_ERR(subcommand__help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + } + + /* Every subcommand except `help' requires a second argument -- the + repository path. Parse it out here and store it in opt_state. */ + if (!(subcommand->cmd_func == subcommand__help)) + { + const char *repos_path = NULL; + + if (os->ind >= os->argc) + { + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Repository argument required")); + } + + SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, os->argv[os->ind++], pool)); + + if (svn_path_is_url(repos_path)) + { + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("'%s' is a URL when it should be a " + "local path"), repos_path); + } + + 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_ERR(subcommand__help(NULL, NULL, pool)); + else + svn_error_clear(svn_cmdline_fprintf(stderr, pool + , _("Subcommand '%s' doesn't accept option '%s'\n" + "Type 'svnfsfs help %s' for usage.\n"), + subcommand->name, optstr, subcommand->name)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } + } + + /* 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 svnfsfs. + * 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 'svnfsfs help' for more info")); + } + return err; + } + + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnfsfs", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnfsfs: "); + } + + svn_pool_destroy(pool); + return exit_code; +} diff --git a/contrib/subversion/subversion/svnfsfs/svnfsfs.h b/contrib/subversion/subversion/svnfsfs/svnfsfs.h new file mode 100644 index 000000000..132b2bc03 --- /dev/null +++ b/contrib/subversion/subversion/svnfsfs/svnfsfs.h @@ -0,0 +1,73 @@ +/* + * svnfsfs.h: shared stuff in the command line program + * + * ==================================================================== + * 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 SVNFSFS_H +#define SVNFSFS_H + +/*** Includes. ***/ + +#include "svn_opt.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*** Command dispatch. ***/ + +/* Baton for passing option/argument state to a subcommand function. */ +typedef struct svnfsfs__opt_state +{ + const char *repository_path; + svn_opt_revision_t start_revision, end_revision; /* -r X[:Y] */ + svn_boolean_t help; /* --help or -? */ + svn_boolean_t version; /* --version */ + svn_boolean_t quiet; /* --quiet */ + apr_uint64_t memory_cache_size; /* --memory-cache-size M */ +} svnfsfs__opt_state; + +/* Declare all the command procedures */ +svn_opt_subcommand_t + subcommand__help, + subcommand__dump_index, + subcommand__load_index, + subcommand__stats; + + +/* Check that the filesystem at PATH is an FSFS repository and then open it. + * Return the filesystem in *FS, allocated in POOL. */ +svn_error_t * +open_fs(svn_fs_t **fs, + const char *path, + apr_pool_t *pool); + +/* Our cancellation callback. */ +svn_error_t * +check_cancel(void *baton); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVNFSFS_H */ diff --git a/contrib/subversion/subversion/svnlook/svnlook.c b/contrib/subversion/subversion/svnlook/svnlook.c index 8162e414e..53ff2977d 100644 --- a/contrib/subversion/subversion/svnlook/svnlook.c +++ b/contrib/subversion/subversion/svnlook/svnlook.c @@ -54,11 +54,11 @@ #include "svn_version.h" #include "svn_xml.h" -#include "private/svn_diff_private.h" #include "private/svn_cmdline_private.h" +#include "private/svn_diff_private.h" #include "private/svn_fspath.h" #include "private/svn_io_private.h" -#include "private/svn_subr_private.h" +#include "private/svn_sorts_private.h" #include "svn_private_config.h" @@ -102,7 +102,8 @@ enum svnlook__ignore_properties, svnlook__properties_only, svnlook__diff_cmd, - svnlook__show_inherited_props + svnlook__show_inherited_props, + svnlook__no_newline }; /* @@ -143,6 +144,9 @@ static const apr_getopt_option_t options_table[] = {"properties-only", svnlook__properties_only, 0, N_("show only properties during the operation")}, + {"no-newline", svnlook__no_newline, 0, + N_("do not output the trailing newline")}, + {"non-recursive", 'N', 0, N_("operate on single directory only")}, @@ -187,6 +191,8 @@ static const apr_getopt_option_t options_table[] = " " " --ignore-eol-style: Ignore changes in EOL style\n" " " + " -U ARG, --context ARG: Show ARG lines of context\n" + " " " -p, --show-c-function: Show C function name")}, {"quiet", 'q', 0, @@ -300,7 +306,7 @@ static const svn_opt_subcommand_desc2_t cmd_table[] = {"youngest", subcommand_youngest, {0}, N_("usage: svnlook youngest REPOS_PATH\n\n" "Print the youngest revision number.\n"), - {0} }, + {svnlook__no_newline} }, { NULL, NULL, {0}, NULL, {0} } }; @@ -333,6 +339,7 @@ struct svnlook_opt_state svn_boolean_t properties_only; /* --properties-only */ const char *diff_cmd; /* --diff-cmd */ svn_boolean_t show_inherited_props; /* --show-inherited-props */ + svn_boolean_t no_newline; /* --no-newline */ }; @@ -667,7 +674,8 @@ dump_contents(svn_stream_t *stream, non-textual data -- in this case, the *IS_BINARY flag is set and no temporary files are created. - Use POOL for all that allocation goodness. */ + TMPFILE1 and TMPFILE2 will be removed when RESULT_POOL is destroyed. + */ static svn_error_t * prepare_tmpfiles(const char **tmpfile1, const char **tmpfile2, @@ -676,8 +684,8 @@ prepare_tmpfiles(const char **tmpfile1, const char *path1, svn_fs_root_t *root2, const char *path2, - const char *tmpdir, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_string_t *mimetype; svn_stream_t *stream; @@ -694,7 +702,7 @@ prepare_tmpfiles(const char **tmpfile1, if (root1) { SVN_ERR(svn_fs_node_prop(&mimetype, root1, path1, - SVN_PROP_MIME_TYPE, pool)); + SVN_PROP_MIME_TYPE, scratch_pool)); if (mimetype && svn_mime_type_is_binary(mimetype->data)) { *is_binary = TRUE; @@ -704,7 +712,7 @@ prepare_tmpfiles(const char **tmpfile1, if (root2) { SVN_ERR(svn_fs_node_prop(&mimetype, root2, path2, - SVN_PROP_MIME_TYPE, pool)); + SVN_PROP_MIME_TYPE, scratch_pool)); if (mimetype && svn_mime_type_is_binary(mimetype->data)) { *is_binary = TRUE; @@ -714,17 +722,15 @@ prepare_tmpfiles(const char **tmpfile1, /* Now, prepare the two temporary files, each of which will either be empty, or will have real contents. */ - SVN_ERR(svn_stream_open_unique(&stream, tmpfile1, - tmpdir, - svn_io_file_del_none, - pool, pool)); - SVN_ERR(dump_contents(stream, root1, path1, pool)); - - SVN_ERR(svn_stream_open_unique(&stream, tmpfile2, - tmpdir, - svn_io_file_del_none, - pool, pool)); - SVN_ERR(dump_contents(stream, root2, path2, pool)); + SVN_ERR(svn_stream_open_unique(&stream, tmpfile1, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(dump_contents(stream, root1, path1, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&stream, tmpfile2, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); + SVN_ERR(dump_contents(stream, root2, path2, scratch_pool)); return SVN_NO_ERROR; } @@ -804,7 +810,9 @@ display_prop_diffs(svn_stream_t *outstream, SVN_ERR(svn_diff__display_prop_diffs( outstream, encoding, propchanges, original_props, - FALSE /* pretty_print_mergeinfo */, pool)); + FALSE /* pretty_print_mergeinfo */, + -1 /* context_size */, + check_cancel, NULL, pool)); return SVN_NO_ERROR; } @@ -821,7 +829,6 @@ print_diff_tree(svn_stream_t *out_stream, const char *path /* UTF-8! */, const char *base_path /* UTF-8! */, const svnlook_ctxt_t *c, - const char *tmpdir, apr_pool_t *pool) { const char *orig_path = NULL, *new_path = NULL; @@ -830,7 +837,7 @@ print_diff_tree(svn_stream_t *out_stream, svn_boolean_t is_copy = FALSE; svn_boolean_t binary = FALSE; svn_boolean_t diff_header_printed = FALSE; - apr_pool_t *subpool; + apr_pool_t *iterpool; svn_stringbuf_t *header; SVN_ERR(check_cancel(NULL)); @@ -891,7 +898,7 @@ print_diff_tree(svn_stream_t *out_stream, do_diff = TRUE; SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, base_root, base_path, root, path, - tmpdir, pool)); + pool, pool)); } else if (c->diff_copy_from && node->action == 'A' && is_copy) { @@ -900,7 +907,7 @@ print_diff_tree(svn_stream_t *out_stream, do_diff = TRUE; SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, base_root, base_path, root, path, - tmpdir, pool)); + pool, pool)); } } else if (! c->no_diff_added && node->action == 'A') @@ -909,14 +916,14 @@ print_diff_tree(svn_stream_t *out_stream, orig_empty = TRUE; SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, NULL, base_path, root, path, - tmpdir, pool)); + pool, pool)); } else if (! c->no_diff_deleted && node->action == 'D') { do_diff = TRUE; SVN_ERR(prepare_tmpfiles(&orig_path, &new_path, &binary, base_root, base_path, NULL, path, - tmpdir, pool)); + pool, pool)); } /* The header for the copy case has already been created, and we don't @@ -1058,11 +1065,12 @@ print_diff_tree(svn_stream_t *out_stream, SVN_ERR(generate_label(&orig_label, base_root, base_path, pool)); SVN_ERR(generate_label(&new_label, root, path, pool)); - SVN_ERR(svn_diff_file_output_unified3 - (out_stream, diff, orig_path, new_path, + SVN_ERR(svn_diff_file_output_unified4( + out_stream, diff, orig_path, new_path, orig_label, new_label, svn_cmdline_output_encoding(pool), NULL, - opts->show_c_function, pool)); + opts->show_c_function, opts->context_size, + check_cancel, NULL, pool)); SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool, "\n")); diff_header_printed = TRUE; @@ -1081,12 +1089,6 @@ print_diff_tree(svn_stream_t *out_stream, } } - /* Make sure we delete any temporary files. */ - if (orig_path) - SVN_ERR(svn_io_remove_file2(orig_path, FALSE, pool)); - if (new_path) - SVN_ERR(svn_io_remove_file2(new_path, FALSE, pool)); - /*** Now handle property diffs ***/ if ((node->prop_mod) && (node->action != 'D') && (! c->ignore_properties)) { @@ -1133,26 +1135,21 @@ print_diff_tree(svn_stream_t *out_stream, } /* Return here if the node has no children. */ - node = node->child; - if (! node) + if (! node->child) return SVN_NO_ERROR; /* Recursively handle the node's children. */ - subpool = svn_pool_create(pool); - SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, - svn_dirent_join(path, node->name, subpool), - svn_dirent_join(base_path, node->name, subpool), - c, tmpdir, subpool)); - while (node->sibling) + iterpool = svn_pool_create(pool); + for (node = node->child; node; node = node->sibling) { - svn_pool_clear(subpool); - node = node->sibling; + svn_pool_clear(iterpool); + SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node, - svn_dirent_join(path, node->name, subpool), - svn_dirent_join(base_path, node->name, subpool), - c, tmpdir, subpool)); + svn_dirent_join(path, node->name, iterpool), + svn_dirent_join(base_path, node->name, iterpool), + c, iterpool)); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -1515,12 +1512,10 @@ do_diff(svnlook_ctxt_t *c, apr_pool_t *pool) SVN_ERR(generate_delta_tree(&tree, c->repos, root, base_rev_id, pool)); if (tree) { - const char *tmpdir; svn_stream_t *out_stream; const char *encoding = svn_cmdline_output_encoding(pool); SVN_ERR(svn_fs_revision_root(&base_root, c->fs, base_rev_id, pool)); - SVN_ERR(svn_io_temp_dir(&tmpdir, pool)); /* This fflush() might seem odd, but it was added to deal with this bug report: @@ -1549,7 +1544,7 @@ do_diff(svnlook_ctxt_t *c, apr_pool_t *pool) SVN_ERR(svn_stream_for_stdout(&out_stream, pool)); SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, tree, - "", "", c, tmpdir, pool)); + "", "", c, pool)); } return SVN_NO_ERROR; } @@ -1599,7 +1594,7 @@ print_history(void *baton, { phb->count++; if (phb->count >= phb->limit) - /* Not L10N'd, since this error is supressed by the caller. */ + /* Not L10N'd, since this error is suppressed by the caller. */ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, _("History item limit reached")); } @@ -1696,9 +1691,14 @@ do_pget(svnlook_ctxt_t *c, if (path == NULL) { /* We're operating on a revprop (e.g. c->is_revision). */ - err_msg = apr_psprintf(pool, - _("Property '%s' not found on revision %ld"), - propname, c->rev_id); + if (SVN_IS_VALID_REVNUM(c->rev_id)) + err_msg = apr_psprintf(pool, + _("Property '%s' not found on revision %ld"), + propname, c->rev_id); + else + err_msg = apr_psprintf(pool, + _("Property '%s' not found on transaction %s"), + propname, c->txn_name); } else { @@ -1761,8 +1761,7 @@ do_pget(svnlook_ctxt_t *c, else { svn_string_t *propval = - svn__apr_hash_index_val(apr_hash_first(pool, - elt->prop_hash)); + apr_hash_this_val(apr_hash_first(pool, elt->prop_hash)); SVN_ERR(svn_stream_printf( stdout_stream, pool, "%s - ", @@ -1877,7 +1876,8 @@ do_plist(svnlook_ctxt_t *c, svn_xml_make_header2(&sb, "UTF-8", pool); /* "" */ - svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "properties", NULL); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "properties", + SVN_VA_NULL); } if (inherited_props) @@ -1896,7 +1896,7 @@ do_plist(svnlook_ctxt_t *c, svn_xml_make_open_tag( &sb, pool, svn_xml_normal, "target", "path", svn_fspath__canonicalize(elt->path_or_url, pool), - NULL); + SVN_VA_NULL); SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, elt->prop_hash, !verbose, TRUE, pool)); @@ -1923,19 +1923,19 @@ do_plist(svnlook_ctxt_t *c, char *revstr = apr_psprintf(pool, "%ld", c->rev_id); svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", - "rev", revstr, NULL); + "rev", revstr, SVN_VA_NULL); } else { svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", - "txn", c->txn_name, NULL); + "txn", c->txn_name, SVN_VA_NULL); } } else { /* "" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target", - "path", path, NULL); + "path", path, SVN_VA_NULL); } } @@ -1944,8 +1944,8 @@ do_plist(svnlook_ctxt_t *c, for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { - const char *pname = svn__apr_hash_index_key(hi); - svn_string_t *propval = svn__apr_hash_index_val(hi); + const char *pname = apr_hash_this_key(hi); + svn_string_t *propval = apr_hash_this_val(hi); SVN_ERR(check_cancel(NULL)); @@ -1982,7 +1982,7 @@ do_plist(svnlook_ctxt_t *c, } else if (xml) svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "property", - "name", pname, NULL); + "name", pname, SVN_VA_NULL); else printf(" %s\n", pname); } @@ -2003,10 +2003,11 @@ do_plist(svnlook_ctxt_t *c, /* "" */ svn_xml_make_close_tag(&sb, pool, "properties"); + errno = 0; if (fputs(sb->data, stdout) == EOF) { - if (errno) - return svn_error_wrap_apr(errno, _("Write error")); + if (apr_get_os_error()) /* is errno on POSIX */ + return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); else return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL); } @@ -2074,8 +2075,8 @@ get_ctxt_baton(svnlook_ctxt_t **baton_p, { svnlook_ctxt_t *baton = apr_pcalloc(pool, sizeof(*baton)); - SVN_ERR(svn_repos_open2(&(baton->repos), opt_state->repos_path, NULL, - pool)); + SVN_ERR(svn_repos_open3(&(baton->repos), opt_state->repos_path, NULL, + pool, pool)); baton->fs = svn_repos_fs(baton->repos); svn_fs_set_warning_func(baton->fs, warning_func, NULL); baton->show_ids = opt_state->show_ids; @@ -2214,11 +2215,12 @@ subcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool) struct svnlook_opt_state *opt_state = baton; const char *header = _("general usage: svnlook SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]\n" + "Subversion repository inspection tool.\n" + "Type 'svnlook help ' for help on a specific subcommand.\n" + "Type 'svnlook --version' to see the program version and FS modules.\n" "Note: any subcommand which takes the '--revision' and '--transaction'\n" " options will, if invoked without one of those options, act on\n" " the repository's youngest revision.\n" - "Type 'svnlook help ' for help on a specific subcommand.\n" - "Type 'svnlook --version' to see the program version and FS modules.\n" "\n" "Available subcommands:\n"); @@ -2409,7 +2411,8 @@ subcommand_youngest(apr_getopt_t *os, void *baton, apr_pool_t *pool) SVN_ERR(check_number_of_args(opt_state, 0)); SVN_ERR(get_ctxt_baton(&c, opt_state, pool)); - SVN_ERR(svn_cmdline_printf(pool, "%ld\n", c->rev_id)); + SVN_ERR(svn_cmdline_printf(pool, "%ld%s", c->rev_id, + opt_state->no_newline ? "" : "\n")); return SVN_NO_ERROR; } @@ -2433,12 +2436,16 @@ subcommand_uuid(apr_getopt_t *os, void *baton, apr_pool_t *pool) /*** Main. ***/ -int -main(int argc, const char *argv[]) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err; apr_status_t apr_err; - apr_pool_t *pool; const svn_opt_subcommand_desc2_t *subcommand = NULL; struct svnlook_opt_state opt_state; @@ -2447,32 +2454,19 @@ main(int argc, const char *argv[]) apr_array_header_t *received_opts; int i; - /* Initialize the app. */ - if (svn_cmdline_init("svnlook", 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)); - received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int)); /* Check library versions */ - err = check_lib_versions(); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + SVN_ERR(check_lib_versions()); /* Initialize the FS library. */ - err = svn_fs_initialize(pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + SVN_ERR(svn_fs_initialize(pool)); if (argc <= 1) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Initialize opt_state. */ @@ -2480,9 +2474,7 @@ main(int argc, const char *argv[]) opt_state.rev = SVN_INVALID_REVNUM; /* Parse options. */ - err = svn_cmdline__getopt_init(&os, argc, argv, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; while (1) @@ -2495,9 +2487,9 @@ main(int argc, const char *argv[]) break; else if (apr_err) { - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -2512,9 +2504,8 @@ main(int argc, const char *argv[]) if ((! SVN_IS_VALID_REVNUM(opt_state.rev)) || (! digits_end) || *digits_end) - SVN_INT_ERR(svn_error_create - (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Invalid revision number supplied"))); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Invalid revision number supplied")); } break; @@ -2561,15 +2552,13 @@ main(int argc, const char *argv[]) opt_state.limit = strtol(opt_arg, &end, 10); if (end == opt_arg || *end != '\0') { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Non-numeric limit argument given")); - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Non-numeric limit argument given")); } if (opt_state.limit <= 0) { - err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, _("Argument to --limit must be positive")); - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); } } break; @@ -2614,27 +2603,31 @@ main(int argc, const char *argv[]) opt_state.show_inherited_props = TRUE; break; + case svnlook__no_newline: + opt_state.no_newline = TRUE; + break; + default: - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } /* The --transaction and --revision options may not co-exist. */ if ((opt_state.rev != SVN_INVALID_REVNUM) && opt_state.txn) - SVN_INT_ERR(svn_error_create + return svn_error_create (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, _("The '--transaction' (-t) and '--revision' (-r) arguments " - "cannot co-exist"))); + "cannot co-exist")); /* The --show-inherited-props and --revprop options may not co-exist. */ if (opt_state.show_inherited_props && opt_state.revprop) - SVN_INT_ERR(svn_error_create + return svn_error_create (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, _("Cannot use the '--show-inherited-props' option with the " - "'--revprop' option"))); + "'--revprop' option")); /* 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 @@ -2665,9 +2658,9 @@ main(int argc, const char *argv[]) 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; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -2677,15 +2670,13 @@ main(int argc, const char *argv[]) if (subcommand == NULL) { const char *first_arg_utf8; - err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, - pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + SVN_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)); + SVN_ERR(subcommand_help(NULL, NULL, pool)); /* Be kind to people who try 'svnlook verify'. */ if (strcmp(first_arg_utf8, "verify") == 0) @@ -2695,9 +2686,8 @@ main(int argc, const char *argv[]) _("Try 'svnadmin verify' instead.\n"))); } - - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -2716,9 +2706,9 @@ main(int argc, const char *argv[]) /* Get the repository. */ if (os->ind < os->argc) { - SVN_INT_ERR(svn_utf_cstring_to_utf8(&repos_path, - os->argv[os->ind++], - pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&repos_path, + os->argv[os->ind++], + pool)); repos_path = svn_dirent_internal_style(repos_path, pool); } @@ -2727,9 +2717,9 @@ main(int argc, const char *argv[]) svn_error_clear (svn_cmdline_fprintf(stderr, pool, _("Repository argument required\n"))); - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(subcommand_help(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } else if (svn_path_is_url(repos_path)) { @@ -2737,8 +2727,8 @@ main(int argc, const char *argv[]) (svn_cmdline_fprintf(stderr, pool, _("'%s' is a URL when it should be a path\n"), repos_path)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } opt_state.repos_path = repos_path; @@ -2746,8 +2736,7 @@ main(int argc, const char *argv[]) /* Get next arg (arg1), if any. */ if (os->ind < os->argc) { - SVN_INT_ERR(svn_utf_cstring_to_utf8 - (&arg1, os->argv[os->ind++], pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&arg1, os->argv[os->ind++], pool)); arg1 = svn_dirent_internal_style(arg1, pool); } opt_state.arg1 = arg1; @@ -2755,8 +2744,7 @@ main(int argc, const char *argv[]) /* Get next arg (arg2), if any. */ if (os->ind < os->argc) { - SVN_INT_ERR(svn_utf_cstring_to_utf8 - (&arg2, os->argv[os->ind++], pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&arg2, os->argv[os->ind++], pool)); arg2 = svn_dirent_internal_style(arg2, pool); } opt_state.arg2 = arg2; @@ -2782,7 +2770,7 @@ main(int argc, const char *argv[]) pool); svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') - SVN_INT_ERR(subcommand_help(NULL, NULL, pool)); + SVN_ERR(subcommand_help(NULL, NULL, pool)); else svn_error_clear (svn_cmdline_fprintf @@ -2790,8 +2778,8 @@ main(int argc, const char *argv[]) _("Subcommand '%s' doesn't accept option '%s'\n" "Type 'svnlook help %s' for usage.\n"), subcommand->name, optstr, subcommand->name)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } @@ -2832,14 +2820,40 @@ main(int argc, const char *argv[]) err = svn_error_quick_wrap(err, _("Try 'svnlook help' for more info")); } - return svn_cmdline_handle_exit_error(err, pool, "svnlook: "); + return err; } - else + + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnlook", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) { - svn_pool_destroy(pool); - /* Ensure everything is printed on stdout, so the user sees any - print errors. */ - SVN_INT_ERR(svn_cmdline_fflush(stdout)); - return EXIT_SUCCESS; + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnlook: "); } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/contrib/subversion/subversion/svnmucc/svnmucc.c b/contrib/subversion/subversion/svnmucc/svnmucc.c index d53f18f6f..1d2be38bb 100644 --- a/contrib/subversion/subversion/svnmucc/svnmucc.c +++ b/contrib/subversion/subversion/svnmucc/svnmucc.c @@ -40,96 +40,40 @@ #include +#include "svn_private_config.h" #include "svn_hash.h" #include "svn_client.h" +#include "private/svn_client_mtcc.h" #include "svn_cmdline.h" #include "svn_config.h" #include "svn_error.h" #include "svn_path.h" #include "svn_pools.h" #include "svn_props.h" -#include "svn_ra.h" #include "svn_string.h" #include "svn_subst.h" #include "svn_utf.h" #include "svn_version.h" #include "private/svn_cmdline_private.h" -#include "private/svn_ra_private.h" -#include "private/svn_string_private.h" #include "private/svn_subr_private.h" -#include "svn_private_config.h" - -static void handle_error(svn_error_t *err, apr_pool_t *pool) -{ - if (err) - svn_handle_error2(err, stderr, FALSE, "svnmucc: "); - svn_error_clear(err); - if (pool) - svn_pool_destroy(pool); - exit(EXIT_FAILURE); -} - -static apr_pool_t * -init(const char *application) -{ - svn_error_t *err; - const svn_version_checklist_t checklist[] = { - {"svn_client", svn_client_version}, - {"svn_subr", svn_subr_version}, - {"svn_ra", svn_ra_version}, - {NULL, NULL} - }; - SVN_VERSION_DEFINE(my_version); - - if (svn_cmdline_init(application, stderr)) - exit(EXIT_FAILURE); - - err = svn_ver_check_list2(&my_version, checklist, svn_ver_equal); - if (err) - handle_error(err, NULL); - - return apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); -} - +/* Version compatibility check */ static svn_error_t * -open_tmp_file(apr_file_t **fp, - void *callback_baton, - apr_pool_t *pool) +check_lib_versions(void) { - /* Open a unique file; use APR_DELONCLOSE. */ - return svn_io_open_unique_file3(fp, NULL, NULL, svn_io_file_del_on_close, - pool, pool); -} - -static svn_error_t * -create_ra_callbacks(svn_ra_callbacks2_t **callbacks, - const char *username, - const char *password, - const char *config_dir, - svn_config_t *cfg_config, - svn_boolean_t non_interactive, - svn_boolean_t trust_server_cert, - svn_boolean_t no_auth_cache, - apr_pool_t *pool) -{ - SVN_ERR(svn_ra_create_callbacks(callbacks, pool)); - - SVN_ERR(svn_cmdline_create_auth_baton(&(*callbacks)->auth_baton, - non_interactive, - username, password, config_dir, - no_auth_cache, - trust_server_cert, - cfg_config, NULL, NULL, pool)); - - (*callbacks)->open_tmp_file = open_tmp_file; + static const svn_version_checklist_t checklist[] = + { + { "svn_client", svn_client_version }, + { "svn_subr", svn_subr_version }, + { "svn_ra", svn_ra_version }, + { NULL, NULL } + }; + SVN_VERSION_DEFINE(my_version); - return SVN_NO_ERROR; + return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); } - - static svn_error_t * commit_callback(const svn_commit_info_t *commit_info, void *baton, @@ -154,204 +98,6 @@ typedef enum action_code_t { ACTION_RM } action_code_t; -struct operation { - enum { - OP_OPEN, - OP_DELETE, - OP_ADD, - OP_REPLACE, - OP_PROPSET /* only for files for which no other operation is - occuring; directories are OP_OPEN with non-empty - props */ - } operation; - svn_node_kind_t kind; /* to copy, mkdir, put or set revprops */ - svn_revnum_t rev; /* to copy, valid for add and replace */ - const char *url; /* to copy, valid for add and replace */ - const char *src_file; /* for put, the source file for contents */ - apr_hash_t *children; /* const char *path -> struct operation * */ - apr_hash_t *prop_mods; /* const char *prop_name -> - const svn_string_t *prop_value */ - apr_array_header_t *prop_dels; /* const char *prop_name deletions */ - void *baton; /* as returned by the commit editor */ -}; - - -/* An iterator (for use via apr_table_do) which sets node properties. - REC is a pointer to a struct driver_state. */ -static svn_error_t * -change_props(const svn_delta_editor_t *editor, - void *baton, - struct operation *child, - apr_pool_t *pool) -{ - apr_pool_t *iterpool = svn_pool_create(pool); - - if (child->prop_dels) - { - int i; - for (i = 0; i < child->prop_dels->nelts; i++) - { - const char *prop_name; - - svn_pool_clear(iterpool); - prop_name = APR_ARRAY_IDX(child->prop_dels, i, const char *); - if (child->kind == svn_node_dir) - SVN_ERR(editor->change_dir_prop(baton, prop_name, - NULL, iterpool)); - else - SVN_ERR(editor->change_file_prop(baton, prop_name, - NULL, iterpool)); - } - } - if (apr_hash_count(child->prop_mods)) - { - apr_hash_index_t *hi; - for (hi = apr_hash_first(pool, child->prop_mods); - hi; hi = apr_hash_next(hi)) - { - const char *propname = svn__apr_hash_index_key(hi); - const svn_string_t *val = svn__apr_hash_index_val(hi); - - svn_pool_clear(iterpool); - if (child->kind == svn_node_dir) - SVN_ERR(editor->change_dir_prop(baton, propname, val, iterpool)); - else - SVN_ERR(editor->change_file_prop(baton, propname, val, iterpool)); - } - } - - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; -} - - -/* Drive EDITOR to affect the change represented by OPERATION. HEAD - is the last-known youngest revision in the repository. */ -static svn_error_t * -drive(struct operation *operation, - svn_revnum_t head, - const svn_delta_editor_t *editor, - apr_pool_t *pool) -{ - apr_pool_t *subpool = svn_pool_create(pool); - apr_hash_index_t *hi; - - for (hi = apr_hash_first(pool, operation->children); - hi; hi = apr_hash_next(hi)) - { - const char *key = svn__apr_hash_index_key(hi); - struct operation *child = svn__apr_hash_index_val(hi); - void *file_baton = NULL; - - svn_pool_clear(subpool); - - /* Deletes and replacements are simple -- delete something. */ - if (child->operation == OP_DELETE || child->operation == OP_REPLACE) - { - SVN_ERR(editor->delete_entry(key, head, operation->baton, subpool)); - } - /* Opens could be for directories or files. */ - if (child->operation == OP_OPEN || child->operation == OP_PROPSET) - { - if (child->kind == svn_node_dir) - { - SVN_ERR(editor->open_directory(key, operation->baton, head, - subpool, &child->baton)); - } - else - { - SVN_ERR(editor->open_file(key, operation->baton, head, - subpool, &file_baton)); - } - } - /* Adds and replacements could also be for directories or files. */ - if (child->operation == OP_ADD || child->operation == OP_REPLACE) - { - if (child->kind == svn_node_dir) - { - SVN_ERR(editor->add_directory(key, operation->baton, - child->url, child->rev, - subpool, &child->baton)); - } - else - { - SVN_ERR(editor->add_file(key, operation->baton, child->url, - child->rev, subpool, &file_baton)); - } - } - /* If there's a source file and an open file baton, we get to - change textual contents. */ - if ((child->src_file) && (file_baton)) - { - svn_txdelta_window_handler_t handler; - void *handler_baton; - svn_stream_t *contents; - - SVN_ERR(editor->apply_textdelta(file_baton, NULL, subpool, - &handler, &handler_baton)); - if (strcmp(child->src_file, "-") != 0) - { - SVN_ERR(svn_stream_open_readonly(&contents, child->src_file, - pool, pool)); - } - else - { - SVN_ERR(svn_stream_for_stdin(&contents, pool)); - } - SVN_ERR(svn_txdelta_send_stream(contents, handler, - handler_baton, NULL, pool)); - } - /* If we opened a file, we need to apply outstanding propmods, - then close it. */ - if (file_baton) - { - if (child->kind == svn_node_file) - { - SVN_ERR(change_props(editor, file_baton, child, subpool)); - } - SVN_ERR(editor->close_file(file_baton, NULL, subpool)); - } - /* If we opened, added, or replaced a directory, we need to - recurse, apply outstanding propmods, and then close it. */ - if ((child->kind == svn_node_dir) - && child->operation != OP_DELETE) - { - SVN_ERR(change_props(editor, child->baton, child, subpool)); - - SVN_ERR(drive(child, head, editor, subpool)); - - SVN_ERR(editor->close_directory(child->baton, subpool)); - } - } - svn_pool_destroy(subpool); - return SVN_NO_ERROR; -} - - -/* Find the operation associated with PATH, which is a single-path - component representing a child of the path represented by - OPERATION. If no such child operation exists, create a new one of - type OP_OPEN. */ -static struct operation * -get_operation(const char *path, - struct operation *operation, - apr_pool_t *pool) -{ - struct operation *child = svn_hash_gets(operation->children, path); - if (! child) - { - child = apr_pcalloc(pool, sizeof(*child)); - child->children = apr_hash_make(pool); - child->operation = OP_OPEN; - child->rev = SVN_INVALID_REVNUM; - child->kind = svn_node_dir; - child->prop_mods = apr_hash_make(pool); - child->prop_dels = apr_array_make(pool, 1, sizeof(const char *)); - svn_hash_sets(operation->children, path, child); - } - return child; -} - /* Return the portion of URL that is relative to ANCHOR (URI-decoded). */ static const char * subtract_anchor(const char *anchor, const char *url, apr_pool_t *pool) @@ -359,221 +105,6 @@ subtract_anchor(const char *anchor, const char *url, apr_pool_t *pool) return svn_uri_skip_ancestor(anchor, url, pool); } -/* Add PATH to the operations tree rooted at OPERATION, creating any - intermediate nodes that are required. Here's what's expected for - each action type: - - ACTION URL REV SRC-FILE PROPNAME - ------------ ----- ------- -------- -------- - ACTION_MKDIR NULL invalid NULL NULL - ACTION_CP valid valid NULL NULL - ACTION_PUT NULL invalid valid NULL - ACTION_RM NULL invalid NULL NULL - ACTION_PROPSET valid invalid NULL valid - ACTION_PROPDEL valid invalid NULL valid - - Node type information is obtained for any copy source (to determine - whether to create a file or directory) and for any deleted path (to - ensure it exists since svn_delta_editor_t->delete_entry doesn't - return an error on non-existent nodes). */ -static svn_error_t * -build(action_code_t action, - const char *path, - const char *url, - svn_revnum_t rev, - const char *prop_name, - const svn_string_t *prop_value, - const char *src_file, - svn_revnum_t head, - const char *anchor, - svn_ra_session_t *session, - struct operation *operation, - apr_pool_t *pool) -{ - apr_array_header_t *path_bits = svn_path_decompose(path, pool); - const char *path_so_far = ""; - const char *copy_src = NULL; - svn_revnum_t copy_rev = SVN_INVALID_REVNUM; - int i; - - /* Look for any previous operations we've recognized for PATH. If - any of PATH's ancestors have not yet been traversed, we'll be - creating OP_OPEN operations for them as we walk down PATH's path - components. */ - for (i = 0; i < path_bits->nelts; ++i) - { - const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *); - path_so_far = svn_relpath_join(path_so_far, path_bit, pool); - operation = get_operation(path_so_far, operation, pool); - - /* If we cross a replace- or add-with-history, remember the - source of those things in case we need to lookup the node kind - of one of their children. And if this isn't such a copy, - but we've already seen one in of our parent paths, we just need - to extend that copy source path by our current path - component. */ - if (operation->url - && SVN_IS_VALID_REVNUM(operation->rev) - && (operation->operation == OP_REPLACE - || operation->operation == OP_ADD)) - { - copy_src = subtract_anchor(anchor, operation->url, pool); - copy_rev = operation->rev; - } - else if (copy_src) - { - copy_src = svn_relpath_join(copy_src, path_bit, pool); - } - } - - /* Handle property changes. */ - if (prop_name) - { - if (operation->operation == OP_DELETE) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "cannot set properties on a location being" - " deleted ('%s')", path); - /* If we're not adding this thing ourselves, check for existence. */ - if (! ((operation->operation == OP_ADD) || - (operation->operation == OP_REPLACE))) - { - SVN_ERR(svn_ra_check_path(session, - copy_src ? copy_src : path, - copy_src ? copy_rev : head, - &operation->kind, pool)); - if (operation->kind == svn_node_none) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "propset: '%s' not found", path); - else if ((operation->kind == svn_node_file) - && (operation->operation == OP_OPEN)) - operation->operation = OP_PROPSET; - } - if (! prop_value) - APR_ARRAY_PUSH(operation->prop_dels, const char *) = prop_name; - else - svn_hash_sets(operation->prop_mods, prop_name, prop_value); - if (!operation->rev) - operation->rev = rev; - return SVN_NO_ERROR; - } - - /* We won't fuss about multiple operations on the same path in the - following cases: - - - the prior operation was, in fact, a no-op (open) - - the prior operation was a propset placeholder - - the prior operation was a deletion - - Note: while the operation structure certainly supports the - ability to do a copy of a file followed by a put of new contents - for the file, we don't let that happen (yet). - */ - if (operation->operation != OP_OPEN - && operation->operation != OP_PROPSET - && operation->operation != OP_DELETE) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "unsupported multiple operations on '%s'", path); - - /* For deletions, we validate that there's actually something to - delete. If this is a deletion of the child of a copied - directory, we need to remember to look in the copy source tree to - verify that this thing actually exists. */ - if (action == ACTION_RM) - { - operation->operation = OP_DELETE; - SVN_ERR(svn_ra_check_path(session, - copy_src ? copy_src : path, - copy_src ? copy_rev : head, - &operation->kind, pool)); - if (operation->kind == svn_node_none) - { - if (copy_src && strcmp(path, copy_src)) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "'%s' (from '%s:%ld') not found", - path, copy_src, copy_rev); - else - return svn_error_createf(SVN_ERR_BAD_URL, NULL, "'%s' not found", - path); - } - } - /* Handle copy operations (which can be adds or replacements). */ - else if (action == ACTION_CP) - { - if (rev > head) - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - "Copy source revision cannot be younger " - "than base revision"); - operation->operation = - operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD; - if (operation->operation == OP_ADD) - { - /* There is a bug in the current version of mod_dav_svn - which incorrectly replaces existing directories. - Therefore we need to check if the target exists - and raise an error here. */ - SVN_ERR(svn_ra_check_path(session, - copy_src ? copy_src : path, - copy_src ? copy_rev : head, - &operation->kind, pool)); - if (operation->kind != svn_node_none) - { - if (copy_src && strcmp(path, copy_src)) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "'%s' (from '%s:%ld') already exists", - path, copy_src, copy_rev); - else - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "'%s' already exists", path); - } - } - SVN_ERR(svn_ra_check_path(session, subtract_anchor(anchor, url, pool), - rev, &operation->kind, pool)); - if (operation->kind == svn_node_none) - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "'%s' not found", - subtract_anchor(anchor, url, pool)); - operation->url = url; - operation->rev = rev; - } - /* Handle mkdir operations (which can be adds or replacements). */ - else if (action == ACTION_MKDIR) - { - operation->operation = - operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD; - operation->kind = svn_node_dir; - } - /* Handle put operations (which can be adds, replacements, or opens). */ - else if (action == ACTION_PUT) - { - if (operation->operation == OP_DELETE) - { - operation->operation = OP_REPLACE; - } - else - { - SVN_ERR(svn_ra_check_path(session, - copy_src ? copy_src : path, - copy_src ? copy_rev : head, - &operation->kind, pool)); - if (operation->kind == svn_node_file) - operation->operation = OP_OPEN; - else if (operation->kind == svn_node_none) - operation->operation = OP_ADD; - else - return svn_error_createf(SVN_ERR_BAD_URL, NULL, - "'%s' is not a file", path); - } - operation->kind = svn_node_file; - operation->src_file = src_file; - } - else - { - /* We shouldn't get here. */ - SVN_ERR_MALFUNCTION(); - } - - return SVN_NO_ERROR; -} struct action { action_code_t action; @@ -597,274 +128,89 @@ struct action { const svn_string_t *prop_value; }; -struct fetch_baton -{ - svn_ra_session_t *session; - svn_revnum_t head; -}; - -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 fetch_baton *fb = baton; - svn_stream_t *fstream; - svn_error_t *err; - - if (! SVN_IS_VALID_REVNUM(base_revision)) - base_revision = fb->head; - - 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(fb->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 fetch_baton *fb = baton; - svn_node_kind_t node_kind; - - if (! SVN_IS_VALID_REVNUM(base_revision)) - base_revision = fb->head; - - SVN_ERR(svn_ra_check_path(fb->session, path, base_revision, &node_kind, - scratch_pool)); - - if (node_kind == svn_node_file) - { - SVN_ERR(svn_ra_get_file(fb->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(fb->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 fetch_baton *fb = baton; - - if (! SVN_IS_VALID_REVNUM(base_revision)) - base_revision = fb->head; - - SVN_ERR(svn_ra_check_path(fb->session, path, base_revision, kind, - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_delta_shim_callbacks_t * -get_shim_callbacks(svn_ra_session_t *session, - svn_revnum_t head, - apr_pool_t *result_pool) -{ - svn_delta_shim_callbacks_t *callbacks = - svn_delta_shim_callbacks_default(result_pool); - struct fetch_baton *fb = apr_pcalloc(result_pool, sizeof(*fb)); - - fb->session = session; - fb->head = head; - - callbacks->fetch_props_func = fetch_props_func; - callbacks->fetch_kind_func = fetch_kind_func; - callbacks->fetch_base_func = fetch_base_func; - callbacks->fetch_baton = fb; - - return callbacks; -} - static svn_error_t * execute(const apr_array_header_t *actions, const char *anchor, apr_hash_t *revprops, - const char *username, - const char *password, - const char *config_dir, - const apr_array_header_t *config_options, - svn_boolean_t non_interactive, - svn_boolean_t trust_server_cert, - svn_boolean_t no_auth_cache, svn_revnum_t base_revision, + svn_client_ctx_t *ctx, apr_pool_t *pool) { - svn_ra_session_t *session; - svn_ra_session_t *aux_session; - const char *repos_root; - svn_revnum_t head; - const svn_delta_editor_t *editor; - svn_ra_callbacks2_t *ra_callbacks; - void *editor_baton; - struct operation root; + svn_client__mtcc_t *mtcc; + apr_pool_t *iterpool = svn_pool_create(pool); svn_error_t *err; - apr_hash_t *config; - svn_config_t *cfg_config; int i; - SVN_ERR(svn_config_get_config(&config, config_dir, pool)); - SVN_ERR(svn_cmdline__apply_config_options(config, config_options, - "svnmucc: ", "--config-option")); - cfg_config = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); - - if (! svn_hash_gets(revprops, SVN_PROP_REVISION_LOG)) - { - svn_string_t *msg = svn_string_create("", pool); - - /* If we can do so, try to pop up $EDITOR to fetch a log message. */ - if (non_interactive) - { - return svn_error_create - (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, - _("Cannot invoke editor to get log message " - "when non-interactive")); - } - else - { - SVN_ERR(svn_cmdline__edit_string_externally( - &msg, NULL, NULL, "", msg, "svnmucc-commit", config, - TRUE, NULL, apr_hash_pool_get(revprops))); - } - - svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, msg); - } - - SVN_ERR(create_ra_callbacks(&ra_callbacks, username, password, config_dir, - cfg_config, non_interactive, trust_server_cert, - no_auth_cache, pool)); - SVN_ERR(svn_ra_open4(&session, NULL, anchor, NULL, ra_callbacks, - NULL, config, pool)); - /* Open, then reparent to avoid AUTHZ errors when opening the reposroot */ - SVN_ERR(svn_ra_open4(&aux_session, NULL, anchor, NULL, ra_callbacks, - NULL, config, pool)); - SVN_ERR(svn_ra_get_repos_root2(aux_session, &repos_root, pool)); - SVN_ERR(svn_ra_reparent(aux_session, repos_root, pool)); - SVN_ERR(svn_ra_get_latest_revnum(session, &head, pool)); - - /* Reparent to ANCHOR's dir, if ANCHOR is not a directory. */ - { - svn_node_kind_t kind; - - SVN_ERR(svn_ra_check_path(aux_session, - svn_uri_skip_ancestor(repos_root, anchor, pool), - head, &kind, pool)); - if (kind != svn_node_dir) - { - anchor = svn_uri_dirname(anchor, pool); - SVN_ERR(svn_ra_reparent(session, anchor, pool)); - } - } - - if (SVN_IS_VALID_REVNUM(base_revision)) - { - if (base_revision > head) - return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, - "No such revision %ld (youngest is %ld)", - base_revision, head); - head = base_revision; - } - - memset(&root, 0, sizeof(root)); - root.children = apr_hash_make(pool); - root.operation = OP_OPEN; - root.kind = svn_node_dir; /* For setting properties */ - root.prop_mods = apr_hash_make(pool); - root.prop_dels = apr_array_make(pool, 1, sizeof(const char *)); + SVN_ERR(svn_client__mtcc_create(&mtcc, anchor, + SVN_IS_VALID_REVNUM(base_revision) + ? base_revision + : SVN_INVALID_REVNUM, + ctx, pool, iterpool)); for (i = 0; i < actions->nelts; ++i) { struct action *action = APR_ARRAY_IDX(actions, i, struct action *); const char *path1, *path2; + svn_node_kind_t kind; + + svn_pool_clear(iterpool); + switch (action->action) { case ACTION_MV: path1 = subtract_anchor(anchor, action->path[0], pool); path2 = subtract_anchor(anchor, action->path[1], pool); - SVN_ERR(build(ACTION_RM, path1, NULL, - SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor, - session, &root, pool)); - SVN_ERR(build(ACTION_CP, path2, action->path[0], - head, NULL, NULL, NULL, head, anchor, - session, &root, pool)); + SVN_ERR(svn_client__mtcc_add_move(path1, path2, mtcc, iterpool)); break; case ACTION_CP: + path1 = subtract_anchor(anchor, action->path[0], pool); path2 = subtract_anchor(anchor, action->path[1], pool); - if (action->rev == SVN_INVALID_REVNUM) - action->rev = head; - SVN_ERR(build(ACTION_CP, path2, action->path[0], - action->rev, NULL, NULL, NULL, head, anchor, - session, &root, pool)); + SVN_ERR(svn_client__mtcc_add_copy(path1, action->rev, path2, + mtcc, iterpool)); break; case ACTION_RM: path1 = subtract_anchor(anchor, action->path[0], pool); - SVN_ERR(build(ACTION_RM, path1, NULL, - SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor, - session, &root, pool)); + SVN_ERR(svn_client__mtcc_add_delete(path1, mtcc, iterpool)); break; case ACTION_MKDIR: path1 = subtract_anchor(anchor, action->path[0], pool); - SVN_ERR(build(ACTION_MKDIR, path1, action->path[0], - SVN_INVALID_REVNUM, NULL, NULL, NULL, head, anchor, - session, &root, pool)); + SVN_ERR(svn_client__mtcc_add_mkdir(path1, mtcc, iterpool)); break; case ACTION_PUT: path1 = subtract_anchor(anchor, action->path[0], pool); - SVN_ERR(build(ACTION_PUT, path1, action->path[0], - SVN_INVALID_REVNUM, NULL, NULL, action->path[1], - head, anchor, session, &root, pool)); + SVN_ERR(svn_client__mtcc_check_path(&kind, path1, TRUE, mtcc, pool)); + + if (kind == svn_node_dir) + { + SVN_ERR(svn_client__mtcc_add_delete(path1, mtcc, pool)); + kind = svn_node_none; + } + + { + svn_stream_t *src; + + if (strcmp(action->path[1], "-") != 0) + SVN_ERR(svn_stream_open_readonly(&src, action->path[1], + pool, iterpool)); + else + SVN_ERR(svn_stream_for_stdin(&src, pool)); + + + if (kind == svn_node_file) + SVN_ERR(svn_client__mtcc_add_update_file(path1, src, NULL, + NULL, NULL, + mtcc, iterpool)); + else if (kind == svn_node_none) + SVN_ERR(svn_client__mtcc_add_add_file(path1, src, NULL, + mtcc, iterpool)); + } break; case ACTION_PROPSET: case ACTION_PROPDEL: path1 = subtract_anchor(anchor, action->path[0], pool); - SVN_ERR(build(action->action, path1, action->path[0], - SVN_INVALID_REVNUM, - action->prop_name, action->prop_value, - NULL, head, anchor, session, &root, pool)); + SVN_ERR(svn_client__mtcc_add_propset(path1, action->prop_name, + action->prop_value, FALSE, + mtcc, iterpool)); break; case ACTION_PROPSETF: default: @@ -872,25 +218,11 @@ execute(const apr_array_header_t *actions, } } - SVN_ERR(svn_ra__register_editor_shim_callbacks(session, - get_shim_callbacks(aux_session, head, pool))); - SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &editor_baton, revprops, - commit_callback, NULL, NULL, FALSE, pool)); + err = svn_client__mtcc_commit(revprops, commit_callback, NULL, + mtcc, iterpool); - SVN_ERR(editor->open_root(editor_baton, head, pool, &root.baton)); - err = change_props(editor, root.baton, &root, pool); - if (!err) - err = drive(&root, head, editor, pool); - if (!err) - err = editor->close_directory(root.baton, pool); - if (!err) - err = editor->close_edit(editor_baton, pool); - - if (err) - err = svn_error_compose_create(err, - editor->abort_edit(editor_baton, pool)); - - return err; + svn_pool_destroy(iterpool); + return svn_error_trace(err); } static svn_error_t * @@ -920,12 +252,20 @@ sanitize_url(const char *url, } static void -usage(apr_pool_t *pool, int exit_val) +usage(apr_pool_t *pool) +{ + svn_error_clear(svn_cmdline_fprintf + (stderr, pool, _("Type 'svnmucc --help' for usage.\n"))); +} + +/* Print a usage message on STREAM. */ +static void +help(FILE *stream, apr_pool_t *pool) { - FILE *stream = exit_val == EXIT_SUCCESS ? stdout : stderr; svn_error_clear(svn_cmdline_fputs( - _("Subversion multiple URL command client\n" - "usage: svnmucc ACTION...\n" + _("usage: svnmucc ACTION...\n" + "Subversion multiple URL command client.\n" + "Type 'svnmucc --version' to see the program version and RA modules.\n" "\n" " Perform one or more Subversion repository URL-based ACTIONs, committing\n" " the result as a (single) new revision.\n" @@ -955,9 +295,16 @@ usage(apr_pool_t *pool, int exit_val) " prompt only if standard input is a terminal)\n" " --force-interactive : do interactive prompting even if standard\n" " input is not a terminal\n" - " --trust-server-cert : accept SSL server certificates from unknown\n" - " certificate authorities without prompting (but\n" - " only with '--non-interactive')\n" + " --trust-server-cert : deprecated;\n" + " same as --trust-server-cert-failures=unknown-ca\n" + " --trust-server-cert-failures ARG\n" + " with --non-interactive, accept SSL server\n" + " certificates with failures; ARG is comma-separated\n" + " list of 'unknown-ca' (Unknown Authority),\n" + " 'cn-mismatch' (Hostname mismatch), 'expired'\n" + " (Expired certificate),'not-yet-valid' (Not yet\n" + " valid certificate) and 'other' (all other not\n" + " separately classified certificate errors).\n" " -X [--extra-args] ARG : append arguments from file ARG (one per line;\n" " use \"-\" to read from standard input)\n" " --config-dir ARG : use ARG to override the config directory\n" @@ -965,20 +312,17 @@ usage(apr_pool_t *pool, int exit_val) " --no-auth-cache : do not cache authentication tokens\n" " --version : print version information\n"), stream, pool)); - svn_pool_destroy(pool); - exit(exit_val); } -static void -insufficient(apr_pool_t *pool) +static svn_error_t * +insufficient(void) { - handle_error(svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, - "insufficient arguments"), - pool); + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + "insufficient arguments"); } static svn_error_t * -display_version(apr_getopt_t *os, apr_pool_t *pool) +display_version(apr_pool_t *pool) { const char *ra_desc_start = "The following repository access (RA) modules are available:\n\n"; @@ -987,7 +331,7 @@ display_version(apr_getopt_t *os, apr_pool_t *pool) version_footer = svn_stringbuf_create(ra_desc_start, pool); SVN_ERR(svn_ra_print_modules(version_footer, pool)); - SVN_ERR(svn_opt_print_help4(os, "svnmucc", TRUE, FALSE, FALSE, + SVN_ERR(svn_opt_print_help4(NULL, "svnmucc", TRUE, FALSE, FALSE, version_footer->data, NULL, NULL, NULL, NULL, NULL, pool)); @@ -1005,46 +349,113 @@ mutually_exclusive_logs_error(void) "exclusive")); } -/* Ensure that the REVPROPS hash contains a command-line-provided log - message, if any, and that there was but one source of such a thing - provided on that command-line. */ +/* Obtain the log message from multiple sources, producing an error + if there are multiple sources. Store the result in *FINAL_MESSAGE. */ static svn_error_t * -sanitize_log_sources(apr_hash_t *revprops, +sanitize_log_sources(const char **final_message, const char *message, - svn_stringbuf_t *filedata) + apr_hash_t *revprops, + svn_stringbuf_t *filedata, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *hash_pool = apr_hash_pool_get(revprops); + svn_string_t *msg; + *final_message = NULL; /* If we already have a log message in the revprop hash, then just make sure the user didn't try to also use -m or -F. Otherwise, we need to consult -m or -F to find a log message, if any. */ - if (svn_hash_gets(revprops, SVN_PROP_REVISION_LOG)) + msg = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG); + if (msg) { if (filedata || message) return mutually_exclusive_logs_error(); + + *final_message = apr_pstrdup(result_pool, msg->data); + + /* Will be re-added by libsvn_client */ + svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, NULL); } else if (filedata) { if (message) return mutually_exclusive_logs_error(); - SVN_ERR(svn_utf_cstring_to_utf8(&message, filedata->data, hash_pool)); - svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, - svn_stringbuf__morph_into_string(filedata)); + *final_message = apr_pstrdup(result_pool, filedata->data); } else if (message) { - svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, - svn_string_create(message, hash_pool)); + *final_message = apr_pstrdup(result_pool, message); } return SVN_NO_ERROR; } -int -main(int argc, const char **argv) +/* Baton for log_message_func */ +struct log_message_baton +{ + svn_boolean_t non_interactive; + const char *log_message; + svn_client_ctx_t *ctx; +}; + +/* Implements svn_client_get_commit_log3_t */ +static svn_error_t * +log_message_func(const char **log_msg, + const char **tmp_file, + const apr_array_header_t *commit_items, + void *baton, + apr_pool_t *pool) +{ + struct log_message_baton *lmb = baton; + + *tmp_file = NULL; + + if (lmb->log_message) + { + svn_string_t *message = svn_string_create(lmb->log_message, pool); + + SVN_ERR_W(svn_subst_translate_string2(&message, NULL, NULL, + message, NULL, FALSE, + pool, pool), + _("Error normalizing log message to internal format")); + + *log_msg = message->data; + + return SVN_NO_ERROR; + } + + if (lmb->non_interactive) + { + return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, + _("Cannot invoke editor to get log message " + "when non-interactive")); + } + else + { + svn_string_t *msg = svn_string_create("", pool); + + SVN_ERR(svn_cmdline__edit_string_externally( + &msg, NULL, NULL, "", msg, "svnmucc-commit", + lmb->ctx->config, TRUE, NULL, pool)); + + if (msg && msg->data) + *log_msg = msg->data; + else + *log_msg = NULL; + + return SVN_NO_ERROR; + } +} + +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { - apr_pool_t *pool = init("svnmucc"); apr_array_header_t *actions = apr_array_make(pool, 1, sizeof(struct action *)); const char *anchor = NULL; @@ -1058,7 +469,8 @@ main(int argc, const char **argv) with_revprop_opt, non_interactive_opt, force_interactive_opt, - trust_server_cert_opt + trust_server_cert_opt, + trust_server_cert_failures_opt, }; static const apr_getopt_option_t options[] = { {"message", 'm', 1, ""}, @@ -1074,6 +486,7 @@ main(int argc, const char **argv) {"non-interactive", non_interactive_opt, 0, ""}, {"force-interactive", force_interactive_opt, 0, ""}, {"trust-server-cert", trust_server_cert_opt, 0, ""}, + {"trust-server-cert-failures", trust_server_cert_failures_opt, 1, ""}, {"config-dir", config_dir_opt, 1, ""}, {"config-option", config_inline_opt, 1, ""}, {"no-auth-cache", no_auth_cache_opt, 0, ""}, @@ -1088,13 +501,26 @@ main(int argc, const char **argv) apr_array_header_t *config_options; svn_boolean_t non_interactive = FALSE; svn_boolean_t force_interactive = FALSE; - svn_boolean_t trust_server_cert = FALSE; + svn_boolean_t trust_unknown_ca = FALSE; + svn_boolean_t trust_cn_mismatch = FALSE; + svn_boolean_t trust_expired = FALSE; + svn_boolean_t trust_not_yet_valid = FALSE; + svn_boolean_t trust_other_failure = FALSE; svn_boolean_t no_auth_cache = FALSE; + svn_boolean_t show_version = FALSE; + svn_boolean_t show_help = FALSE; svn_revnum_t base_revision = SVN_INVALID_REVNUM; apr_array_header_t *action_args; apr_hash_t *revprops = apr_hash_make(pool); + apr_hash_t *cfg_hash; + svn_config_t *cfg_config; + svn_client_ctx_t *ctx; + struct log_message_baton lmb; int i; + /* Check library versions */ + SVN_ERR(check_lib_versions()); + config_options = apr_array_make(pool, 0, sizeof(svn_cmdline__config_argument_t*)); @@ -1110,22 +536,21 @@ main(int argc, const char **argv) if (APR_STATUS_IS_EOF(status)) break; if (status != APR_SUCCESS) - handle_error(svn_error_wrap_apr(status, "getopt failure"), pool); + { + usage(pool); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } switch(opt) { case 'm': - err = svn_utf_cstring_to_utf8(&message, arg, pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&message, arg, pool)); break; case 'F': { const char *arg_utf8; - err = svn_utf_cstring_to_utf8(&arg_utf8, arg, pool); - if (! err) - err = svn_stringbuf_from_file2(&filedata, arg, pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&arg_utf8, arg, pool)); + SVN_ERR(svn_stringbuf_from_file2(&filedata, arg, pool)); } break; case 'u': @@ -1135,31 +560,29 @@ main(int argc, const char **argv) password = apr_pstrdup(pool, arg); break; case 'U': - err = svn_utf_cstring_to_utf8(&root_url, arg, pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&root_url, arg, pool)); if (! svn_path_is_url(root_url)) - handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "'%s' is not a URL\n", root_url), - pool); + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "'%s' is not a URL\n", root_url); root_url = sanitize_url(root_url, pool); break; case 'r': { + const char *saved_arg = arg; char *digits_end = NULL; + while (*arg == 'r') + arg++; base_revision = strtol(arg, &digits_end, 10); if ((! SVN_IS_VALID_REVNUM(base_revision)) || (! digits_end) || *digits_end) - handle_error(svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, - NULL, "Invalid revision number"), - pool); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Invalid revision number '%s'"), + saved_arg); } break; case with_revprop_opt: - err = svn_opt_parse_revprop(&revprops, arg, pool); - if (err != SVN_NO_ERROR) - handle_error(err, pool); + SVN_ERR(svn_opt_parse_revprop(&revprops, arg, pool)); break; case 'X': extra_args_file = apr_pstrdup(pool, arg); @@ -1170,72 +593,81 @@ main(int argc, const char **argv) case force_interactive_opt: force_interactive = TRUE; break; - case trust_server_cert_opt: - trust_server_cert = TRUE; + case trust_server_cert_opt: /* backward compat */ + trust_unknown_ca = TRUE; + break; + case trust_server_cert_failures_opt: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &trust_unknown_ca, + &trust_cn_mismatch, + &trust_expired, + &trust_not_yet_valid, + &trust_other_failure, + opt_arg, pool)); break; case config_dir_opt: - err = svn_utf_cstring_to_utf8(&config_dir, arg, pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&config_dir, arg, pool)); break; case config_inline_opt: - err = svn_utf_cstring_to_utf8(&opt_arg, arg, pool); - if (err) - handle_error(err, pool); - - err = svn_cmdline__parse_config_option(config_options, opt_arg, - pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(config_options, opt_arg, + "svnmucc: ", + pool)); break; case no_auth_cache_opt: no_auth_cache = TRUE; break; case version_opt: - SVN_INT_ERR(display_version(opts, pool)); - exit(EXIT_SUCCESS); + show_version = TRUE; break; case 'h': case '?': - usage(pool, EXIT_SUCCESS); + show_help = TRUE; break; } } + if (show_help) + { + help(stdout, pool); + return SVN_NO_ERROR; + } + + if (show_version) + { + SVN_ERR(display_version(pool)); + return SVN_NO_ERROR; + } + if (non_interactive && force_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--non-interactive and --force-interactive " - "are mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnmucc: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); } else non_interactive = !svn_cmdline__be_interactive(non_interactive, force_interactive); - if (trust_server_cert && !non_interactive) + if (!non_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--trust-server-cert requires " - "--non-interactive")); - return svn_cmdline_handle_exit_error(err, pool, "svnmucc: "); + if (trust_unknown_ca || trust_cn_mismatch || trust_expired + || trust_not_yet_valid || trust_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); } - /* Make sure we have a log message to use. */ - err = sanitize_log_sources(revprops, message, filedata); - if (err) - handle_error(err, pool); - /* Copy the rest of our command-line arguments to an array, UTF-8-ing them along the way. */ action_args = apr_array_make(pool, opts->argc, sizeof(const char *)); while (opts->ind < opts->argc) { const char *arg = opts->argv[opts->ind++]; - if ((err = svn_utf_cstring_to_utf8(&(APR_ARRAY_PUSH(action_args, - const char *)), - arg, pool))) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&APR_ARRAY_PUSH(action_args, + const char *), + arg, pool)); } /* If there are extra arguments in a supplementary file, tack those @@ -1245,18 +677,69 @@ main(int argc, const char **argv) const char *extra_args_file_utf8; svn_stringbuf_t *contents, *contents_utf8; - err = svn_utf_cstring_to_utf8(&extra_args_file_utf8, - extra_args_file, pool); - if (! err) - err = svn_stringbuf_from_file2(&contents, extra_args_file_utf8, pool); - if (! err) - err = svn_utf_stringbuf_to_utf8(&contents_utf8, contents, pool); - if (err) - handle_error(err, pool); + SVN_ERR(svn_utf_cstring_to_utf8(&extra_args_file_utf8, + extra_args_file, pool)); + SVN_ERR(svn_stringbuf_from_file2(&contents, extra_args_file_utf8, pool)); + SVN_ERR(svn_utf_stringbuf_to_utf8(&contents_utf8, contents, pool)); svn_cstring_split_append(action_args, contents_utf8->data, "\n\r", FALSE, pool); } + /* Now initialize the client context */ + + err = svn_config_get_config(&cfg_hash, 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_handle_warning2(stderr, err, "svnmucc: "); + svn_error_clear(err); + + SVN_ERR(svn_config__get_default_config(&cfg_hash, pool)); + } + else + return err; + } + + if (config_options) + { + svn_error_clear( + svn_cmdline__apply_config_options(cfg_hash, config_options, + "svnmucc: ", "--config-option")); + } + + SVN_ERR(svn_client_create_context2(&ctx, cfg_hash, pool)); + + cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG); + SVN_ERR(svn_cmdline_create_auth_baton2( + &ctx->auth_baton, + non_interactive, + username, + password, + config_dir, + no_auth_cache, + trust_unknown_ca, + trust_cn_mismatch, + trust_expired, + trust_not_yet_valid, + trust_other_failure, + cfg_config, + ctx->cancel_func, + ctx->cancel_baton, + pool)); + + lmb.non_interactive = non_interactive; + lmb.ctx = ctx; + /* Make sure we have a log message to use. */ + SVN_ERR(sanitize_log_sources(&lmb.log_message, message, revprops, filedata, + pool, pool)); + + ctx->log_msg_func3 = log_message_func; + ctx->log_msg_baton3 = &lmb; + /* Now, we iterate over the combined set of arguments -- our actions. */ for (i = 0; i < action_args->nelts; ) { @@ -1283,13 +766,16 @@ main(int argc, const char **argv) action->action = ACTION_PROPDEL; else if (! strcmp(action_string, "?") || ! strcmp(action_string, "h") || ! strcmp(action_string, "help")) - usage(pool, EXIT_SUCCESS); + { + help(stdout, pool); + return SVN_NO_ERROR; + } else - handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "'%s' is not an action\n", - action_string), pool); + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "'%s' is not an action\n", + action_string); if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); /* For copies, there should be a revision number next. */ if (action->action == ACTION_CP) @@ -1308,12 +794,12 @@ main(int argc, const char **argv) action->rev = strtol(rev_str, &end, 0); if (*end) - handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "'%s' is not a revision\n", - rev_str), pool); + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "'%s' is not a revision\n", + rev_str); } if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); } else { @@ -1327,7 +813,7 @@ main(int argc, const char **argv) svn_dirent_internal_style(APR_ARRAY_IDX(action_args, i, const char *), pool); if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); } /* For propset, propsetf, and propdel, a property name (and @@ -1338,7 +824,7 @@ main(int argc, const char **argv) { action->prop_name = APR_ARRAY_IDX(action_args, i, const char *); if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); if (action->action == ACTION_PROPDEL) { @@ -1350,7 +836,7 @@ main(int argc, const char **argv) svn_string_create(APR_ARRAY_IDX(action_args, i, const char *), pool); if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); } else { @@ -1359,12 +845,10 @@ main(int argc, const char **argv) const char *), pool); if (++i == action_args->nelts) - insufficient(pool); + return insufficient(); - err = read_propvalue_file(&(action->prop_value), - propval_file, pool); - if (err) - handle_error(err, pool); + SVN_ERR(read_propvalue_file(&(action->prop_value), + propval_file, pool)); action->action = ACTION_PROPSET; } @@ -1373,14 +857,10 @@ main(int argc, const char **argv) && svn_prop_needs_translation(action->prop_name)) { svn_string_t *translated_value; - err = svn_subst_translate_string2(&translated_value, NULL, - NULL, action->prop_value, NULL, - FALSE, pool, pool); - if (err) - handle_error( - svn_error_quick_wrap(err, - "Error normalizing property value"), - pool); + SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL, + NULL, action->prop_value, + NULL, FALSE, pool, pool), + "Error normalizing property value"); action->prop_value = translated_value; } } @@ -1408,14 +888,14 @@ main(int argc, const char **argv) if (! svn_path_is_url(url)) { if (! root_url) - handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "'%s' is not a URL, and " - "--root-url (-U) not provided\n", - url), pool); + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "'%s' is not a URL, and " + "--root-url (-U) not provided\n", + url); /* ### These relpaths are already URI-encoded. */ url = apr_pstrcat(pool, root_url, "/", svn_relpath_canonicalize(url, pool), - (char *)NULL); + SVN_VA_NULL); } url = sanitize_url(url, pool); action->path[j] = url; @@ -1433,36 +913,66 @@ main(int argc, const char **argv) { anchor = svn_uri_get_longest_ancestor(anchor, url, pool); if (!anchor || !anchor[0]) - handle_error(svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "URLs in the action list do not " - "share a common ancestor"), - pool); + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "URLs in the action list do not " + "share a common ancestor"); } if ((++i == action_args->nelts) && (j + 1 < num_url_args)) - insufficient(pool); + return insufficient(); } + APR_ARRAY_PUSH(actions, struct action *) = action; } if (! actions->nelts) - usage(pool, EXIT_FAILURE); + { + *exit_code = EXIT_FAILURE; + help(stderr, pool); + return SVN_NO_ERROR; + } - if ((err = execute(actions, anchor, revprops, username, password, - config_dir, config_options, non_interactive, - trust_server_cert, no_auth_cache, base_revision, pool))) + if ((err = execute(actions, anchor, revprops, base_revision, ctx, pool))) { if (err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive) err = svn_error_quick_wrap(err, _("Authentication failed and interactive" " prompting is disabled; see the" " --force-interactive option")); - handle_error(err, pool); + return err; } - /* Ensure that stdout is flushed, so the user will see all results. */ - svn_error_clear(svn_cmdline_fflush(stdout)); + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnmucc", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnmucc: "); + } svn_pool_destroy(pool); - return EXIT_SUCCESS; + return exit_code; } diff --git a/contrib/subversion/subversion/svnrdump/dump_editor.c b/contrib/subversion/subversion/svnrdump/dump_editor.c index faf029f47..bf4f81c8b 100644 --- a/contrib/subversion/subversion/svnrdump/dump_editor.c +++ b/contrib/subversion/subversion/svnrdump/dump_editor.c @@ -30,6 +30,7 @@ #include "svn_subst.h" #include "svn_dirent_uri.h" +#include "private/svn_repos_private.h" #include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" #include "private/svn_editor.h" @@ -39,28 +40,16 @@ #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 /* A directory baton used by all directory-related callback functions * in the dump editor. */ struct dir_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-directory allocations */ apr_pool_t *pool; - /* is this directory a new addition to this revision? */ - svn_boolean_t added; - - /* has this directory been written to the output stream? */ - svn_boolean_t written_out; - /* the path to this directory */ const char *repos_relpath; /* a relpath */ @@ -68,6 +57,9 @@ struct dir_baton const char *copyfrom_path; /* a relpath */ svn_revnum_t copyfrom_rev; + /* Headers accumulated so far for this directory */ + svn_repos__dumpfile_headers_t *headers; + /* Properties which were modified during change_dir_prop. */ apr_hash_t *props; @@ -80,9 +72,8 @@ struct dir_baton us, although they're all really within this directory. */ apr_hash_t *deleted_entries; - /* Flags to trigger dumping props and record termination newlines. */ + /* Flag to trigger dumping props. */ svn_boolean_t dump_props; - svn_boolean_t dump_newlines; }; /* A file baton used by all file-related callback functions in the dump @@ -90,7 +81,6 @@ struct dir_baton struct file_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-file allocations */ apr_pool_t *pool; @@ -120,13 +110,6 @@ struct file_baton svn_boolean_t dump_props; }; -/* A handler baton to be used in window_handler(). */ -struct handler_baton -{ - svn_txdelta_window_handler_t apply_handler; - void *apply_baton; -}; - /* The baton used by the dump editor. */ struct dump_edit_baton { /* The output stream we write the dumpfile to */ @@ -155,11 +138,10 @@ struct dump_edit_baton { /* The revision we're currently dumping. */ svn_revnum_t current_revision; - /* The kind (file or directory) and baton of the item whose block of + /* The baton of the directory node whose block of dump stream data has not been fully completed; NULL if there's no such item. */ - svn_node_kind_t pending_kind; - void *pending_baton; + struct dir_baton *pending_db; }; /* Make a directory baton to represent the directory at PATH (relative @@ -171,16 +153,15 @@ struct dump_edit_baton { * copy source. * * PB is the directory baton of this directory's parent, or NULL if - * this is the top-level directory of the edit. ADDED indicates if - * this directory is newly added in this revision. Perform all - * allocations in POOL. */ + * this is the top-level directory of the edit. + * + * Perform all allocations in POOL. */ static struct dir_baton * make_dir_baton(const char *path, const char *copyfrom_path, svn_revnum_t copyfrom_rev, void *edit_baton, struct dir_baton *pb, - svn_boolean_t added, apr_pool_t *pool) { struct dump_edit_baton *eb = edit_baton; @@ -199,15 +180,13 @@ make_dir_baton(const char *path, copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool); new_db->eb = eb; - new_db->parent_dir_baton = pb; new_db->pool = pool; new_db->repos_relpath = repos_relpath; new_db->copyfrom_path = copyfrom_path ? svn_relpath_canonicalize(copyfrom_path, pool) : NULL; new_db->copyfrom_rev = copyfrom_rev; - new_db->added = added; - new_db->written_out = FALSE; + new_db->headers = NULL; new_db->props = apr_hash_make(pool); new_db->deleted_props = apr_hash_make(pool); new_db->deleted_entries = apr_hash_make(pool); @@ -227,7 +206,6 @@ make_file_baton(const char *path, struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); new_fb->eb = pb->eb; - new_fb->parent_dir_baton = pb; new_fb->pool = pool; new_fb->repos_relpath = svn_relpath_canonicalize(path, pool); new_fb->props = apr_hash_make(pool); @@ -240,9 +218,11 @@ make_file_baton(const char *path, return new_fb; } -/* Return in *HEADER and *CONTENT the headers and content for PROPS. */ +/* Append to HEADERS the required headers, and set *CONTENT to the property + * content section, to represent the property delta of PROPS/DELETED_PROPS. + */ static svn_error_t * -get_props_content(svn_stringbuf_t **header, +get_props_content(svn_repos__dumpfile_headers_t *headers, svn_stringbuf_t **content, apr_hash_t *props, apr_hash_t *deleted_props, @@ -251,10 +231,8 @@ get_props_content(svn_stringbuf_t **header, { svn_stream_t *content_stream; apr_hash_t *normal_props; - const char *buf; *content = svn_stringbuf_create_empty(result_pool); - *header = svn_stringbuf_create_empty(result_pool); content_stream = svn_stream_from_stringbuf(*content, scratch_pool); @@ -265,101 +243,63 @@ get_props_content(svn_stringbuf_t **header, SVN_ERR(svn_stream_close(content_stream)); /* Prop-delta: true */ - *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA - ": true\n"); - - /* Prop-content-length: 193 */ - buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", (*content)->len); - svn_stringbuf_appendcstr(*header, buf); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true"); return SVN_NO_ERROR; } -/* Extract and dump properties stored in PROPS and property deletions - * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to - * FALSE. +/* A special case of dump_node(), for a delete record. * - * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing - * the content block of the property changes; otherwise, dump that to - * the stream, too. + * The only thing special about this version is it only writes one blank + * line, not two, after the headers. Why? Historical precedent for the + * case where a delete record is used as part of a (delete + add-with-history) + * in implementing a replacement. */ static svn_error_t * -do_dump_props(svn_stringbuf_t **propstring, - svn_stream_t *stream, - apr_hash_t *props, - apr_hash_t *deleted_props, - svn_boolean_t *trigger_var, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +dump_node_delete(svn_stream_t *stream, + const char *node_relpath, + apr_pool_t *pool) { - svn_stringbuf_t *header; - svn_stringbuf_t *content; - apr_size_t len; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); - if (trigger_var && !*trigger_var) - return SVN_NO_ERROR; - - SVN_ERR(get_props_content(&header, &content, props, deleted_props, - result_pool, scratch_pool)); - len = header->len; - SVN_ERR(svn_stream_write(stream, header->data, &len)); + assert(svn_relpath_is_canonical(node_relpath)); - if (propstring) - { - *propstring = content; - } - else - { - /* Content-length: 14 */ - SVN_ERR(svn_stream_printf(stream, scratch_pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - content->len)); - - len = content->len; - SVN_ERR(svn_stream_write(stream, content->data, &len)); - - /* No text is going to be dumped. Write a couple of newlines and - wait for the next node/ revision. */ - SVN_ERR(svn_stream_puts(stream, "\n\n")); - - /* Cleanup so that data is never dumped twice. */ - apr_hash_clear(props); - apr_hash_clear(deleted_props); - if (trigger_var) - *trigger_var = FALSE; - } + /* Node-path: ... */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); - return SVN_NO_ERROR; -} + /* Node-action: delete */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); -static svn_error_t * -do_dump_newlines(struct dump_edit_baton *eb, - svn_boolean_t *trigger_var, - apr_pool_t *pool) -{ - if (trigger_var && *trigger_var) - { - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - *trigger_var = FALSE; - } + SVN_ERR(svn_repos__dump_node_record(stream, headers, + NULL, FALSE, 0, /* props & text */ + FALSE /*content_length_always*/, pool)); return SVN_NO_ERROR; } -/* - * Write out a node record for PATH of type KIND under EB->FS_ROOT. +/* Set *HEADERS_P to contain some headers for the node at PATH of type KIND. + * * ACTION describes what is happening to the node (see enum - * svn_node_action). Write record to writable EB->STREAM, using - * EB->BUFFER to write in chunks. + * svn_node_action). * * If the node was itself copied, IS_COPY is TRUE and the * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV. * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this * node is part of a copied subtree. + * + * Iff ACTION is svn_node_action_replace and IS_COPY, then first write a + * complete deletion record to the dump stream. + * + * If ACTION is svn_node_action_delete, then the node record will be + * complete. (The caller may want to write two blank lines after the + * header block.) */ static svn_error_t * -dump_node(struct dump_edit_baton *eb, +dump_node(svn_repos__dumpfile_headers_t **headers_p, + struct dump_edit_baton *eb, const char *repos_relpath, struct dir_baton *db, struct file_baton *fb, @@ -370,6 +310,8 @@ dump_node(struct dump_edit_baton *eb, apr_pool_t *pool) { const char *node_relpath = repos_relpath; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); assert(svn_relpath_is_canonical(repos_relpath)); assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path)); @@ -381,17 +323,16 @@ dump_node(struct dump_edit_baton *eb, node_relpath, pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - node_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); /* Node-kind: "file" | "dir" */ if (fb) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file"); else if (db) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Write the appropriate Node-action header */ @@ -403,26 +344,22 @@ dump_node(struct dump_edit_baton *eb, do here but print node action information. Node-action: change. */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change"); + break; + + case svn_node_action_delete: + /* Node-action: delete */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); break; case svn_node_action_replace: - if (is_copy) - { - /* Delete the original, and then re-add the replacement as a - copy using recursive calls into this function. */ - SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete, - FALSE, NULL, SVN_INVALID_REVNUM, pool)); - SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add, - is_copy, copyfrom_path, copyfrom_rev, pool)); - } - else + if (! is_copy) { /* Node-action: replace */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION - ": replace\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace"); /* Wait for a change_*_prop to be called before dumping anything */ @@ -430,45 +367,36 @@ dump_node(struct dump_edit_baton *eb, fb->dump_props = TRUE; else if (db) db->dump_props = TRUE; + break; } - break; - - case svn_node_action_delete: - /* Node-action: delete */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); + else + { + /* More complex case: is_copy is true, and copyfrom_path/ + copyfrom_rev are present: delete the original, and then re-add + it */ + /* ### Why not write a 'replace' record? Don't know. */ - /* We can leave this routine quietly now. Nothing more to do- - print a couple of newlines because we're not dumping props or - text. */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); + /* ### Unusually, we end this 'delete' node record with only a single + blank line after the header block -- no extra blank line. */ + SVN_ERR(dump_node_delete(eb->stream, repos_relpath, pool)); - break; + /* The remaining action is a non-replacing add-with-history */ + /* action = svn_node_action_add; */ + } + /* FALL THROUGH to 'add' */ case svn_node_action_add: /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); if (is_copy) { /* Node-copyfrom-rev / Node-copyfrom-path */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV - ": %ld\n" - SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH - ": %s\n", - copyfrom_rev, copyfrom_path)); - - /* Ugly hack: If a directory was copied from a previous - revision, nothing like close_file() will be called to write two - blank lines. If change_dir_prop() is called, props are dumped - (along with the necessary PROPS-END\n\n and we're good. So - set DUMP_NEWLINES here to print the newlines unless - change_dir_prop() is called next otherwise the `svnadmin load` - parser will fail. */ - if (db) - db->dump_newlines = TRUE; + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", copyfrom_rev); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path); } else { @@ -496,6 +424,11 @@ dump_node(struct dump_edit_baton *eb, break; } + + /* Return the headers so far. We don't necessarily have all the headers + yet -- there may be property-related and content length headers to + come, if this was not a 'delete' record. */ + *headers_p = headers; return SVN_NO_ERROR; } @@ -504,35 +437,29 @@ dump_mkdir(struct dump_edit_baton *eb, const char *repos_relpath, apr_pool_t *pool) { - svn_stringbuf_t *prop_header, *prop_content; - apr_size_t len; - const char *buf; + svn_stringbuf_t *prop_content; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - repos_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, repos_relpath); /* Node-kind: dir */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); /* Dump the (empty) property block. */ - SVN_ERR(get_props_content(&prop_header, &prop_content, + SVN_ERR(get_props_content(headers, &prop_content, apr_hash_make(pool), apr_hash_make(pool), pool, pool)); - len = prop_header->len; - SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len)); - len = prop_content->len; - buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", len); - SVN_ERR(svn_stream_puts(eb->stream, buf)); - SVN_ERR(svn_stream_puts(eb->stream, "\n")); - SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len)); + SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, prop_content, + FALSE, 0, FALSE /*content_length_always*/, + pool)); /* Newlines to tie it all off. */ SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); @@ -540,40 +467,43 @@ dump_mkdir(struct dump_edit_baton *eb, return SVN_NO_ERROR; } -/* Dump pending items from the specified node, to allow starting the dump - of a child node */ +/* Dump pending headers and properties for the directory EB->pending_db (if + * not null), to allow starting the dump of a child node */ static svn_error_t * -dump_pending(struct dump_edit_baton *eb, - apr_pool_t *scratch_pool) +dump_pending_dir(struct dump_edit_baton *eb, + apr_pool_t *scratch_pool) { - if (! eb->pending_baton) + struct dir_baton *db = eb->pending_db; + svn_stringbuf_t *prop_content = NULL; + + if (! db) return SVN_NO_ERROR; - if (eb->pending_kind == svn_node_dir) + /* Some pending properties to dump? */ + if (db->dump_props) { - struct dir_baton *db = eb->pending_baton; + SVN_ERR(get_props_content(db->headers, &prop_content, + db->props, db->deleted_props, + scratch_pool, scratch_pool)); + } + SVN_ERR(svn_repos__dump_node_record(eb->stream, db->headers, prop_content, + FALSE, 0, FALSE /*content_length_always*/, + scratch_pool)); - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props, - &(db->dump_props), db->pool, scratch_pool)); + /* No text is going to be dumped. Write a couple of newlines and + wait for the next node/ revision. */ + SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - /* Some pending newlines to dump? */ - SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool)); - } - else if (eb->pending_kind == svn_node_file) + if (db->dump_props) { - struct file_baton *fb = eb->pending_baton; - - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), fb->pool, scratch_pool)); + /* Cleanup so that data is never dumped twice. */ + apr_hash_clear(db->props); + apr_hash_clear(db->deleted_props); + db->dump_props = FALSE; } - else - abort(); /* Anything that was pending is pending no longer. */ - eb->pending_baton = NULL; - eb->pending_kind = svn_node_none; + eb->pending_db = NULL; return SVN_NO_ERROR; } @@ -594,8 +524,6 @@ open_root(void *edit_baton, /* Clear the per-revision pool after each revision */ svn_pool_clear(eb->pool); - LDR_DBG(("open_root %p\n", *root_baton)); - if (eb->update_anchor_relpath) { int i; @@ -628,16 +556,15 @@ open_root(void *edit_baton, /* ... but for the source directory itself, we'll defer to letting the typical plumbing handle this task. */ new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, TRUE, pool); - SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db, + edit_baton, NULL, pool); + SVN_ERR(dump_node(&new_db->headers, + eb, new_db->repos_relpath, new_db, NULL, svn_node_action_add, FALSE, NULL, SVN_INVALID_REVNUM, pool)); /* Remember that we've started but not yet finished handling this directory. */ - new_db->written_out = TRUE; - eb->pending_baton = new_db; - eb->pending_kind = svn_node_dir; + eb->pending_db = new_db; } } svn_pool_destroy(iterpool); @@ -646,7 +573,7 @@ open_root(void *edit_baton, if (! new_db) { new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, FALSE, pool); + edit_baton, NULL, pool); } *root_baton = new_db; @@ -661,15 +588,13 @@ delete_entry(const char *path, { struct dir_baton *pb = parent_baton; - LDR_DBG(("delete_entry %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* We don't dump this deletion immediate. Rather, we add this path to the deleted_entries of the parent directory baton. That way, we can tell (later) an addition from a replacement. All the real deletions get handled in close_directory(). */ - svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path), pb); + svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->pool, path), pb); return SVN_NO_ERROR; } @@ -683,40 +608,37 @@ add_directory(const char *path, void **child_baton) { struct dir_baton *pb = parent_baton; - void *val; + void *was_deleted; struct dir_baton *new_db; svn_boolean_t is_copy; - LDR_DBG(("add_directory %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, - pb, TRUE, pb->eb->pool); + pb, pb->pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect an add-with-history */ is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); /* Dump the node */ - SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL, - val ? svn_node_action_replace : svn_node_action_add, + SVN_ERR(dump_node(&new_db->headers, + pb->eb, new_db->repos_relpath, new_db, NULL, + was_deleted ? svn_node_action_replace : svn_node_action_add, is_copy, is_copy ? new_db->copyfrom_path : NULL, is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, pool)); - if (val) + if (was_deleted) /* Delete the path, it's now been dumped */ svn_hash_sets(pb->deleted_entries, path, NULL); /* Remember that we've started, but not yet finished handling this directory. */ - new_db->written_out = TRUE; - pb->eb->pending_baton = new_db; - pb->eb->pending_kind = svn_node_dir; + pb->eb->pending_db = new_db; *child_baton = new_db; return SVN_NO_ERROR; @@ -734,9 +656,7 @@ open_directory(const char *path, const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; - LDR_DBG(("open_directory %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* If the parent directory has explicit comparison path and rev, record the same for this one. */ @@ -744,12 +664,12 @@ open_directory(const char *path, { copyfrom_path = svn_relpath_join(pb->copyfrom_path, svn_relpath_basename(path, NULL), - pb->eb->pool); + pb->pool); copyfrom_rev = pb->copyfrom_rev; } new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, - FALSE, pb->eb->pool); + pb->pool); *child_baton = new_db; return SVN_NO_ERROR; @@ -763,12 +683,10 @@ close_directory(void *dir_baton, apr_hash_index_t *hi; svn_boolean_t this_pending; - LDR_DBG(("close_directory %p\n", dir_baton)); - /* Remember if this directory is the one currently pending. */ - this_pending = (db->eb->pending_baton == db); + this_pending = (db->eb->pending_db == db); - SVN_ERR(dump_pending(db->eb, pool)); + SVN_ERR(dump_pending_dir(db->eb, pool)); /* If this directory was pending, then dump_pending() should have taken care of all the props and such. Of course, the only way @@ -781,22 +699,23 @@ close_directory(void *dir_baton, directory. */ if ((! this_pending) && (db->dump_props)) { - SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL, + SVN_ERR(dump_node(&db->headers, + db->eb, db->repos_relpath, db, NULL, svn_node_action_change, FALSE, NULL, SVN_INVALID_REVNUM, pool)); - db->eb->pending_baton = db; - db->eb->pending_kind = svn_node_dir; - SVN_ERR(dump_pending(db->eb, pool)); + db->eb->pending_db = db; + SVN_ERR(dump_pending_dir(db->eb, pool)); } /* Dump the deleted directory entries */ for (hi = apr_hash_first(pool, db->deleted_entries); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); - SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete, - FALSE, NULL, SVN_INVALID_REVNUM, pool)); + SVN_ERR(dump_node_delete(db->eb->stream, path, pool)); + /* This deletion record is complete -- write an extra newline */ + SVN_ERR(svn_stream_puts(db->eb->stream, "\n")); } /* ### should be unnecessary */ @@ -815,17 +734,15 @@ add_file(const char *path, { struct dir_baton *pb = parent_baton; struct file_baton *fb; - void *val; + void *was_deleted; - LDR_DBG(("add_file %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* Make the file baton. */ fb = make_file_baton(path, pb, pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect add-with-history. */ if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev)) @@ -834,10 +751,10 @@ add_file(const char *path, fb->copyfrom_rev = copyfrom_rev; fb->is_copy = TRUE; } - fb->action = val ? svn_node_action_replace : svn_node_action_add; + fb->action = was_deleted ? svn_node_action_replace : svn_node_action_add; /* Delete the path, it's now been dumped. */ - if (val) + if (was_deleted) svn_hash_sets(pb->deleted_entries, path, NULL); *file_baton = fb; @@ -854,9 +771,7 @@ open_file(const char *path, struct dir_baton *pb = parent_baton; struct file_baton *fb; - LDR_DBG(("open_file %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* Make the file baton. */ fb = make_file_baton(path, pb, pool); @@ -867,7 +782,7 @@ open_file(const char *path, { fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path, svn_relpath_basename(path, NULL), - pb->eb->pool); + pb->pool); fb->copyfrom_rev = pb->copyfrom_rev; } @@ -884,13 +799,11 @@ change_dir_prop(void *parent_baton, struct dir_baton *db = parent_baton; svn_boolean_t this_pending; - LDR_DBG(("change_dir_prop %p\n", parent_baton)); - /* This directory is not pending, but something else is, so handle the "something else". */ - this_pending = (db->eb->pending_baton == db); + this_pending = (db->eb->pending_db == db); if (! this_pending) - SVN_ERR(dump_pending(db->eb, pool)); + SVN_ERR(dump_pending_dir(db->eb, pool)); if (svn_property_kind2(name) != svn_prop_regular_kind) return SVN_NO_ERROR; @@ -902,9 +815,7 @@ change_dir_prop(void *parent_baton, else svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), ""); - /* Make sure we eventually output the props, and disable printing - a couple of extra newlines */ - db->dump_newlines = FALSE; + /* Make sure we eventually output the props */ db->dump_props = TRUE; return SVN_NO_ERROR; @@ -918,8 +829,6 @@ change_file_prop(void *file_baton, { struct file_baton *fb = file_baton; - LDR_DBG(("change_file_prop %p\n", file_baton)); - if (svn_property_kind2(name) != svn_prop_regular_kind) return SVN_NO_ERROR; @@ -938,22 +847,6 @@ change_file_prop(void *file_baton, return SVN_NO_ERROR; } -static svn_error_t * -window_handler(svn_txdelta_window_t *window, void *baton) -{ - struct handler_baton *hb = baton; - static svn_error_t *err; - - err = hb->apply_handler(window, hb->apply_baton); - if (window != NULL && !err) - return SVN_NO_ERROR; - - if (err) - SVN_ERR(err); - - return SVN_NO_ERROR; -} - static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, @@ -962,31 +855,19 @@ apply_textdelta(void *file_baton, const char *base_checksum, { struct file_baton *fb = file_baton; struct dump_edit_baton *eb = fb->eb; - struct handler_baton *hb; svn_stream_t *delta_filestream; - LDR_DBG(("apply_textdelta %p\n", file_baton)); - - /* This is custom handler_baton, allocated from a separate pool. */ - hb = apr_pcalloc(eb->pool, sizeof(*hb)); - /* Use a temporary file to measure the Text-content-length */ delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); /* Prepare to write the delta to the delta_filestream */ - svn_txdelta_to_svndiff3(&(hb->apply_handler), &(hb->apply_baton), + svn_txdelta_to_svndiff3(handler, handler_baton, delta_filestream, 0, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); /* Record that there's text to be dumped, and its base checksum. */ fb->dump_text = TRUE; - fb->base_checksum = apr_pstrdup(eb->pool, base_checksum); - - /* The actual writing takes place when this function has - finished. Set handler and handler_baton now so for - window_handler() */ - *handler = window_handler; - *handler_baton = hb; + fb->base_checksum = apr_pstrdup(fb->pool, base_checksum); return SVN_NO_ERROR; } @@ -999,22 +880,25 @@ close_file(void *file_baton, struct file_baton *fb = file_baton; struct dump_edit_baton *eb = fb->eb; apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t)); - svn_stringbuf_t *propstring; + svn_stringbuf_t *propstring = NULL; + svn_repos__dumpfile_headers_t *headers; - LDR_DBG(("close_file %p\n", file_baton)); + SVN_ERR(dump_pending_dir(eb, pool)); - SVN_ERR(dump_pending(eb, pool)); - - /* Dump the node. */ - SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb, + /* Start dumping this node, by collecting some basic headers for it. */ + SVN_ERR(dump_node(&headers, eb, fb->repos_relpath, NULL, fb, fb->action, fb->is_copy, fb->copyfrom_path, fb->copyfrom_rev, pool)); /* Some pending properties to dump? We'll dump just the headers for now, then dump the actual propchange content only after dumping the text headers too (if present). */ - SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), pool, pool)); + if (fb->dump_props) + { + SVN_ERR(get_props_content(headers, &propstring, + fb->props, fb->deleted_props, + pool, pool)); + } /* Dump the text headers */ if (fb->dump_text) @@ -1022,9 +906,8 @@ close_file(void *file_baton, apr_status_t err; /* Text-delta: true */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_TEXT_DELTA - ": true\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true"); err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file); if (err) @@ -1032,43 +915,22 @@ close_file(void *file_baton, if (fb->base_checksum) /* Text-delta-base-md5: */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 - ": %s\n", - fb->base_checksum)); - - /* Text-content-length: 39 */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH - ": %lu\n", - (unsigned long)info->size)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, fb->base_checksum); /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 - ": %s\n", - text_checksum)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, text_checksum); } - /* Content-length: 1549 */ - /* If both text and props are absent, skip this header */ - if (fb->dump_props) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size + propstring->len)); - else if (fb->dump_text) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size)); - - /* Dump the props now */ + /* Dump the headers and props now */ + SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, propstring, + fb->dump_text, info->size, + FALSE /*content_length_always*/, + pool)); + if (fb->dump_props) { - SVN_ERR(svn_stream_write(eb->stream, propstring->data, - &(propstring->len))); - /* Cleanup */ fb->dump_props = FALSE; apr_hash_clear(fb->props); @@ -1235,7 +1097,7 @@ svn_rdump__get_dump_editor(const svn_delta_editor_t **editor, eb->ra_session = ra_session; eb->update_anchor_relpath = update_anchor_relpath; eb->current_revision = revision; - eb->pending_kind = svn_node_none; + eb->pending_db = NULL; /* Create a special per-revision pool */ eb->pool = svn_pool_create(pool); diff --git a/contrib/subversion/subversion/svnrdump/load_editor.c b/contrib/subversion/subversion/svnrdump/load_editor.c index c35a28965..15dac6e98 100644 --- a/contrib/subversion/subversion/svnrdump/load_editor.c +++ b/contrib/subversion/subversion/svnrdump/load_editor.c @@ -43,14 +43,7 @@ #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. */ @@ -68,9 +61,6 @@ struct parse_baton /* 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; @@ -93,17 +83,20 @@ struct parse_baton /* The oldest revision loaded from the dump stream, or SVN_INVALID_REVNUM if none have been loaded. */ svn_revnum_t oldest_dumpstream_rev; + + /* An hash containing specific revision properties to skip while + loading. */ + apr_hash_t *skip_revprops; }; /** * 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. + * 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 @@ -190,160 +183,6 @@ get_revision_mapping(apr_hash_t *rev_map, } -/* 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__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 * commit_callback(const svn_commit_info_t *commit_info, void *baton, @@ -564,8 +403,8 @@ new_revision_record(void **revision_baton, 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); + const char *hname = apr_hash_this_key(hi); + const char *hval = apr_hash_this_val(hi); if (strcmp(hname, SVN_REPOS_DUMPFILE_REVISION_NUMBER) == 0) rb->rev = atoi(hval); @@ -606,9 +445,6 @@ 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; } @@ -704,8 +540,6 @@ new_node_record(void **node_baton, rb->rev - rb->rev_offset - 1, rb->pool, &child_baton)); - LDR_DBG(("Opened root %p\n", child_baton)); - /* child_baton corresponds to the root directory baton here */ push_directory(rb, child_baton, "", TRUE /*is_added*/, NULL, SVN_INVALID_REVNUM); @@ -713,8 +547,8 @@ new_node_record(void **node_baton, 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); + const char *hname = apr_hash_this_key(hi); + const char *hval = apr_hash_this_val(hi); /* Parse the different kinds of headers we can encounter and stuff them into the node_baton for writing later */ @@ -741,6 +575,9 @@ new_node_record(void **node_baton, nb->copyfrom_path = apr_pstrdup(rb->pool, hval); } + /* Before handling the new node, ensure depth-first editing order by + traversing the directory hierarchy from the old node's to the new + node's parent directory. */ nb_dirname = svn_relpath_dirname(nb->path, pool); if (svn_path_compare_paths(nb_dirname, rb->db->relpath) != 0) @@ -751,9 +588,6 @@ new_node_record(void **node_baton, 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); @@ -771,7 +605,6 @@ new_node_record(void **node_baton, /* 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; } @@ -786,7 +619,6 @@ new_node_record(void **node_baton, rb->db->baton, rb->rev - rb->rev_offset - 1, rb->pool, &child_baton)); - LDR_DBG(("Opened dir %p\n", child_baton)); push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/, NULL, SVN_INVALID_REVNUM); } @@ -816,6 +648,7 @@ new_node_record(void **node_baton, if (rb->pb->parent_dir) nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, nb->copyfrom_path, rb->pool); + /* Convert to a URL, as the commit editor requires. */ nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url, nb->copyfrom_path, rb->pool); @@ -826,7 +659,6 @@ new_node_record(void **node_baton, { 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 - 1, rb->db->baton, rb->pool)); @@ -843,16 +675,12 @@ new_node_record(void **node_baton, 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_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)); push_directory(rb, child_baton, nb->path, TRUE /*is_added*/, nb->copyfrom_path, nb->copyfrom_rev); break; @@ -896,11 +724,13 @@ set_revision_property(void *baton, if (rb->rev > 0) { - svn_hash_sets(rb->revprop_table, - apr_pstrdup(rb->pool, name), - svn_string_dup(value, rb->pool)); + if (! svn_hash_gets(rb->pb->skip_revprops, name)) + svn_hash_sets(rb->revprop_table, + apr_pstrdup(rb->pool, name), + svn_string_dup(value, rb->pool)); } - else if (rb->rev_offset == -1) + else if (rb->rev_offset == -1 + && ! svn_hash_gets(rb->pb->skip_revprops, name)) { /* Special case: set revision 0 properties directly (which is safe because the commit_editor hasn't been created yet), but @@ -925,51 +755,30 @@ set_node_property(void *baton, const svn_string_t *value) { struct node_baton *nb = baton; + struct revision_baton *rb = nb->rb; + struct parse_baton *pb = rb->pb; 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")) + svn_string_t *new_value; + svn_error_t *err; + + err = svn_repos__adjust_mergeinfo_property(&new_value, value, + pb->parent_dir, + pb->rev_map, + pb->oldest_dumpstream_rev, + rb->rev_offset, + NULL, NULL, /*notify*/ + pool, pool); + if (err) { - 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? */ + return svn_error_quick_wrap(err, + _("Invalid svn:mergeinfo value")); } - /* 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; - } + value = new_value; } SVN_ERR(svn_rdump__normalize_prop(name, &value, pool)); @@ -978,7 +787,7 @@ set_node_property(void *baton, prop = apr_palloc(nb->rb->pool, sizeof (*prop)); prop->name = apr_pstrdup(pool, name); - prop->value = value ? svn_string_dup(value, pool) : NULL; + prop->value = svn_string_dup(value, pool); svn_hash_sets(nb->prop_changes, prop->name, prop); return SVN_NO_ERROR; @@ -1024,7 +833,6 @@ remove_node_props(void *baton) /* 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; } @@ -1033,8 +841,6 @@ remove_node_props(void *baton) { /* 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, @@ -1044,13 +850,11 @@ remove_node_props(void *baton) } 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) @@ -1071,7 +875,7 @@ remove_node_props(void *baton) for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); svn_prop_kind_t kind = svn_property_kind2(name); if (kind == svn_prop_regular_kind) @@ -1091,7 +895,6 @@ set_fulltext(svn_stream_t **stream, 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, @@ -1108,7 +911,6 @@ apply_textdelta(svn_txdelta_window_handler_t *handler, 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)); @@ -1126,8 +928,8 @@ close_node(void *baton) 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); + const char *name = apr_hash_this_key(hi); + svn_prop_t *prop = apr_hash_this_val(hi); switch (nb->kind) { @@ -1148,7 +950,6 @@ close_node(void *baton) 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)); } @@ -1178,12 +979,10 @@ close_revision(void *baton) 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)); } @@ -1201,8 +1000,6 @@ close_revision(void *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)); } @@ -1222,16 +1019,22 @@ close_revision(void *baton) 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)); + if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_DATE)) + { + 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)); + } + if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_AUTHOR)) + { + 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); @@ -1244,6 +1047,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, svn_ra_session_t *session, svn_ra_session_t *aux_session, svn_boolean_t quiet, + apr_hash_t *skip_revprops, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) @@ -1287,6 +1091,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, parse_baton->rev_map = apr_hash_make(pool); parse_baton->last_rev_mapped = SVN_INVALID_REVNUM; parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM; + parse_baton->skip_revprops = skip_revprops; err = svn_repos_parse_dumpstream3(stream, parser, parse_baton, FALSE, cancel_func, cancel_baton, pool); diff --git a/contrib/subversion/subversion/svnrdump/svnrdump.c b/contrib/subversion/subversion/svnrdump/svnrdump.c index 6bf409c7d..df0286bca 100644 --- a/contrib/subversion/subversion/svnrdump/svnrdump.c +++ b/contrib/subversion/subversion/svnrdump/svnrdump.c @@ -39,6 +39,7 @@ #include "svnrdump.h" +#include "private/svn_repos_private.h" #include "private/svn_cmdline_private.h" #include "private/svn_ra_private.h" @@ -80,9 +81,11 @@ enum svn_svnrdump__longopt_t opt_auth_password, opt_auth_nocache, opt_non_interactive, + opt_skip_revprop, opt_force_interactive, opt_incremental, opt_trust_server_cert, + opt_trust_server_cert_failures, opt_version }; @@ -92,6 +95,7 @@ enum svn_svnrdump__longopt_t opt_auth_password, \ opt_auth_nocache, \ opt_trust_server_cert, \ + opt_trust_server_cert_failures, \ opt_non_interactive, \ opt_force_interactive @@ -106,7 +110,7 @@ static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] = { "load", load_cmd, { 0 }, N_("usage: svnrdump load URL\n\n" "Load a 'dumpfile' given on stdin to a repository at remote URL.\n"), - { 'q', SVN_SVNRDUMP__BASE_OPTIONS } }, + { 'q', opt_skip_revprop, SVN_SVNRDUMP__BASE_OPTIONS } }, { "help", 0, { "?", "h" }, N_("usage: svnrdump help [SUBCOMMAND...]\n\n" "Describe the usage of this program or its subcommands.\n"), @@ -122,6 +126,8 @@ static const apr_getopt_option_t svnrdump__options[] = N_("no progress (only errors) to stderr")}, {"incremental", opt_incremental, 0, N_("dump incrementally")}, + {"skip-revprop", opt_skip_revprop, 1, + N_("skip revision property ARG (e.g., \"svn:author\")")}, {"config-dir", opt_config_dir, 1, N_("read user configuration files from directory ARG")}, {"username", opt_auth_username, 1, @@ -150,12 +156,24 @@ static const apr_getopt_option_t svnrdump__options[] = "For example:\n" " " " servers:global:http-library=serf")}, - {"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')") }, + {"trust-server-cert", opt_trust_server_cert, 0, + N_("deprecated; same as\n" + " " + "--trust-server-cert-failures=unknown-ca")}, + {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, + N_("with --non-interactive, accept SSL server\n" + " " + "certificates with failures; ARG is comma-separated\n" + " " + "list of 'unknown-ca' (Unknown Authority),\n" + " " + "'cn-mismatch' (Hostname mismatch), 'expired'\n" + " " + "(Expired certificate), 'not-yet-valid' (Not yet\n" + " " + "valid certificate) and 'other' (all other not\n" + " " + "separately classified certificate errors).")}, {0, 0, 0, 0} }; @@ -182,6 +200,7 @@ typedef struct opt_baton_t { svn_opt_revision_t end_revision; svn_boolean_t quiet; svn_boolean_t incremental; + apr_hash_t *skip_revprops; } opt_baton_t; /* Print dumpstream-formatted information about REVISION. @@ -197,38 +216,13 @@ replay_revstart(svn_revnum_t revision, { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, NULL, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, rb->stdout_stream, rb->extra_ra_session, @@ -271,38 +265,13 @@ replay_revstart_v2(svn_revnum_t revision, { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, rb->stdout_stream, @@ -348,7 +317,11 @@ init_client_context(svn_client_ctx_t **ctx_p, const char *config_dir, const char *repos_url, svn_boolean_t no_auth_cache, - svn_boolean_t trust_server_cert, + svn_boolean_t trust_unknown_ca, + svn_boolean_t trust_cn_mismatch, + svn_boolean_t trust_expired, + svn_boolean_t trust_not_yet_valid, + svn_boolean_t trust_other_failure, apr_array_header_t *config_options, apr_pool_t *pool) { @@ -412,11 +385,14 @@ init_client_context(svn_client_ctx_t **ctx_p, ctx->cancel_func = check_cancel; /* Default authentication providers for non-interactive use */ - SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), non_interactive, - username, password, config_dir, - no_auth_cache, trust_server_cert, - cfg_config, ctx->cancel_func, - ctx->cancel_baton, pool)); + SVN_ERR(svn_cmdline_create_auth_baton2(&(ctx->auth_baton), non_interactive, + username, password, config_dir, + no_auth_cache, trust_unknown_ca, + trust_cn_mismatch, trust_expired, + trust_not_yet_valid, + trust_other_failure, + cfg_config, ctx->cancel_func, + ctx->cancel_baton, pool)); *ctx_p = ctx; return SVN_NO_ERROR; } @@ -432,35 +408,12 @@ dump_revision_header(svn_ra_session_t *session, apr_pool_t *pool) { apr_hash_t *prophash; - svn_stringbuf_t *propstring; - svn_stream_t *propstream; - - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); - prophash = apr_hash_make(pool); - propstring = svn_stringbuf_create_empty(pool); SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); - - propstream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(propstream)); - - /* Property-content-length: 14; Content-length: 14 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", - propstring->len)); - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - propstring->len)); - /* The properties */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - + SVN_ERR(svn_repos__dump_revision_record(stdout_stream, revision, NULL, + prophash, + TRUE /*props_section_always*/, + pool)); return SVN_NO_ERROR; } @@ -608,6 +561,7 @@ load_revisions(svn_ra_session_t *session, svn_ra_session_t *aux_session, const char *url, svn_boolean_t quiet, + apr_hash_t *skip_revprops, apr_pool_t *pool) { apr_file_t *stdin_file; @@ -617,7 +571,8 @@ load_revisions(svn_ra_session_t *session, stdin_stream = svn_stream_from_aprfile2(stdin_file, FALSE, pool); SVN_ERR(svn_rdump__load_dumpstream(stdin_stream, session, aux_session, - quiet, check_cancel, NULL, pool)); + quiet, skip_revprops, + check_cancel, NULL, pool)); SVN_ERR(svn_stream_close(stdin_stream)); @@ -667,23 +622,6 @@ version(const char *progname, } -/* A statement macro, similar to @c SVN_ERR, but returns an integer. - * Evaluate @a expr. If it yields an error, handle that error and - * return @c EXIT_FAILURE. - */ -#define SVNRDUMP_ERR(expr) \ - do \ - { \ - svn_error_t *svn_err__temp = (expr); \ - if (svn_err__temp) \ - { \ - svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \ - svn_error_clear(svn_err__temp); \ - return EXIT_FAILURE; \ - } \ - } \ - while (0) - /* Handle the "dump" subcommand. Implements `svn_opt_subcommand_t'. */ static svn_error_t * dump_cmd(apr_getopt_t *os, @@ -718,7 +656,7 @@ load_cmd(apr_getopt_t *os, SVN_ERR(svn_client_open_ra_session2(&aux_session, opt_baton->url, NULL, opt_baton->ctx, pool, pool)); return load_revisions(opt_baton->session, aux_session, opt_baton->url, - opt_baton->quiet, pool); + opt_baton->quiet, opt_baton->skip_revprops, pool); } /* Handle the "help" subcommand. Implements `svn_opt_subcommand_t'. */ @@ -729,6 +667,7 @@ help_cmd(apr_getopt_t *os, { const char *header = _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n" + "Subversion remote repository dump and load tool.\n" "Type 'svnrdump help ' for help on a specific subcommand.\n" "Type 'svnrdump --version' to see the program version and RA modules.\n" "\n" @@ -831,19 +770,27 @@ validate_and_resolve_revisions(opt_baton_t *opt_baton, return SVN_NO_ERROR; } -int -main(int argc, const char **argv) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; const svn_opt_subcommand_desc2_t *subcommand = NULL; opt_baton_t *opt_baton; svn_revnum_t latest_revision = SVN_INVALID_REVNUM; - apr_pool_t *pool = NULL; const char *config_dir = NULL; const char *username = NULL; const char *password = NULL; svn_boolean_t no_auth_cache = FALSE; - svn_boolean_t trust_server_cert = FALSE; + svn_boolean_t trust_unknown_ca = FALSE; + svn_boolean_t trust_cn_mismatch = FALSE; + svn_boolean_t trust_expired = FALSE; + svn_boolean_t trust_not_yet_valid = FALSE; + svn_boolean_t trust_other_failure = FALSE; svn_boolean_t non_interactive = FALSE; svn_boolean_t force_interactive = FALSE; apr_array_header_t *config_options = NULL; @@ -852,20 +799,13 @@ main(int argc, const char **argv) apr_array_header_t *received_opts; int i; - if (svn_cmdline_init ("svnrdump", 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)); - opt_baton = apr_pcalloc(pool, sizeof(*opt_baton)); opt_baton->start_revision.kind = svn_opt_revision_unspecified; opt_baton->end_revision.kind = svn_opt_revision_unspecified; opt_baton->url = NULL; + opt_baton->skip_revprops = apr_hash_make(pool); - SVNRDUMP_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = TRUE; /* Options and arguments can be interleaved */ @@ -905,8 +845,9 @@ main(int argc, const char **argv) break; if (status != APR_SUCCESS) { - SVNRDUMP_ERR(usage(argv[0], pool)); - exit(EXIT_FAILURE); + SVN_ERR(usage(argv[0], pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -919,11 +860,10 @@ main(int argc, const char **argv) /* Make sure we've not seen -r already. */ if (opt_baton->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 svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments " + "encountered; try '-r N:M' instead " + "of '-r N -r M'")); } /* Parse the -r argument. */ if (svn_opt_parse_revision(&(opt_baton->start_revision), @@ -931,12 +871,10 @@ main(int argc, const char **argv) opt_arg, pool) != 0) { const char *utf8_opt_arg; - 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 svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Syntax error in revision " + "argument '%s'"), utf8_opt_arg); } } break; @@ -953,10 +891,10 @@ main(int argc, const char **argv) opt_baton->help = TRUE; break; case opt_auth_username: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); break; case opt_auth_password: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); break; case opt_auth_nocache: no_auth_cache = TRUE; @@ -970,8 +908,22 @@ main(int argc, const char **argv) case opt_incremental: opt_baton->incremental = TRUE; break; - case opt_trust_server_cert: - trust_server_cert = TRUE; + case opt_skip_revprop: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + svn_hash_sets(opt_baton->skip_revprops, opt_arg, opt_arg); + break; + case opt_trust_server_cert: /* backward compat */ + trust_unknown_ca = TRUE; + break; + case opt_trust_server_cert_failures: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &trust_unknown_ca, + &trust_cn_mismatch, + &trust_expired, + &trust_not_yet_valid, + &trust_other_failure, + opt_arg, pool)); break; case opt_config_option: if (!config_options) @@ -979,9 +931,11 @@ main(int argc, const char **argv) apr_array_make(pool, 1, sizeof(svn_cmdline__config_argument_t*)); - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); - SVNRDUMP_ERR(svn_cmdline__parse_config_option(config_options, - opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(config_options, + opt_arg, + "svnrdump: ", + pool)); } } @@ -989,10 +943,9 @@ main(int argc, const char **argv) * exclusive. */ if (non_interactive && force_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--non-interactive and --force-interactive " - "are mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); } if (opt_baton->help) @@ -1017,9 +970,9 @@ main(int argc, const char **argv) else { - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -1031,16 +984,15 @@ main(int argc, const char **argv) if (subcommand == NULL) { const char *first_arg_utf8; - err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + SVN_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)); - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -1065,69 +1017,64 @@ main(int argc, const char **argv) subcommand, pool); svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); + SVN_ERR(help_cmd(NULL, NULL, pool)); else svn_error_clear(svn_cmdline_fprintf( stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n" "Type 'svnrdump help %s' for usage.\n"), subcommand->name, optstr, subcommand->name)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } - if (subcommand && strcmp(subcommand->name, "--version") == 0) + if (strcmp(subcommand->name, "--version") == 0) { - SVNRDUMP_ERR(version(argv[0], opt_baton->quiet, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_ERR(version(argv[0], opt_baton->quiet, pool)); + return SVN_NO_ERROR; } - if (subcommand && strcmp(subcommand->name, "help") == 0) + if (strcmp(subcommand->name, "help") == 0) { - SVNRDUMP_ERR(help_cmd(os, opt_baton, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_ERR(help_cmd(os, opt_baton, pool)); + return SVN_NO_ERROR; } - /* --trust-server-cert can only be used with --non-interactive */ - if (trust_server_cert && !non_interactive) + /* --trust-* can only be used with --non-interactive */ + if (!non_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--trust-server-cert requires " - "--non-interactive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + if (trust_unknown_ca || trust_cn_mismatch || trust_expired + || trust_not_yet_valid || trust_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); } /* Expect one more non-option argument: the repository URL. */ if (os->ind != os->argc - 1) { - SVNRDUMP_ERR(usage(argv[0], pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(usage(argv[0], pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } else { const char *repos_url; - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&repos_url, - os->argv[os->ind], pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&repos_url, os->argv[os->ind], pool)); if (! svn_path_is_url(repos_url)) { - err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, - "Target '%s' is not a URL", - repos_url); - SVNRDUMP_ERR(err); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, + "Target '%s' is not a URL", + repos_url); } opt_baton->url = svn_uri_canonicalize(repos_url, pool); } if (strcmp(subcommand->name, "load") == 0) { - /* + /* * By default (no --*-interactive options given), the 'load' subcommand * is interactive unless username and password were provided on the * command line. This allows prompting for auth creds to work without @@ -1141,16 +1088,20 @@ main(int argc, const char **argv) non_interactive = !svn_cmdline__be_interactive(non_interactive, force_interactive); - SVNRDUMP_ERR(init_client_context(&(opt_baton->ctx), - non_interactive, - username, - password, - config_dir, - opt_baton->url, - no_auth_cache, - trust_server_cert, - config_options, - pool)); + SVN_ERR(init_client_context(&(opt_baton->ctx), + non_interactive, + username, + password, + config_dir, + opt_baton->url, + no_auth_cache, + trust_unknown_ca, + trust_cn_mismatch, + trust_expired, + trust_not_yet_valid, + trust_other_failure, + config_options, + pool)); err = svn_client_open_ra_session2(&(opt_baton->session), opt_baton->url, NULL, @@ -1171,15 +1122,45 @@ main(int argc, const char **argv) if (err && err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive) { - err = svn_error_quick_wrap(err, - _("Authentication failed and interactive" - " prompting is disabled; see the" - " --force-interactive option")); + return svn_error_quick_wrap(err, + _("Authentication failed and interactive" + " prompting is disabled; see the" + " --force-interactive option")); } + else if (err) + return err; + else + return SVN_NO_ERROR; +} - SVNRDUMP_ERR(err); +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; - svn_pool_destroy(pool); + /* Initialize the app. */ + if (svn_cmdline_init("svnrdump", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); - return EXIT_SUCCESS; + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnrdump: "); + } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/contrib/subversion/subversion/svnrdump/svnrdump.h b/contrib/subversion/subversion/svnrdump/svnrdump.h index 2a81014c8..919ea5e1e 100644 --- a/contrib/subversion/subversion/svnrdump/svnrdump.h +++ b/contrib/subversion/subversion/svnrdump/svnrdump.h @@ -89,6 +89,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, svn_ra_session_t *session, svn_ra_session_t *aux_session, svn_boolean_t quiet, + apr_hash_t *skip_revprops, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool); diff --git a/contrib/subversion/subversion/svnrdump/util.c b/contrib/subversion/subversion/svnrdump/util.c index 2586cd1db..9a89c8918 100644 --- a/contrib/subversion/subversion/svnrdump/util.c +++ b/contrib/subversion/subversion/svnrdump/util.c @@ -61,8 +61,8 @@ svn_rdump__normalize_props(apr_hash_t **normal_props, for (hi = apr_hash_first(result_pool, props); hi; hi = apr_hash_next(hi)) { - const char *key = svn__apr_hash_index_key(hi); - const svn_string_t *value = svn__apr_hash_index_val(hi); + const char *key = apr_hash_this_key(hi); + const svn_string_t *value = apr_hash_this_val(hi); SVN_ERR(svn_rdump__normalize_prop(key, &value, result_pool)); diff --git a/contrib/subversion/subversion/svnserve/cyrus_auth.c b/contrib/subversion/subversion/svnserve/cyrus_auth.c index 2d750473d..40f4228a9 100644 --- a/contrib/subversion/subversion/svnserve/cyrus_auth.c +++ b/contrib/subversion/subversion/svnserve/cyrus_auth.c @@ -74,6 +74,8 @@ static int canonicalize_username(sasl_conn_t *conn, { /* The only valid realm is user_realm (i.e. the repository's realm). If the user gave us another realm, complain. */ + if (realm_len != inlen-(pos-in+1)) + return SASL_BADPROT; if (strncmp(pos+1, user_realm, inlen-(pos-in+1)) != 0) return SASL_BADPROT; } @@ -177,16 +179,22 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?s)", &mech, &in)); if (strcmp(mech, "EXTERNAL") == 0 && !in) - in = svn_string_create(b->tunnel_user, pool); + in = svn_string_create(b->client_info->tunnel_user, pool); else if (in) in = svn_base64_decode_string(in, pool); /* For CRAM-MD5, we don't base64-encode stuff. */ use_base64 = (strcmp(mech, "CRAM-MD5") != 0); + /* sasl uses unsigned int for the length of strings, we use apr_size_t + * which may not be the same size. Deal with potential integer overflow */ + if (in && in->len > UINT_MAX) + return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("Initial token is too long")); + result = sasl_server_start(sasl_ctx, mech, in ? in->data : NULL, - in ? in->len : 0, &out, &outlen); + in ? (unsigned int) in->len : 0, &out, &outlen); if (result != SASL_OK && result != SASL_CONTINUE) return fail_auth(conn, pool, sasl_ctx); @@ -210,7 +218,13 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, in = item->u.string; if (use_base64) in = svn_base64_decode_string(in, pool); - result = sasl_server_step(sasl_ctx, in->data, in->len, &out, &outlen); + + if (in->len > UINT_MAX) + return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("Step response is too long")); + + result = sasl_server_step(sasl_ctx, in->data, (unsigned int) in->len, + &out, &outlen); } if (result != SASL_OK) @@ -246,7 +260,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *subpool; apr_status_t apr_err; const char *localaddrport = NULL, *remoteaddrport = NULL; - const char *mechlist, *val; + const char *mechlist; char hostname[APRMAXHOSTLEN + 1]; sasl_security_properties_t secprops; svn_boolean_t success, no_anonymous; @@ -265,7 +279,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, /* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol supports sending data along with the final "success" message. */ result = sasl_server_new(SVN_RA_SVN_SASL_NAME, - hostname, b->realm, + hostname, b->repository->realm, localaddrport, remoteaddrport, NULL, SASL_SUCCESS_DATA, &sasl_ctx); @@ -285,21 +299,12 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, svn_ra_svn__default_secprops(&secprops); /* Don't allow ANONYMOUS if a username is required. */ - no_anonymous = needs_username || get_access(b, UNAUTHENTICATED) < required; + no_anonymous = needs_username || b->repository->anon_access < required; if (no_anonymous) secprops.security_flags |= SASL_SEC_NOANONYMOUS; - svn_config_get(b->cfg, &val, - SVN_CONFIG_SECTION_SASL, - SVN_CONFIG_OPTION_MIN_SSF, - "0"); - SVN_ERR(svn_cstring_atoui(&secprops.min_ssf, val)); - - svn_config_get(b->cfg, &val, - SVN_CONFIG_SECTION_SASL, - SVN_CONFIG_OPTION_MAX_SSF, - "256"); - SVN_ERR(svn_cstring_atoui(&secprops.max_ssf, val)); + secprops.min_ssf = b->repository->min_ssf; + secprops.max_ssf = b->repository->max_ssf; /* Set security properties. */ result = sasl_setprop(sasl_ctx, SASL_SEC_PROPS, &secprops); @@ -307,8 +312,9 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, return fail_cmd(conn, pool, sasl_ctx); /* SASL needs to know if we are externally authenticated. */ - if (b->tunnel_user) - result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, b->tunnel_user); + if (b->client_info->tunnel_user) + result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, + b->client_info->tunnel_user); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); @@ -330,7 +336,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, /* Send the list of mechanisms and the realm to the client. */ SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(w)c", - mechlist, b->realm)); + mechlist, b->repository->realm)); /* The main authentication loop. */ subpool = svn_pool_create(pool); @@ -358,7 +364,8 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, if ((p = strchr(user, '@')) != NULL) { /* Drop the realm part. */ - b->user = apr_pstrndup(b->pool, user, p - (const char *)user); + b->client_info->user = apr_pstrndup(b->pool, user, + p - (const char *)user); } else { diff --git a/contrib/subversion/subversion/svnserve/logger.c b/contrib/subversion/subversion/svnserve/logger.c new file mode 100644 index 000000000..19b6bd48a --- /dev/null +++ b/contrib/subversion/subversion/svnserve/logger.c @@ -0,0 +1,161 @@ +/* + * logger.c : Implementation of the SvnServe logger API + * + * ==================================================================== + * 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 + +#include "svn_error.h" +#include "svn_io.h" +#include "svn_pools.h" +#include "svn_time.h" + +#include "private/svn_mutex.h" + +#include "svn_private_config.h" +#include "logger.h" + +#ifdef HAVE_UNISTD_H +#include /* For getpid() */ +#endif + +struct logger_t +{ + /* actual log file / stream object */ + svn_stream_t *stream; + + /* mutex used to serialize access to this structure */ + svn_mutex__t *mutex; + + /* private pool used for temporary allocations */ + apr_pool_t *pool; +}; + +svn_error_t * +logger__create_for_stderr(logger_t **logger, + apr_pool_t *pool) +{ + logger_t *result = apr_pcalloc(pool, sizeof(*result)); + result->pool = svn_pool_create(pool); + + SVN_ERR(svn_stream_for_stderr(&result->stream, pool)); + SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool)); + + *logger = result; + + return SVN_NO_ERROR; +} + +svn_error_t * +logger__create(logger_t **logger, + const char *filename, + apr_pool_t *pool) +{ + logger_t *result = apr_pcalloc(pool, sizeof(*result)); + apr_file_t *file; + + SVN_ERR(svn_io_file_open(&file, filename, + APR_WRITE | APR_CREATE | APR_APPEND, + APR_OS_DEFAULT, pool)); + SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool)); + + result->stream = svn_stream_from_aprfile2(file, FALSE, pool); + result->pool = svn_pool_create(pool); + + *logger = result; + + return SVN_NO_ERROR; +} + +void +logger__log_error(logger_t *logger, + svn_error_t *err, + repository_t *repository, + client_info_t *client_info) +{ + if (logger && err) + { + const char *timestr, *continuation; + const char *user, *repos, *remote_host; + char errbuf[256]; + /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */ + char errstr[8192]; + + svn_error_clear(svn_mutex__lock(logger->mutex)); + + timestr = svn_time_to_cstring(apr_time_now(), logger->pool); + remote_host = client_info && client_info->remote_host + ? client_info->remote_host + : "-"; + user = client_info && client_info->user + ? client_info->user + : "-"; + repos = repository && repository->repos_name + ? repository->repos_name + : "-"; + + continuation = ""; + while (err) + { + 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); + } + + memcpy(errstr + len, APR_EOL_STR, sizeof(APR_EOL_STR)); + len += sizeof(APR_EOL_STR) -1; /* add NL, ex terminating NUL */ + + svn_error_clear(svn_stream_write(logger->stream, errstr, &len)); + + continuation = "-"; + err = err->child; + } + + svn_pool_clear(logger->pool); + + svn_error_clear(svn_mutex__unlock(logger->mutex, SVN_NO_ERROR)); + } +} + +svn_error_t * +logger__write(logger_t *logger, + const char *errstr, + apr_size_t len) +{ + SVN_MUTEX__WITH_LOCK(logger->mutex, + svn_stream_write(logger->stream, errstr, &len)); + return SVN_NO_ERROR; +} diff --git a/contrib/subversion/subversion/svnserve/logger.h b/contrib/subversion/subversion/svnserve/logger.h new file mode 100644 index 000000000..aac804b31 --- /dev/null +++ b/contrib/subversion/subversion/svnserve/logger.h @@ -0,0 +1,79 @@ +/* + * logger.h : Public definitions for the Repository Cache + * + * ==================================================================== + * 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 LOGGER_H +#define LOGGER_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "server.h" + + + +/* Opaque svnserve log file writer data structure. Access to the log + * file will be serialized among threads within the same process. + */ +typedef struct logger_t logger_t; + +/* In POOL, create a writer object that will write log messages to stderr + * and return it in *LOGGER. The log file will not add any buffering + * on top of stderr. + */ +svn_error_t * +logger__create_for_stderr(logger_t **logger, + apr_pool_t *pool); + +/* In POOL, create a writer object for log file FILENAME and return it + * in *LOGGER. The log file will be flushed & closed when POOL gets + * cleared or destroyed. + */ +svn_error_t * +logger__create(logger_t **logger, + const char *filename, + apr_pool_t *pool); + +/* Write the first LEN bytes from ERRSTR to the log file managed by LOGGER. + */ +svn_error_t * +logger__write(logger_t *logger, + const char *errstr, + apr_size_t len); + +/* Write a description of ERR with additional information from REPOSITORY + * and CLIENT_INFO to the log file managed by LOGGER. REPOSITORY as well + * as CLIENT_INFO may be NULL. If either ERR or LOGGER are NULL, this + * becomes a no-op. + */ +void +logger__log_error(logger_t *logger, + svn_error_t *err, + repository_t *repository, + client_info_t *client_info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LOGGER_H */ diff --git a/contrib/subversion/subversion/svnserve/serve.c b/contrib/subversion/subversion/svnserve/serve.c index 23ef6ed1d..c66764bd0 100644 --- a/contrib/subversion/subversion/svnserve/serve.c +++ b/contrib/subversion/subversion/svnserve/serve.c @@ -61,6 +61,7 @@ #endif #include "server.h" +#include "logger.h" typedef struct commit_callback_baton_t { apr_pool_t *pool; @@ -96,7 +97,6 @@ typedef struct 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 { @@ -104,81 +104,23 @@ typedef struct authz_baton_t { 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. */ +/* svn_error_create() a new error, log_server_error() it, and + return it. */ 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(svn_error_t *err, server_baton_t *server) { - log_error(err, server->log_file, svn_ra_svn_conn_remote_host(conn), - server->user, server->repos_name, pool); + logger__log_error(server->logger, err, server->repository, + server->client_info); } /* 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) + const char *message, server_baton_t *server) { svn_error_t *err = svn_error_create(apr_err, child, message); - log_server_error(err, server, conn, pool); + log_error(err, server); return err; } @@ -190,7 +132,7 @@ log_fail_and_flush(svn_error_t *err, server_baton_t *server, { svn_error_t *io_err; - log_server_error(err, server, conn, pool); + log_error(err, server); io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); svn_error_clear(err); SVN_ERR(io_err); @@ -207,7 +149,7 @@ static svn_error_t *log_command(server_baton_t *b, va_list ap; apr_size_t nbytes; - if (b->log_file == NULL) + if (b->logger == NULL) return SVN_NO_ERROR; remote_host = svn_ra_svn_conn_remote_host(conn); @@ -221,10 +163,11 @@ static svn_error_t *log_command(server_baton_t *b, " %s %s %s %s %s" APR_EOL_STR, getpid(), timestr, (remote_host ? remote_host : "-"), - (b->user ? b->user : "-"), b->repos_name, log); + (b->client_info->user ? b->client_info->user : "-"), + b->repository->repos_name, log); nbytes = strlen(line); - return log_write(b->log_file, line, nbytes, pool); + return logger__write(b->logger, line, nbytes); } /* Log an authz failure */ @@ -232,56 +175,59 @@ 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) + if (!b->logger) return SVN_NO_ERROR; - if (!b->user) + if (!b->client_info || !b->client_info->user) return SVN_NO_ERROR; timestr = svn_time_to_cstring(apr_time_now(), pool); - remote_host = svn_ra_svn_conn_remote_host(conn); + remote_host = b->client_info->remote_host; 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, + b->client_info->user, + b->repository->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); + return logger__write(b->logger, line, strlen(line)); } - -svn_error_t *load_pwdb_config(server_baton_t *server, - svn_ra_svn_conn_t *conn, - apr_pool_t *pool) +/* If CFG specifies a path to the password DB, read that DB through + * CONFIG_POOL and store it in REPOSITORY->PWDB. + */ +static svn_error_t * +load_pwdb_config(repository_t *repository, + svn_config_t *cfg, + svn_repos__config_pool_t *config_pool, + 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_get(cfg, &pwdb_path, + SVN_CONFIG_SECTION_GENERAL, SVN_CONFIG_OPTION_PASSWORD_DB, NULL); - server->pwdb = NULL; + repository->pwdb = NULL; if (pwdb_path) { pwdb_path = svn_dirent_internal_style(pwdb_path, pool); - pwdb_path = svn_dirent_join(server->base, pwdb_path, pool); + pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool); - err = svn_config_read3(&server->pwdb, pwdb_path, TRUE, - FALSE, FALSE, pool); + err = svn_repos__config_pool_get(&repository->pwdb, NULL, config_pool, + pwdb_path, TRUE, FALSE, + repository->repos, 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 @@ -294,11 +240,7 @@ svn_error_t *load_pwdb_config(server_baton_t *server, 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); + return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL); } else /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */ @@ -310,11 +252,11 @@ svn_error_t *load_pwdb_config(server_baton_t *server, } /* Canonicalize *ACCESS_FILE based on the type of argument. Results are - * placed in *ACCESS_FILE. SERVER baton is used to convert relative paths to + * placed in *ACCESS_FILE. REPOSITORY 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, +canonicalize_access_file(const char **access_file, repository_t *repository, const char *repos_root, apr_pool_t *pool) { if (svn_path_is_url(*access_file)) @@ -334,26 +276,33 @@ canonicalize_access_file(const char **access_file, server_baton_t *server, else { *access_file = svn_dirent_internal_style(*access_file, pool); - *access_file = svn_dirent_join(server->base, *access_file, pool); + *access_file = svn_dirent_join(repository->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) +/* Load the authz database for the listening server through AUTHZ_POOL + based on the entries in the SERVER struct. + + SERVER and CONN must not be NULL. The real errors will be logged with + SERVER and CONN but return generic errors to the client. */ +static svn_error_t * +load_authz_config(repository_t *repository, + const char *repos_root, + svn_config_t *cfg, + svn_repos__authz_pool_t *authz_pool, + 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_get(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_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL, SVN_CONFIG_OPTION_GROUPS_DB, NULL); if (authzdb_path) @@ -361,48 +310,71 @@ svn_error_t *load_authz_config(server_baton_t *server, const char *case_force_val; /* Canonicalize and add the base onto the authzdb_path (if needed). */ - err = canonicalize_access_file(&authzdb_path, server, + err = canonicalize_access_file(&authzdb_path, repository, repos_root, pool); /* Same for the groupsdb_path if it is present. */ if (groupsdb_path && !err) - err = canonicalize_access_file(&groupsdb_path, server, + err = canonicalize_access_file(&groupsdb_path, repository, repos_root, pool); if (!err) - err = svn_repos_authz_read2(&server->authzdb, authzdb_path, - groupsdb_path, TRUE, pool); + err = svn_repos__authz_pool_get(&repository->authzdb, authz_pool, + authzdb_path, groupsdb_path, TRUE, + repository->repos, 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); - } + return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, 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_get(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; + repository->username_case = CASE_FORCE_UPPER; else if (strcmp(case_force_val, "lower") == 0) - server->username_case = CASE_FORCE_LOWER; + repository->username_case = CASE_FORCE_LOWER; else - server->username_case = CASE_ASIS; + repository->username_case = CASE_ASIS; } } else { - server->authzdb = NULL; - server->username_case = CASE_ASIS; + repository->authzdb = NULL; + repository->username_case = CASE_ASIS; } return SVN_NO_ERROR; } +/* If ERROR is a AUTH* error as returned by load_pwdb_config or + * load_authz_config, write it to SERVER's log file. + * Return a sanitized version of ERROR. + */ +static svn_error_t * +handle_config_error(svn_error_t *error, + server_baton_t *server) +{ + if ( error + && ( error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG + || error->apr_err == SVN_ERR_AUTHN_FAILED)) + { + apr_status_t apr_err = error->apr_err; + log_error(error, server); + + /* 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(error); + return svn_error_create(apr_err, NULL, NULL); + } + + return 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). @@ -450,14 +422,16 @@ 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) { + repository_t *repository = b->repository; + client_info_t *client_info = b->client_info; + /* 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) + if (!repository->authzdb) { *allowed = TRUE; return SVN_NO_ERROR; @@ -475,21 +449,23 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed, /* 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)) + if (client_info->user && (! client_info->authz_user)) { - char *authz_user = apr_pstrdup(b->pool, b->user); - if (b->username_case == CASE_FORCE_UPPER) + char *authz_user = apr_pstrdup(b->pool, client_info->user); + if (repository->username_case == CASE_FORCE_UPPER) convert_case(authz_user, TRUE); - else if (b->username_case == CASE_FORCE_LOWER) + else if (repository->username_case == CASE_FORCE_LOWER) convert_case(authz_user, FALSE); - b->authz_user = authz_user; + + client_info->authz_user = authz_user; } - SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name, - path, b->authz_user, required, - allowed, pool)); + SVN_ERR(svn_repos_authz_check_access(repository->authzdb, + repository->authz_repos_name, + path, client_info->authz_user, + required, allowed, pool)); if (!*allowed) - SVN_ERR(log_authz_denied(path, required, b, conn, pool)); + SVN_ERR(log_authz_denied(path, required, b, pool)); return SVN_NO_ERROR; } @@ -507,14 +483,14 @@ static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed, authz_baton_t *sb = baton; return authz_check_access(allowed, path, svn_authz_read, - sb->server, sb->conn, pool); + sb->server, 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) + if (baton->repository->authzdb) return authz_check_access_cb; return NULL; } @@ -533,27 +509,50 @@ static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required, { authz_baton_t *sb = baton; - return authz_check_access(allowed, path, required, - sb->server, sb->conn, pool); + return authz_check_access(allowed, path, required, sb->server, pool); } - -enum access_type get_access(server_baton_t *b, enum authn_type auth) +/* Return the access level specified for OPTION in CFG. If no such + * setting exists, use DEF. If READ_ONLY is set, unconditionally disable + * write access. + */ +static enum access_type +get_access(svn_config_t *cfg, + const char *option, + const char *def, + svn_boolean_t read_only) { - 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; + const char *val; - svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_GENERAL, var, def); + svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, 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; + + return result == WRITE_ACCESS && read_only ? READ_ACCESS : result; } -static enum access_type current_access(server_baton_t *b) +/* Set the *_ACCESS members in REPOSITORY according to the settings in + * CFG. If READ_ONLY is set, unconditionally disable write access. + */ +static void +set_access(repository_t *repository, + svn_config_t *cfg, + svn_boolean_t read_only) { - return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED); + repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS, + "write", read_only); + repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS, + "read", read_only); +} + +/* Return the access level for the user in B. + */ +static enum access_type +current_access(server_baton_t *b) +{ + return b->client_info->user ? b->repository->auth_access + : b->repository->anon_access; } /* Send authentication mechs for ACCESS_TYPE to the client. If NEEDS_USERNAME @@ -563,11 +562,11 @@ 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) + if (!needs_username && b->repository->anon_access >= required) SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS")); - if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required) + if (b->client_info->tunnel_user && b->repository->auth_access >= required) SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL")); - if (b->pwdb && get_access(b, AUTHENTICATED) >= required) + if (b->repository->pwdb && b->repository->auth_access >= required) SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5")); return SVN_NO_ERROR; } @@ -607,15 +606,15 @@ 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) + if (!b->client_info->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)); + SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool)); + SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access)); cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton)); cleanup_baton->pool = pool; - cleanup_baton->fs = b->fs; + cleanup_baton->fs = b->repository->fs; apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access, apr_pool_cleanup_null); @@ -637,19 +636,19 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *user; *success = FALSE; - if (get_access(b, AUTHENTICATED) >= required - && b->tunnel_user && strcmp(mech, "EXTERNAL") == 0) + if (b->repository->auth_access >= required + && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0) { - if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0) + if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0) return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", "Requested username does not match"); - b->user = b->tunnel_user; + b->client_info->user = b->client_info->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 + if (b->repository->anon_access >= required && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) { SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); @@ -657,11 +656,12 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } - if (get_access(b, AUTHENTICATED) >= required - && b->pwdb && strcmp(mech, "CRAM-MD5") == 0) + if (b->repository->auth_access >= required + && b->repository->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); + SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->repository->pwdb, + &user, success)); + b->client_info->user = apr_pstrdup(b->pool, user); return SVN_NO_ERROR; } @@ -680,7 +680,7 @@ internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm)); do { SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); @@ -702,7 +702,7 @@ static svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t needs_username) { #ifdef SVN_HAVE_SASL - if (b->use_sasl) + if (b->repository->use_sasl) return cyrus_auth_request(conn, pool, b, required, needs_username); #endif @@ -732,7 +732,6 @@ static svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn, */ 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) @@ -743,12 +742,12 @@ static svn_boolean_t lookup_access(apr_pool_t *pool, svn_error_t *err; /* Get authz's opinion on the access. */ - err = authz_check_access(&authorized, path, required, baton, conn, pool); + err = authz_check_access(&authorized, path, required, baton, pool); /* If an error made lookup fail, deny access. */ if (err) { - log_server_error(err, baton, conn, pool); + log_error(err, baton); svn_error_clear(err); return FALSE; } @@ -758,7 +757,7 @@ static svn_boolean_t lookup_access(apr_pool_t *pool, lookup has succeeded. */ if (current_access(baton) >= req && authorized - && (! needs_username || baton->user)) + && (! needs_username || baton->client_info->user)) return TRUE; return FALSE; @@ -791,7 +790,7 @@ static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, /* 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)) + if (lookup_access(pool, b, required, path, needs_username)) { SVN_ERR(create_fs_access(b, pool)); return trivial_auth_request(conn, pool, b); @@ -803,17 +802,18 @@ static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, 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)) + if (b->client_info->user == NULL + && b->repository->auth_access >= req + && (b->client_info->tunnel_user || b->repository->pwdb + || b->repository->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)) + if (! lookup_access(pool, b, 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, NULL, b), NULL); /* Else, access is granted, and there is much rejoicing. */ @@ -966,17 +966,18 @@ static svn_error_t *accept_report(svn_boolean_t *only_empty_entry, /* 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, + SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, + b->repository->repos, + b->repository->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.repos_url = svn_path_uri_decode(b->repository->repos_url, pool); rb.report_baton = report_baton; rb.err = NULL; rb.entry_counter = 0; @@ -1028,7 +1029,7 @@ static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn, /* 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 svn_lock_t *lock) { const char *cdate, *edate; @@ -1065,8 +1066,7 @@ get_props(apr_hash_t **props, /* 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); + str = svn_string_createf(pool, "%ld", crev); 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); @@ -1102,11 +1102,11 @@ static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->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_stringbuf_set(b->repository->fs_path, fs_path); SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; } @@ -1120,7 +1120,7 @@ static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); return SVN_NO_ERROR; } @@ -1138,7 +1138,7 @@ static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool)); SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); return SVN_NO_ERROR; } @@ -1160,7 +1160,8 @@ static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *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, + SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev, + b->client_info->user, name, old_value_p, value, TRUE, TRUE, authz_check_access_cb_func(b), &ab, @@ -1239,9 +1240,10 @@ static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->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, "!))")); @@ -1265,9 +1267,9 @@ static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->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; } @@ -1294,15 +1296,14 @@ static svn_error_t *commit_done(const svn_commit_info_t *commit_info, * 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, +static svn_error_t *add_lock_tokens(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)); + SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs)); /* If there is no access context, nowhere to add the tokens. */ if (! fs_access) @@ -1329,14 +1330,13 @@ static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn, "Lock token isn't a string"); path = path_item->u.string->data; - full_path = svn_fspath__join(sb->fs_path->data, + full_path = svn_fspath__join(sb->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); - if (! lookup_access(pool, sb, conn, svn_authz_write, - full_path, TRUE)) + if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE)) return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, - sb, conn, pool); + sb); token = token_item->u.string->data; SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token)); @@ -1345,45 +1345,58 @@ static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } +/* Implements svn_fs_lock_callback_t. */ +static svn_error_t * +lock_cb(void *baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + server_baton_t *sb = baton; + + log_error(fs_err, sb); + + 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); + apr_pool_t *subpool = svn_pool_create(pool); + apr_hash_t *targets = apr_hash_make(subpool); + svn_error_t *err; 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; + full_path = svn_fspath__join(sb->repository->fs_path->data, + svn_relpath_canonicalize(path, subpool), + subpool); token = token_item->u.string->data; + svn_hash_sets(targets, full_path, token); + } - 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); - } + /* The lock may have become defunct after the commit, so ignore such + errors. */ + err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE, + lock_cb, sb, subpool, subpool); + log_error(err, sb); + svn_error_clear(err); - svn_pool_destroy(iterpool); + svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -1392,13 +1405,13 @@ 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, + const char *log_msg, *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_array_header_t *revprop_list; apr_hash_t *revprop_table; const svn_delta_editor_t *editor; void *edit_baton; @@ -1439,7 +1452,7 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* 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)); + SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool)); /* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */ if (revprop_list) @@ -1454,7 +1467,9 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *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); + b->client_info->user + ? svn_string_create(b->client_info->user, pool) + : NULL); ccb.pool = pool; ccb.new_rev = &new_rev; @@ -1463,9 +1478,9 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + (&editor, &edit_baton, b->repository->repos, NULL, + svn_path_uri_decode(b->repository->repos_url, pool), + b->repository->fs_path->data, revprop_table, commit_done, &ccb, authz_commit_cb, &ab, pool)); SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); @@ -1482,18 +1497,18 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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)); + if (b->client_info->tunnel) + SVN_ERR(svn_fs_deltify_revision(b->repository->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(unlock_paths(lock_tokens, b, 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)); + if (! b->client_info->tunnel) + SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool)); } return SVN_NO_ERROR; } @@ -1529,7 +1544,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) wants_inherited_props = FALSE; - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); /* Check authorizations */ @@ -1537,14 +1552,14 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, full_path, FALSE)); if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->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_revision_root(&root, b->repository->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); @@ -1594,7 +1609,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, while (1) { len = sizeof(buf); - err = svn_stream_read(contents, buf, &len); + err = svn_stream_read_full(contents, buf, &len); if (err) break; if (len > 0) @@ -1683,7 +1698,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } } - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); /* Check authorizations */ @@ -1691,7 +1706,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, full_path, FALSE)); if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__get_dir(full_path, rev, @@ -1699,7 +1714,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, dirent_fields, pool))); /* Fetch the root of the appropriate revision. */ - SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); + SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); /* Fetch the directory's explicit and/or inherited properties if requested. Although the wants-iprops boolean was added to the @@ -1731,8 +1746,8 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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 *name = apr_hash_this_key(hi); + svn_fs_dirent_t *fsent = apr_hash_this_val(hi); const char *file_path; /* The fields in the entry tuple. */ @@ -1751,8 +1766,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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)) + if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE)) continue; if (dirent_fields & SVN_DIRENT_KIND) @@ -1765,12 +1779,9 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path, subpool)); - has_props = (apr_hash_count(file_props) > 0); } if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) @@ -1834,15 +1845,15 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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 */ + svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ + svn_tristate_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, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?w3?3", &rev, &target, &recurse, &depth_word, &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); @@ -1852,18 +1863,18 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, else depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); - full_path = svn_fspath__join(b->fs_path->data, target, pool); + full_path = svn_fspath__join(b->repository->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_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->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 */)); + (send_copyfrom_args == svn_tristate_true), + (ignore_ancestry == svn_tristate_true))); if (is_checkout) { SVN_ERR(log_command(b, conn, pool, "%s", @@ -1874,7 +1885,9 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { SVN_ERR(log_command(b, conn, pool, "%s", svn_log__update(full_path, rev, depth, - send_copyfrom_args, pool))); + (send_copyfrom_args + == svn_tristate_true), + pool))); } return SVN_NO_ERROR; @@ -1891,11 +1904,11 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* 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 */ + svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ + svn_tristate_t ignore_ancestry; /* Optional; default TRUE */ /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?33", &rev, &target, &recurse, &switch_url, &depth_word, &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); @@ -1908,14 +1921,16 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); - SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), + SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->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); + const char *full_path = svn_fspath__join(b->repository->fs_path->data, + target, pool); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__switch(full_path, switch_path, rev, depth, pool))); @@ -1924,8 +1939,8 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *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 */); + (send_copyfrom_args == svn_tristate_true), + (ignore_ancestry != svn_tristate_false)); } static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -1951,10 +1966,11 @@ static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); { - const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); + const char *full_path = svn_fspath__join(b->repository->fs_path->data, + target, pool); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__status(full_path, rev, depth, pool))); } @@ -2002,13 +2018,15 @@ static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); + SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->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); + const char *full_path = svn_fspath__join(b->repository->fs_path->data, + target, pool); svn_revnum_t from_rev; SVN_ERR(accept_report(NULL, &from_rev, conn, pool, b, rev, target, versus_path, @@ -2060,7 +2078,7 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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); + full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool); APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; } @@ -2070,20 +2088,20 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, pool))); SVN_ERR(trivial_auth_request(conn, pool, b)); - SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos, + SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repository->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)); + b->repository->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); + const char *key = apr_hash_this_key(hi); + svn_mergeinfo_t value = apr_hash_this_val(hi); svn_string_t *mergeinfo_string; svn_pool_clear(iterpool); @@ -2107,9 +2125,8 @@ static svn_error_t *log_receiver(void *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; + const svn_string_t *author, *date, *message; + unsigned revprop_count; if (log_entry->revision == SVN_INVALID_REVNUM) { @@ -2125,43 +2142,57 @@ static svn_error_t *log_receiver(void *baton, b->stack_depth--; } - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!")); + svn_compat_log_revprops_out_string(&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; + + /* send LOG_ENTRY */ + SVN_ERR(svn_ra_svn__start_list(conn, pool)); + + /* send LOG_ENTRY->CHANGED_PATHS2 */ + SVN_ERR(svn_ra_svn__start_list(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); + const char *path = apr_hash_this_key(h); + svn_log_changed_path2_t *change = apr_hash_this_val(h); - action[0] = change->action; - action[1] = '\0'; - SVN_ERR(svn_ra_svn__write_tuple( - conn, pool, "cw(?cr)(cbb)", + SVN_ERR(svn_ra_svn__write_data_log_changed_path( + conn, pool, path, - action, + change->action, change->copyfrom_path, change->copyfrom_rev, - svn_node_kind_to_word(change->node_kind), + 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)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); + + /* send LOG_ENTRY main members */ + SVN_ERR(svn_ra_svn__write_data_log_entry(conn, pool, + log_entry->revision, + author, date, message, + log_entry->has_children, + invalid_revnum, revprop_count)); + + /* send LOG_ENTRY->REVPROPS */ + SVN_ERR(svn_ra_svn__start_list(conn, pool)); + if (revprop_count) + SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); + + /* send LOG_ENTRY members that were added in later SVN releases */ + SVN_ERR(svn_ra_svn__write_boolean(conn, pool, log_entry->subtractive_merge)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); if (log_entry->has_children) b->stack_depth++; @@ -2239,7 +2270,8 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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); + full_path = svn_fspath__join(b->repository->fs_path->data, full_path, + pool); APR_ARRAY_PUSH(full_paths, const char *) = full_path; } SVN_ERR(trivial_auth_request(conn, pool, b)); @@ -2251,14 +2283,14 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, revprops, pool))); /* Get logs. (Can't report errors back to the client at this point.) */ - lb.fs_path = b->fs_path->data; + lb.fs_path = b->repository->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); + err = svn_repos_get_logs4(b->repository->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) @@ -2281,7 +2313,7 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); /* Check authorizations */ @@ -2289,12 +2321,12 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, full_path, FALSE)); if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->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_revision_root(&root, b->repository->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))); @@ -2311,7 +2343,7 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); /* Check authorizations */ @@ -2319,12 +2351,12 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, full_path, FALSE)); if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->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_fs_revision_root(&root, b->repository->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 @@ -2372,7 +2404,8 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, &loc_revs_proto)); relative_path = svn_relpath_canonicalize(relative_path, pool); - abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); + abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, + pool); location_revisions = apr_array_make(pool, loc_revs_proto->nelts, sizeof(svn_revnum_t)); @@ -2397,8 +2430,9 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* 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, + err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations, + abs_path, peg_revision, + location_revisions, authz_check_access_cb_func(b), &ab, pool); @@ -2412,8 +2446,8 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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); + const svn_revnum_t *iter_key = apr_hash_this_key(iter); + const char *iter_value = apr_hash_this_val(iter); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", *iter_key, iter_value)); @@ -2466,7 +2500,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, &start_rev, &end_rev)); relative_path = svn_relpath_canonicalize(relative_path, pool); - abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); + abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, + pool); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_ERR(log_command(baton, conn, pool, "%s", @@ -2479,7 +2514,16 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, { svn_revnum_t youngest; - SVN_CMD_ERR(svn_fs_youngest_rev(&youngest, b->fs, pool)); + err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool); + + if (err) + { + err = svn_error_compose_create( + svn_ra_svn__write_word(conn, pool, "done"), + err); + + return log_fail_and_flush(err, b, conn, pool); + } if (!SVN_IS_VALID_REVNUM(start_rev)) start_rev = youngest; @@ -2493,7 +2537,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, if (end_rev > start_rev) { - err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + err = svn_ra_svn__write_word(conn, pool, "done"); + err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, "Get-location-segments end revision must not be " "younger than start revision"); return log_fail_and_flush(err, b, conn, pool); @@ -2501,7 +2546,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, if (start_rev > peg_revision) { - err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + err = svn_ra_svn__write_word(conn, pool, "done"); + err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, "Get-location-segments start revision must not " "be younger than peg revision"); return log_fail_and_flush(err, b, conn, pool); @@ -2513,7 +2559,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, /* 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, + err = svn_repos_node_location_segments(b->repository->repos, abs_path, peg_revision, start_rev, end_rev, gls_receiver, (void *)conn, authz_check_access_cb_func(b), &ab, @@ -2521,8 +2567,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) { - svn_error_clear(err); - return write_err; + return svn_error_compose_create(write_err, err); } SVN_CMD_ERR(err); @@ -2621,7 +2666,7 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, &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); + full_path = svn_fspath__join(b->repository->fs_path->data, path, pool); if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) include_merged_revisions = FALSE; @@ -2636,8 +2681,8 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, frb.conn = conn; frb.pool = NULL; - err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev, - include_merged_revisions, + err = svn_repos_get_file_revs2(b->repository->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"); @@ -2665,7 +2710,7 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, &steal_lock, ¤t_rev)); - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, @@ -2673,8 +2718,8 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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. */ + SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->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")); @@ -2684,6 +2729,48 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } +struct lock_result_t { + const svn_lock_t *lock; + svn_error_t *err; +}; + +struct lock_many_baton_t { + apr_hash_t *results; + apr_pool_t *pool; +}; + +/* Implements svn_fs_lock_callback_t. */ +static svn_error_t * +lock_many_cb(void *baton, + const char *path, + const svn_lock_t *fs_lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + struct lock_many_baton_t *b = baton; + struct lock_result_t *result = apr_palloc(b->pool, + sizeof(struct lock_result_t)); + + result->lock = fs_lock; + result->err = svn_error_dup(fs_err); + svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result); + + return SVN_NO_ERROR; +} + +static void +clear_lock_result_hash(apr_hash_t *results, + apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi)) + { + struct lock_result_t *result = apr_hash_this_val(hi); + svn_error_clear(result->err); + } +} + static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_array_header_t *params, void *baton) { @@ -2693,12 +2780,11 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_error_t *err, *write_err = SVN_NO_ERROR; + apr_hash_t *targets = apr_hash_make(pool); + apr_hash_t *authz_results = apr_hash_make(pool); + apr_hash_index_t *hi; + struct lock_many_baton_t lmb; SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, &path_revs)); @@ -2711,12 +2797,14 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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)); + /* Parse the lock requests from PATH_REVS into TARGETS. */ for (i = 0; i < path_revs->nelts; ++i) { + const char *path, *full_path; + svn_revnum_t current_rev; svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, svn_ra_svn_item_t); + svn_fs_lock_target_t *target; svn_pool_clear(subpool); @@ -2724,56 +2812,112 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, ¤t_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, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, subpool), pool); - APR_ARRAY_PUSH(log_paths, const char *) = full_path; + target = svn_fs_lock_target_create(NULL, current_rev, pool); + + /* Any duplicate paths, once canonicalized, get collapsed into a + single path that is processed once. The result is then + returned multiple times. */ + svn_hash_sets(targets, full_path, target); + } + + SVN_ERR(log_command(b, conn, subpool, "%s", + svn_log__lock(targets, steal_lock, subpool))); - if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE)) + /* Check authz. + + Note: From here on we need to make sure any errors in authz_results, or + results, are cleared before returning from this function. */ + for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *full_path = apr_hash_this_key(hi); + + svn_pool_clear(subpool); + + if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE)) { - err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, - b, conn, pool); - break; + struct lock_result_t *result + = apr_palloc(pool, sizeof(struct lock_result_t)); + + result->lock = NULL; + result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, + NULL, NULL, b); + svn_hash_sets(authz_results, full_path, result); + svn_hash_sets(targets, full_path, NULL); } + } - err = svn_repos_fs_lock(&l, b->repos, full_path, - NULL, comment, FALSE, - 0, /* No expiration time. */ - current_rev, - steal_lock, subpool); + lmb.results = apr_hash_make(pool); + lmb.pool = pool; - if (err) + err = svn_repos_fs_lock_many(b->repository->repos, targets, + comment, FALSE, + 0, /* No expiration time. */ + steal_lock, lock_many_cb, &lmb, + pool, subpool); + + /* Return results in the same order as the paths were supplied. */ + for (i = 0; i < path_revs->nelts; ++i) + { + const char *path, *full_path; + svn_revnum_t current_rev; + svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, + svn_ra_svn_item_t); + struct lock_result_t *result; + + svn_pool_clear(subpool); + + write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, + ¤t_rev); + if (write_err) + break; + + full_path = svn_fspath__join(b->repository->fs_path->data, + svn_relpath_canonicalize(path, subpool), + subpool); + + result = svn_hash_gets(lmb.results, full_path); + if (!result) + result = svn_hash_gets(authz_results, full_path); + if (!result) { - 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; + /* No result? Something really odd happened, create a + placeholder error so that any other results can be + reported in the correct order. */ + result = apr_palloc(pool, sizeof(struct lock_result_t)); + result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, + _("No result for '%s'."), path); + svn_hash_sets(lmb.results, full_path, result); } + + if (result->err) + write_err = svn_ra_svn__write_cmd_failure(conn, subpool, + result->err); 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, "!")); + write_err = svn_ra_svn__write_tuple(conn, subpool, + "w!", "success"); + if (!write_err) + write_err = write_lock(conn, subpool, result->lock); + if (!write_err) + write_err = svn_ra_svn__write_tuple(conn, subpool, "!"); } + if (write_err) + break; } - svn_pool_destroy(subpool); + clear_lock_result_hash(authz_results, subpool); + clear_lock_result_hash(lmb.results, subpool); - SVN_ERR(log_command(b, conn, pool, "%s", - svn_log__lock(log_paths, steal_lock, pool))); + svn_pool_destroy(subpool); - /* 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) + write_err = svn_ra_svn__write_word(conn, pool, "done"); if (!write_err) SVN_CMD_ERR(err); svn_error_clear(err); @@ -2793,7 +2937,7 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); /* Username required unless break_lock was specified. */ @@ -2802,8 +2946,8 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token, + break_lock, pool)); SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); @@ -2818,11 +2962,11 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR; + apr_hash_t *targets = apr_hash_make(pool); + apr_hash_t *authz_results = apr_hash_make(pool); + apr_hash_index_t *hi; + struct lock_many_baton_t lmb; SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, &unlock_tokens)); @@ -2832,12 +2976,12 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, subpool = svn_pool_create(pool); - /* Loop through the unlock requests. */ - log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path)); + /* Parse the unlock requests from PATH_REVS into TARGETS. */ 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); + const char *path, *full_path, *token; svn_pool_clear(subpool); @@ -2847,51 +2991,106 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, &token)); + if (!token) + 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, + full_path = svn_fspath__join(b->repository->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, + /* Any duplicate paths, once canonicalized, get collapsed into a + single path that is processed once. The result is then + returned multiple times. */ + svn_hash_sets(targets, full_path, token); + } + + SVN_ERR(log_command(b, conn, subpool, "%s", + svn_log__unlock(targets, break_lock, subpool))); + + /* Check authz. + + Note: From here on we need to make sure any errors in authz_results, or + results, are cleared before returning from this function. */ + for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) + { + const char *full_path = apr_hash_this_key(hi); + + svn_pool_clear(subpool); + + if (! lookup_access(subpool, b, 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; + struct lock_result_t *result + = apr_palloc(pool, sizeof(struct lock_result_t)); + + result->lock = NULL; + result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, + NULL, NULL, b); + svn_hash_sets(authz_results, full_path, result); + svn_hash_sets(targets, full_path, NULL); } + } + + lmb.results = apr_hash_make(pool); + lmb.pool = pool; + + err = svn_repos_fs_unlock_many(b->repository->repos, targets, + break_lock, lock_many_cb, &lmb, + pool, subpool); + + /* Return results in the same order as the paths were supplied. */ + for (i = 0; i < unlock_tokens->nelts; ++i) + { + const char *path, *token, *full_path; + svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, + svn_ra_svn_item_t); + struct lock_result_t *result; + + svn_pool_clear(subpool); + + write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, + &token); + if (write_err) + break; + + full_path = svn_fspath__join(b->repository->fs_path->data, + svn_relpath_canonicalize(path, subpool), + pool); + + result = svn_hash_gets(lmb.results, full_path); + if (!result) + result = svn_hash_gets(authz_results, full_path); + if (!result) + { + /* No result? Something really odd happened, create a + placeholder error so that any other results can be + reported in the correct order. */ + result = apr_palloc(pool, sizeof(struct lock_result_t)); + result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, + _("No result for '%s'."), path); + svn_hash_sets(lmb.results, full_path, result); + } + + if (result->err) + write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err); else - SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", - path)); + write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", + path); + if (write_err) + break; } - svn_pool_destroy(subpool); + clear_lock_result_hash(authz_results, subpool); + clear_lock_result_hash(lmb.results, subpool); - SVN_ERR(log_command(b, conn, pool, "%s", - svn_log__unlock(log_paths, break_lock, pool))); + svn_pool_destroy(subpool); - /* 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) + 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; @@ -2907,7 +3106,7 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, @@ -2915,7 +3114,7 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 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_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool)); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); if (l) @@ -2954,21 +3153,22 @@ static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return log_fail_and_flush(err, b, conn, pool); } - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->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, + SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->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_lock_t *l = apr_hash_this_val(hi); SVN_ERR(write_lock(conn, pool, l)); } @@ -2994,15 +3194,16 @@ static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, ab.conn = conn; SVN_ERR(log_command(b, conn, pool, - svn_log__replay(b->fs_path->data, rev, pool))); + svn_log__replay(b->repository->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); + err = svn_fs_revision_root(&root, b->repository->fs, rev, pool); if (! err) - err = svn_repos_replay2(root, b->fs_path->data, low_water_mark, - send_deltas, editor, edit_baton, + err = svn_repos_replay2(root, b->repository->fs_path->data, + low_water_mark, send_deltas, editor, edit_baton, authz_check_access_cb_func(b), &ab, pool); if (err) @@ -3057,7 +3258,8 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_pool_clear(iterpool); - SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, + SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, + b->repository->repos, rev, authz_check_access_cb_func(b), &ab, iterpool)); @@ -3090,12 +3292,12 @@ get_deleted_rev(svn_ra_svn_conn_t *conn, SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", &path, &peg_revision, &end_revision)); - full_path = svn_fspath__join(b->fs_path->data, + full_path = svn_fspath__join(b->repository->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_repos_deleted_rev(b->repository->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; } @@ -3121,7 +3323,7 @@ get_inherited_props(svn_ra_svn_conn_t *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, + full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, iterpool), pool); @@ -3130,14 +3332,14 @@ get_inherited_props(svn_ra_svn_conn_t *conn, full_path, FALSE)); if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->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(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool)); SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); /* Send successful command response with revision and props. */ @@ -3251,18 +3453,30 @@ repos_path_valid(const char *path) } /* 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_*. + * repository root. If we find one, fill in the repos, fs, repos_url, + * and fs_path fields of REPOSITORY. VHOST and READ_ONLY flags are the + * same as in the server baton. + * + * CONFIG_POOL and AUTHZ_POOL shall be used to load any object of the + * respective type. + * + * Use SCRATCH_POOL for temporary allocations. + * */ -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; +static svn_error_t * +find_repos(const char *url, + const char *root, + svn_boolean_t vhost, + svn_boolean_t read_only, + svn_config_t *cfg, + repository_t *repository, + svn_repos__config_pool_t *config_pool, + svn_repos__authz_pool_t *authz_pool, + apr_hash_t *fs_config, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *path, *full_path, *fs_path, *hooks_env; svn_stringbuf_t *url_buf; /* Skip past the scheme and authority part. */ @@ -3271,14 +3485,14 @@ static svn_error_t *find_repos(const char *url, const char *root, return svn_error_createf(SVN_ERR_BAD_URL, NULL, "Non-svn URL passed to svn server: '%s'", url); - if (! b->vhost) + if (! vhost) { path = strchr(path, '/'); if (path == NULL) path = ""; } - path = svn_relpath_canonicalize(path, pool); - path = svn_path_uri_decode(path, pool); + path = svn_relpath_canonicalize(path, scratch_pool); + path = svn_path_uri_decode(path, scratch_pool); /* Ensure that it isn't possible to escape the root by disallowing '..' segments. */ @@ -3287,82 +3501,94 @@ static svn_error_t *find_repos(const char *url, const char *root, "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); + full_path = svn_dirent_join(svn_dirent_canonicalize(root, scratch_pool), + path, scratch_pool); /* Search for a repository in the full path. */ - repos_root = svn_repos_find_root_path(full_path, pool); - if (!repos_root) + repository->repos_root = svn_repos_find_root_path(full_path, result_pool); + if (!repository->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_ERR(svn_repos_open3(&repository->repos, repository->repos_root, + fs_config, result_pool, scratch_pool)); + SVN_ERR(svn_repos_remember_client_capabilities(repository->repos, + repository->capabilities)); + repository->fs = svn_repos_fs(repository->repos); + fs_path = full_path + strlen(repository->repos_root); + repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", + result_pool); + url_buf = svn_stringbuf_create(url, result_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); + svn_path_component_count(repository->fs_path->data)); + repository->repos_url = url_buf->data; + repository->authz_repos_name = svn_dirent_is_child(root, + repository->repos_root, + result_pool); + if (repository->authz_repos_name == NULL) + repository->repos_name = svn_dirent_basename(repository->repos_root, + result_pool); else - b->repos_name = b->authz_repos_name; - b->repos_name = svn_path_uri_encode(b->repos_name, pool); + repository->repos_name = repository->authz_repos_name; + repository->repos_name = svn_path_uri_encode(repository->repos_name, + result_pool); /* If the svnserve configuration has not been loaded then load it from the * repository. */ - if (NULL == b->cfg) + if (NULL == cfg) { - b->base = svn_repos_conf_dir(b->repos, pool); + repository->base = svn_repos_conf_dir(repository->repos, result_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)); + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_repos_svnserve_conf + (repository->repos, result_pool), + FALSE, FALSE, repository->repos, + result_pool)); } + SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool)); + SVN_ERR(load_authz_config(repository, repository->repos_root, cfg, + authz_pool, result_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)); + { + const char *val; + + /* Should we use Cyrus SASL? */ + SVN_ERR(svn_config_get_bool(cfg, &repository->use_sasl, + SVN_CONFIG_SECTION_SASL, + SVN_CONFIG_OPTION_USE_SASL, FALSE)); + + svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, + SVN_CONFIG_OPTION_MIN_SSF, "0"); + SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val)); + + svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, + SVN_CONFIG_OPTION_MAX_SSF, "256"); + SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val)); + } #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); + SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool)); + svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL, + SVN_CONFIG_OPTION_REALM, repository->realm); + repository->realm = apr_pstrdup(result_pool, repository->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); + set_access(repository, cfg, read_only); /* Configure hook script environment variables. */ - svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, + svn_config_get(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)); + hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool); + + repository->hooks_env = apr_pstrdup(result_pool, hooks_env); return SVN_NO_ERROR; } @@ -3385,9 +3611,7 @@ 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); + log_error(err, b->server); } /* Return the normalized repository-relative path for the given PATH @@ -3404,7 +3628,7 @@ get_normalized_repo_rel_path(void *baton, if (svn_path_is_url(path)) { /* This is a copyfrom URL. */ - path = svn_uri_skip_ancestor(sb->repos_url, path, pool); + path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool); path = svn_fspath__canonicalize(path, pool); } else @@ -3412,7 +3636,7 @@ get_normalized_repo_rel_path(void *baton, /* 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); + path = svn_fspath__join(sb->repository->fs_path->data, path, pool); } return path; @@ -3431,9 +3655,9 @@ get_revision_root(svn_fs_root_t **fs_root, 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_youngest_rev(&revision, sb->repository->fs, pool)); - SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool)); + SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool)); return SVN_NO_ERROR; } @@ -3518,46 +3742,60 @@ fetch_base_func(const char **filename, return SVN_NO_ERROR; } -svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, - apr_pool_t *pool) +client_info_t * +get_client_info(svn_ra_svn_conn_t *conn, + serve_params_t *params, + apr_pool_t *pool) +{ + client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info)); + + client_info->tunnel = params->tunnel; + client_info->tunnel_user = get_tunnel_user(params, pool); + client_info->user = NULL; + client_info->authz_user = NULL; + client_info->remote_host = svn_ra_svn_conn_remote_host(conn); + + return client_info; +} + +/* Construct the server baton for CONN using PARAMS and return it in *BATON. + * It's lifetime is the same as that of CONN. SCRATCH_POOL + */ +static svn_error_t * +construct_server_baton(server_baton_t **baton, + svn_ra_svn_conn_t *conn, + serve_params_t *params, + apr_pool_t *scratch_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"); + const char *client_url, *ra_client_string, *client_string; + apr_array_header_t *caplist; + apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn); + server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b)); + fs_warning_baton_t *warn_baton; + svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool); + + b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository)); + b->repository->username_case = params->username_case; + b->repository->base = params->base; + b->repository->pwdb = NULL; + b->repository->authzdb = NULL; + b->repository->realm = NULL; + b->repository->use_sasl = FALSE; + + b->read_only = params->read_only; + b->pool = conn_pool; + b->vhost = params->vhost; + + b->logger = params->logger; + b->client_info = get_client_info(conn, params, conn_pool); /* 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)", + SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, + "nn()(wwwwwwwwwww)", (apr_uint64_t) 2, (apr_uint64_t) 2, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1, @@ -3572,7 +3810,8 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE )); else - SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)", + SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, + "nn()(wwwwwwwwww)", (apr_uint64_t) 2, (apr_uint64_t) 2, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_ABSENT_ENTRIES, @@ -3589,14 +3828,14 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, /* 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)", + SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_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); + client_url = svn_uri_canonicalize(client_url, conn_pool); SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); /* All released versions of Subversion support edit-pipeline, @@ -3617,14 +3856,15 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, int i; svn_ra_svn_item_t *item; - cap_words = apr_array_make(pool, 1, sizeof(const char *)); + b->repository->capabilities = apr_array_make(conn_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 *) + APR_ARRAY_PUSH(b->repository->capabilities, const char *) = SVN_RA_CAPABILITY_MERGEINFO; } /* Save for operational log. */ @@ -3634,46 +3874,40 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, } } - err = find_repos(client_url, params->root, &b, conn, cap_words, pool); + err = handle_config_error(find_repos(client_url, params->root, b->vhost, + b->read_only, params->cfg, + b->repository, params->config_pool, + params->authz_pool, params->fs_config, + conn_pool, scratch_pool), + b); + if (!err) + { + if (b->repository->anon_access == NO_ACCESS + && (b->repository->auth_access == NO_ACCESS + || (!b->client_info->tunnel_user && !b->repository->pwdb + && !b->repository->use_sasl))) + err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + "No access allowed to this repository", + b); + } if (!err) { - SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE)); - if (current_access(&b) == NO_ACCESS) + SVN_ERR(auth_request(conn, scratch_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); + "Not authorized for access", b); } 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); + log_error(err, b); + io_err = svn_ra_svn__write_cmd_failure(conn, scratch_pool, err); svn_error_clear(err); SVN_ERR(io_err); - return svn_ra_svn__flush(conn, pool); + return svn_ra_svn__flush(conn, scratch_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)); + SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid, + conn_pool)); /* We can't claim mergeinfo capability until we know whether the repository supports mergeinfo (i.e., is not a 1.4 repository), @@ -3683,28 +3917,161 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, 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)); + SVN_ERR(svn_repos_has_capability(b->repository->repos, + &supports_mergeinfo, + SVN_REPOS_CAPABILITY_MERGEINFO, + scratch_pool)); + + SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!", + "success", b->repository->uuid, + b->repository->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, "!))")); + SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool, + SVN_RA_SVN_CAP_MERGEINFO)); + SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))")); + SVN_ERR(svn_ra_svn__flush(conn, scratch_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, scratch_pool); + if (client_string == NULL || client_string[0] == '\0') + client_string = "-"; + else + client_string = svn_path_uri_encode(client_string, scratch_pool); + SVN_ERR(log_command(b, conn, scratch_pool, + "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s", + ver, cap_log->data, + svn_path_uri_encode(b->repository->fs_path->data, + scratch_pool), + ra_client_string, client_string)); + + warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton)); + warn_baton->server = b; + warn_baton->conn = conn; + svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton); + /* Set up editor shims. */ { svn_delta_shim_callbacks_t *callbacks = - svn_delta_shim_callbacks_default(pool); + svn_delta_shim_callbacks_default(conn_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; + 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); + *baton = b; + + return SVN_NO_ERROR; +} + +svn_error_t * +serve_interruptable(svn_boolean_t *terminate_p, + connection_t *connection, + svn_boolean_t (* is_busy)(connection_t *), + apr_pool_t *pool) +{ + svn_boolean_t terminate = FALSE; + svn_error_t *err = NULL; + const svn_ra_svn_cmd_entry_t *command; + apr_pool_t *iterpool = svn_pool_create(pool); + + /* Prepare command parser. */ + apr_hash_t *cmd_hash = apr_hash_make(pool); + for (command = main_commands; command->cmdname; command++) + svn_hash_sets(cmd_hash, command->cmdname, command); + + /* Auto-initialize connection */ + if (! connection->conn) + { + apr_status_t ar; + + /* Enable TCP keep-alives on the socket so we time out when + * the connection breaks due to network-layer problems. + * If the peer has dropped the connection due to a network partition + * or a crash, or if the peer no longer considers the connection + * valid because we are behind a NAT and our public IP has changed, + * it will respond to the keep-alive probe with a RST instead of an + * acknowledgment segment, which will cause svn to abort the session + * even while it is currently blocked waiting for data from the peer. */ + ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1); + if (ar) + { + /* It's not a fatal error if we cannot enable keep-alives. */ + } + + /* create the connection, configure ports etc. */ + connection->conn + = svn_ra_svn_create_conn4(connection->usock, NULL, NULL, + connection->params->compression_level, + connection->params->zero_copy_limit, + connection->params->error_check_interval, + connection->pool); + + /* Construct server baton and open the repository for the first time. */ + err = construct_server_baton(&connection->baton, connection->conn, + connection->params, pool); + } + + /* If we can't access the repo for some reason, end this connection. */ + if (err) + terminate = TRUE; + + /* Process incoming commands. */ + while (!terminate && !err) + { + svn_pool_clear(iterpool); + if (is_busy && is_busy(connection)) + { + svn_boolean_t has_command; + + /* If the server is busy, execute just one command and only if + * there is one currently waiting in our receive buffers. + */ + err = svn_ra_svn__has_command(&has_command, &terminate, + connection->conn, iterpool); + if (!err && has_command) + err = svn_ra_svn__handle_command(&terminate, cmd_hash, + connection->baton, + connection->conn, + FALSE, iterpool); + + break; + } + else + { + /* The server is not busy, thus let's serve whichever command + * comes in next and whenever it comes in. This requires the + * busy() callback test to return TRUE while there are still some + * resources left. + */ + err = svn_ra_svn__handle_command(&terminate, cmd_hash, + connection->baton, + connection->conn, + FALSE, iterpool); + } + } + + /* error or normal end of session. Close the connection */ + svn_pool_destroy(iterpool); + if (terminate_p) + *terminate_p = terminate; + + return svn_error_trace(err); +} + +svn_error_t *serve(svn_ra_svn_conn_t *conn, + serve_params_t *params, + apr_pool_t *pool) +{ + server_baton_t *baton = NULL; + + SVN_ERR(construct_server_baton(&baton, conn, params, pool)); + return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE); } diff --git a/contrib/subversion/subversion/svnserve/server.h b/contrib/subversion/subversion/svnserve/server.h index 926a96f61..d366e0cd4 100644 --- a/contrib/subversion/subversion/svnserve/server.h +++ b/contrib/subversion/subversion/svnserve/server.h @@ -36,39 +36,61 @@ extern "C" { #include "svn_repos.h" #include "svn_ra_svn.h" +#include "private/svn_atomic.h" +#include "private/svn_mutex.h" +#include "private/svn_repos_private.h" +#include "private/svn_subr_private.h" + enum username_case_type { CASE_FORCE_UPPER, CASE_FORCE_LOWER, CASE_ASIS }; -typedef struct server_baton_t { +enum authn_type { UNAUTHENTICATED, AUTHENTICATED }; +enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS }; + +typedef struct repository_t { svn_repos_t *repos; const char *repos_name; /* URI-encoded name of repository (not for authz) */ + const char *repos_root; /* Repository root directory */ svn_fs_t *fs; /* For convenience; same as svn_repos_fs(repos) */ const char *base; /* Base directory for config files */ - svn_config_t *cfg; /* Parsed repository svnserve.conf */ svn_config_t *pwdb; /* Parsed password database */ svn_authz_t *authzdb; /* Parsed authz rules */ const char *authz_repos_name; /* The name of the repository for authz */ const char *realm; /* Authentication realm */ const char *repos_url; /* URL to base of repository */ + const char *hooks_env; /* Path to the hooks environment file or NULL */ + const char *uuid; /* Repository ID */ + apr_array_header_t *capabilities; + /* Client capabilities (SVN_RA_CAPABILITY_*) */ svn_stringbuf_t *fs_path;/* Decoded base in-repos path (w/ leading slash) */ - apr_hash_t *fs_config; /* Additional FS configuration parameters */ - const char *user; /* Authenticated username of the user */ enum username_case_type username_case; /* Case-normalize the username? */ + svn_boolean_t use_sasl; /* Use Cyrus SASL for authentication; + always false if SVN_HAVE_SASL not defined */ + unsigned min_ssf; /* min-encryption SASL parameter */ + unsigned max_ssf; /* max-encryption SASL parameter */ + + enum access_type auth_access; /* access granted to authenticated users */ + enum access_type anon_access; /* access granted to annonymous users */ + +} repository_t; + +typedef struct client_info_t { + const char *user; /* Authenticated username of the user */ + const char *remote_host; /* IP of the client that contacted the server */ const char *authz_user; /* Username for authz ('user' + 'username_case') */ svn_boolean_t tunnel; /* Tunneled through login agent */ const char *tunnel_user; /* Allow EXTERNAL to authenticate as this */ +} client_info_t; + +typedef struct server_baton_t { + repository_t *repository; /* repository-specific data to use */ + client_info_t *client_info; /* client-specific data to use */ + struct logger_t *logger; /* Log file data structure. + May be NULL even if log_file is not. */ svn_boolean_t read_only; /* Disallow write access (global flag) */ - svn_boolean_t use_sasl; /* Use Cyrus SASL for authentication; - always false if SVN_HAVE_SASL not defined */ - apr_file_t *log_file; /* Log filehandle. */ svn_boolean_t vhost; /* Use virtual-host-based path to repo. */ apr_pool_t *pool; } server_baton_t; -enum authn_type { UNAUTHENTICATED, AUTHENTICATED }; -enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS }; - -enum access_type get_access(server_baton_t *b, enum authn_type auth); - typedef struct serve_params_t { /* The virtual root of the repositories to serve. The client URL path is interpreted relative to this root and is not allowed to @@ -97,20 +119,21 @@ typedef struct serve_params_t { per-repository svnserve.conf are not read. */ svn_config_t *cfg; - /* A filehandle open for writing logs to; possibly NULL. */ - apr_file_t *log_file; + /* logging data structure; possibly NULL. */ + struct logger_t *logger; - /* Username case normalization style. */ - enum username_case_type username_case; + /* all configurations should be opened through this factory */ + svn_repos__config_pool_t *config_pool; - /* Enable text delta caching for all FSFS repositories. */ - svn_boolean_t cache_txdeltas; + /* all authz data should be opened through this factory */ + svn_repos__authz_pool_t *authz_pool; - /* Enable full-text caching for all FSFS repositories. */ - svn_boolean_t cache_fulltexts; + /* The FS configuration to be applied to all repositories. + It mainly contains things like cache settings. */ + apr_hash_t *fs_config; - /* Enable revprop caching for all FSFS repositories. */ - svn_boolean_t cache_revprops; + /* Username case normalization style. */ + enum username_case_type username_case; /* Size of the in-memory cache (used by FSFS only). */ apr_uint64_t memory_cache_size; @@ -133,28 +156,65 @@ typedef struct serve_params_t { svn_boolean_t vhost; } serve_params_t; +/* This structure contains all data that describes a client / server + connection. Their lifetime is separated from the thread-local + serving pools. */ +typedef struct connection_t +{ + /* socket return by accept() */ + apr_socket_t *usock; + + /* server-global parameters */ + serve_params_t *params; + + /* connection-specific objects */ + server_baton_t *baton; + + /* buffered connection object used by the marshaller */ + svn_ra_svn_conn_t *conn; + + /* memory pool for objects with connection lifetime */ + apr_pool_t *pool; + + /* Number of threads using the pool. + The pool passed to apr_thread_create can only be released when both + + A: the call to apr_thread_create has returned to the calling thread + B: the new thread has started running and reached apr_thread_start_t + + So we set the atomic counter to 2 then both the calling thread and + the new thread decrease it and when it reaches 0 the pool can be + released. */ + svn_atomic_t ref_count; + +} connection_t; + +/* Return a client_info_t structure allocated in POOL and initialize it + * with data from CONN. */ +client_info_t * get_client_info(svn_ra_svn_conn_t *conn, + serve_params_t *params, + apr_pool_t *pool); + /* Serve the connection CONN according to the parameters PARAMS. */ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, apr_pool_t *pool); -/* Load the password database for the listening server based on the - entries in the SERVER struct. +/* Serve the connection CONNECTION for as long as IS_BUSY does not + return TRUE. If IS_BUSY is NULL, serve the connection until it + either gets terminated or there is an error. If TERMINATE_P is + not NULL, set *TERMINATE_P to TRUE if the connection got + terminated. - SERVER and CONN must not be NULL. The real errors will be logged with - SERVER and CONN but return generic errors to the client. */ -svn_error_t *load_pwdb_config(server_baton_t *server, - svn_ra_svn_conn_t *conn, - apr_pool_t *pool); - -/* Load the authz database for the listening server based on the - entries in the SERVER struct. - - SERVER and CONN must not be NULL. The real errors will be logged with - SERVER and CONN but return generic errors to the client. */ -svn_error_t *load_authz_config(server_baton_t *server, - svn_ra_svn_conn_t *conn, - const char *repos_root, - apr_pool_t *pool); + For the first call, CONNECTION->CONN may be NULL in which case we + will create an ra_svn connection object. Subsequent calls will + check for an open repository and automatically re-open the repo + in pool if necessary. + */ +svn_error_t * +serve_interruptable(svn_boolean_t *terminate_p, + connection_t *connection, + svn_boolean_t (* is_busy)(connection_t *), + apr_pool_t *pool); /* Initialize the Cyrus SASL library. POOL is used for allocations. */ svn_error_t *cyrus_init(apr_pool_t *pool); @@ -172,13 +232,6 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, apr_size_t escape_errorlog_item(char *dest, const char *source, apr_size_t buflen); -/* Log ERR to LOG_FILE if LOG_FILE is not NULL. Include REMOTE_HOST, - USER, and REPOS in the log if they are not NULL. Allocate temporary - char buffers in POOL (which caller can then clear or dispose of). */ -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); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/contrib/subversion/subversion/svnserve/svnserve.c b/contrib/subversion/subversion/svnserve/svnserve.c index 3d83323ef..f3a483dbd 100644 --- a/contrib/subversion/subversion/svnserve/svnserve.c +++ b/contrib/subversion/subversion/svnserve/svnserve.c @@ -48,14 +48,20 @@ #include "svn_cache_config.h" #include "svn_version.h" #include "svn_io.h" +#include "svn_hash.h" #include "svn_private_config.h" #include "private/svn_dep_compat.h" #include "private/svn_cmdline_private.h" #include "private/svn_atomic.h" +#include "private/svn_mutex.h" #include "private/svn_subr_private.h" +#if APR_HAS_THREADS +# include +#endif + #include "winservice.h" #ifdef HAVE_UNISTD_H @@ -63,6 +69,7 @@ #endif #include "server.h" +#include "logger.h" /* The strategy for handling incoming connections. Some of these may be unavailable due to platform limitations. */ @@ -103,6 +110,50 @@ enum run_mode { #endif +/* Parameters for the worker thread pool used in threaded mode. */ + +/* Have at least this many worker threads (even if there are no requests + * to handle). + * + * A 0 value is legal but increases the latency for the next incoming + * request. Higher values may be useful for servers that experience short + * bursts of concurrent requests followed by longer idle periods. + */ +#define THREADPOOL_MIN_SIZE 1 + +/* Maximum number of worker threads. If there are more concurrent requests + * than worker threads, the extra requests get queued. + * + * Since very slow connections will hog a full thread for a potentially + * long time before timing out, be sure to not set this limit too low. + * + * On the other hand, keep in mind that every thread will allocate up to + * 4MB of unused RAM in the APR allocator of its root pool. 32 bit servers + * must hence do with fewer threads. + */ +#if (APR_SIZEOF_VOIDP <= 4) +#define THREADPOOL_MAX_SIZE 64 +#else +#define THREADPOOL_MAX_SIZE 256 +#endif + +/* Number of microseconds that an unused thread remains in the pool before + * being terminated. + * + * Higher values are useful if clients frequently send small requests and + * you want to minimize the latency for those. + */ +#define THREADPOOL_THREAD_IDLE_LIMIT 1000000 + +/* Number of client to server connections that may concurrently in the + * TCP 3-way handshake state, i.e. are in the process of being created. + * + * Larger values improve scalability with lots of small requests coming + * on over long latency networks. + * + * The OS may actually use a lower limit than specified here. + */ +#define ACCEPT_BACKLOG 128 #ifdef WIN32 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET; @@ -156,6 +207,9 @@ void winservice_notify_stop(void) #define SVNSERVE_OPT_SINGLE_CONN 268 #define SVNSERVE_OPT_CLIENT_SPEED 269 #define SVNSERVE_OPT_VIRTUAL_HOST 270 +#define SVNSERVE_OPT_MIN_THREADS 271 +#define SVNSERVE_OPT_MAX_THREADS 272 +#define SVNSERVE_OPT_BLOCK_READ 273 static const apr_getopt_option_t svnserve__options[] = { @@ -217,21 +271,23 @@ static const apr_getopt_option_t svnserve__options[] = " " "Default is 16.\n" " " - "[used for FSFS repositories only]")}, + "0 switches to dynamically sized caches.\n" + " " + "[used for FSFS and FSX repositories only]")}, {"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1, N_("enable or disable caching of deltas between older\n" " " "revisions.\n" " " - "Default is no.\n" + "Default is yes.\n" " " - "[used for FSFS repositories only]")}, + "[used for FSFS and FSX repositories only]")}, {"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1, N_("enable or disable caching of file contents\n" " " "Default is yes.\n" " " - "[used for FSFS repositories only]")}, + "[used for FSFS and FSX repositories only]")}, {"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1, N_("enable or disable caching of revision properties.\n" " " @@ -239,7 +295,7 @@ static const apr_getopt_option_t svnserve__options[] = " " "Default is no.\n" " " - "[used for FSFS repositories only]")}, + "[used for FSFS and FSX repositories only]")}, {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1, N_("Optimize network handling based on the assumption\n" " " @@ -248,20 +304,55 @@ static const apr_getopt_option_t svnserve__options[] = "ARG Mbit/s.\n" " " "Default is 0 (optimizations disabled).")}, + {"block-read", SVNSERVE_OPT_BLOCK_READ, 1, + N_("Parse and cache all data found in block instead\n" + " " + "of just the requested item.\n" + " " + "Default is no.\n" + " " + "[used for FSFS repositories in 1.9 format only]")}, #ifdef CONNECTION_HAVE_THREAD_OPTION /* ### Making the assumption here that WIN32 never has fork and so * ### this option never exists when --service exists. */ {"threads", 'T', 0, N_("use threads instead of fork " "[mode: daemon]")}, + {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1, + N_("Minimum number of server threads, even if idle.\n" + " " + "Capped to max-threads; minimum value is 0.\n" + " " + "Default is 1.\n" + " " + "[used only with --threads]")}, +#if (APR_SIZEOF_VOIDP <= 4) + {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, + N_("Maximum number of server threads, even if there\n" + " " + "are more connections. Minimum value is 1.\n" + " " + "Default is 64.\n" + " " + "[used only with --threads]")}, +#else + {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, + N_("Maximum number of server threads, even if there\n" + " " + "are more connections. Minimum value is 1.\n" + " " + "Default is 256.\n" + " " + "[used only with --threads]")}, +#endif #endif {"foreground", SVNSERVE_OPT_FOREGROUND, 0, N_("run in foreground (useful for debugging)\n" " " "[mode: daemon]")}, {"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0, - N_("handle one connection at a time in the parent process\n" + N_("handle one connection at a time in the parent\n" " " - "(useful for debugging)")}, + "process (useful for debugging)")}, {"log-file", SVNSERVE_OPT_LOG_FILE, 1, N_("svnserve log file")}, {"pid-file", SVNSERVE_OPT_PID_FILE, 1, @@ -290,7 +381,6 @@ static const apr_getopt_option_t svnserve__options[] = {0, 0, 0, 0} }; - static void usage(const char *progname, apr_pool_t *pool) { if (!progname) @@ -299,7 +389,6 @@ static void usage(const char *progname, apr_pool_t *pool) svn_error_clear(svn_cmdline_fprintf(stderr, pool, _("Type '%s --help' for usage.\n"), progname)); - exit(1); } static void help(apr_pool_t *pool) @@ -309,15 +398,21 @@ static void help(apr_pool_t *pool) #ifdef WIN32 svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X " "| --service] [options]\n" + "Subversion repository server.\n" + "Type 'svnserve --version' to see the " + "program version.\n" "\n" "Valid options:\n"), - stdout, pool)); + stdout, pool)); #else svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] " "[options]\n" + "Subversion repository server.\n" + "Type 'svnserve --version' to see the " + "program version.\n" "\n" "Valid options:\n"), - stdout, pool)); + stdout, pool)); #endif for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++) { @@ -326,7 +421,6 @@ static void help(apr_pool_t *pool) svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); } svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); - exit(0); } static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool) @@ -378,56 +472,149 @@ static apr_status_t redirect_stdout(void *arg) return apr_file_dup2(out_file, err_file, pool); } -#if APR_HAS_THREADS -/* The pool passed to apr_thread_create can only be released when both +/* Wait for the next client connection to come in from SOCK. Allocate + * the connection in a root pool from CONNECTION_POOLS and assign PARAMS. + * Return the connection object in *CONNECTION. + * + * Use HANDLING_MODE for proper internal cleanup. + */ +static svn_error_t * +accept_connection(connection_t **connection, + apr_socket_t *sock, + serve_params_t *params, + enum connection_handling_mode handling_mode, + apr_pool_t *pool) +{ + apr_status_t status; - A: the call to apr_thread_create has returned to the calling thread - B: the new thread has started running and reached apr_thread_start_t + /* Non-standard pool handling. The main thread never blocks to join + * the connection threads so it cannot clean up after each one. So + * separate pools that can be cleared at thread exit are used. */ - So we set the atomic counter to 2 then both the calling thread and - the new thread decrease it and when it reaches 0 the pool can be - released. */ -struct shared_pool_t { - svn_atomic_t count; - apr_pool_t *pool; -}; + apr_pool_t *connection_pool = svn_pool_create(pool); + *connection = apr_pcalloc(connection_pool, sizeof(**connection)); + (*connection)->pool = connection_pool; + (*connection)->params = params; + (*connection)->ref_count = 1; -static struct shared_pool_t * -attach_shared_pool(apr_pool_t *pool) -{ - struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t)); + do + { + #ifdef WIN32 + if (winservice_is_stopping()) + exit(0); + #endif - shared->pool = pool; - svn_atomic_set(&shared->count, 2); + status = apr_socket_accept(&(*connection)->usock, sock, + connection_pool); + if (handling_mode == connection_mode_fork) + { + apr_proc_t proc; + + /* Collect any zombie child processes. */ + while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, + connection_pool) == APR_CHILD_DONE) + ; + } + } + while (APR_STATUS_IS_EINTR(status) + || APR_STATUS_IS_ECONNABORTED(status) + || APR_STATUS_IS_ECONNRESET(status)); - return shared; + return status + ? svn_error_wrap_apr(status, _("Can't accept client connection")) + : SVN_NO_ERROR; } +/* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless + * that reference gets released using release_shared_pool(). + */ static void -release_shared_pool(struct shared_pool_t *shared) +attach_connection(connection_t *connection) { - if (svn_atomic_dec(&shared->count) == 0) - svn_pool_destroy(shared->pool); + svn_atomic_inc(&connection->ref_count); } -#endif -/* "Arguments" passed from the main thread to the connection thread */ -struct serve_thread_t { - svn_ra_svn_conn_t *conn; - serve_params_t *params; - struct shared_pool_t *shared_pool; -}; +/* Release a reference to CONNECTION. If there are no more references, + * the connection will be + */ +static void +close_connection(connection_t *connection) +{ + /* this will automatically close USOCK */ + if (svn_atomic_dec(&connection->ref_count) == 0) + svn_pool_destroy(connection->pool); +} + +/* Wrapper around serve() that takes a socket instead of a connection. + * This is to off-load work from the main thread in threaded and fork modes. + * + * If an error occurs, log it and also return it. + */ +static svn_error_t * +serve_socket(connection_t *connection, + apr_pool_t *pool) +{ + /* process the actual request and log errors */ + svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool); + if (err) + logger__log_error(connection->params->logger, err, NULL, + get_client_info(connection->conn, connection->params, + pool)); + + return svn_error_trace(err); +} #if APR_HAS_THREADS + +/* allocate and recycle root pools for connection objects. + There should be at most THREADPOOL_MAX_SIZE such pools. */ +static svn_root_pools__t *connection_pools; + +/* The global thread pool serving all connections. */ +static apr_thread_pool_t *threads; + +/* Very simple load determination callback for serve_interruptable: + With less than half the threads in THREADS in use, we can afford to + wait in the socket read() function. Otherwise, poll them round-robin. */ +static svn_boolean_t +is_busy(connection_t *connection) +{ + return apr_thread_pool_threads_count(threads) * 2 + > apr_thread_pool_thread_max_get(threads); +} + +/* Serve the connection given by DATA. Under high load, serve only + the current command (if any) and then put the connection back into + THREAD's task pool. */ static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data) { - struct serve_thread_t *d = data; + svn_boolean_t done; + connection_t *connection = data; + svn_error_t *err; + + apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools); + + /* process the actual request and log errors */ + err = serve_interruptable(&done, connection, is_busy, pool); + if (err) + { + logger__log_error(connection->params->logger, err, NULL, + get_client_info(connection->conn, connection->params, + pool)); + svn_error_clear(err); + done = TRUE; + } + svn_root_pools__release_pool(pool, connection_pools); - svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool)); - release_shared_pool(d->shared_pool); + /* Close or re-schedule connection. */ + if (done) + close_connection(connection); + else + apr_thread_pool_push(threads, serve_thread, connection, 0, NULL); return NULL; } + #endif /* Write the PID of the current process as a decimal number, followed by a @@ -469,31 +656,33 @@ check_lib_versions(void) } -int main(int argc, const char *argv[]) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { enum run_mode run_mode = run_mode_unspecified; svn_boolean_t foreground = FALSE; - apr_socket_t *sock, *usock; - apr_file_t *in_file, *out_file; + apr_socket_t *sock; apr_sockaddr_t *sa; - apr_pool_t *pool; - apr_pool_t *connection_pool; svn_error_t *err; apr_getopt_t *os; int opt; serve_params_t params; const char *arg; apr_status_t status; - svn_ra_svn_conn_t *conn; +#ifndef WIN32 apr_proc_t proc; -#if APR_HAS_THREADS - apr_threadattr_t *tattr; - apr_thread_t *tid; - struct shared_pool_t *shared_pool; - - struct serve_thread_t *thread_data; #endif + svn_boolean_t is_multi_threaded; enum connection_handling_mode handling_mode = CONNECTION_DEFAULT; + svn_boolean_t cache_fulltexts = TRUE; + svn_boolean_t cache_txdeltas = TRUE; + svn_boolean_t cache_revprops = FALSE; + svn_boolean_t use_block_read = FALSE; apr_uint16_t port = SVN_RA_SVN_PORT; const char *host = NULL; int family = APR_INET; @@ -509,31 +698,19 @@ int main(int argc, const char *argv[]) const char *pid_filename = NULL; const char *log_filename = NULL; svn_node_kind_t kind; - - /* Initialize the app. */ - if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS) - return EXIT_FAILURE; - - /* Create our top-level pool. */ - pool = svn_pool_create(NULL); - + apr_size_t min_thread_count = THREADPOOL_MIN_SIZE; + apr_size_t max_thread_count = THREADPOOL_MAX_SIZE; #ifdef SVN_HAVE_SASL - SVN_INT_ERR(cyrus_init(pool)); + SVN_ERR(cyrus_init(pool)); #endif /* Check library versions */ - err = check_lib_versions(); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + SVN_ERR(check_lib_versions()); /* Initialize the FS library. */ - err = svn_fs_initialize(pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + SVN_ERR(svn_fs_initialize(pool)); - err = svn_cmdline__getopt_init(&os, argc, argv, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); params.root = "/"; params.tunnel = FALSE; @@ -542,13 +719,13 @@ int main(int argc, const char *argv[]) params.base = NULL; params.cfg = NULL; params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; - params.log_file = NULL; + params.logger = NULL; + params.config_pool = NULL; + params.authz_pool = NULL; + params.fs_config = NULL; params.vhost = FALSE; params.username_case = CASE_ASIS; params.memory_cache_size = (apr_uint64_t)-1; - params.cache_fulltexts = TRUE; - params.cache_txdeltas = FALSE; - params.cache_revprops = FALSE; params.zero_copy_limit = 0; params.error_check_interval = 4096; @@ -558,7 +735,11 @@ int main(int argc, const char *argv[]) if (APR_STATUS_IS_EOF(status)) break; if (status != APR_SUCCESS) - usage(argv[0], pool); + { + usage(argv[0], pool); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } switch (opt) { case '6': @@ -570,7 +751,7 @@ int main(int argc, const char *argv[]) case 'h': help(pool); - break; + return SVN_NO_ERROR; case 'q': quiet = TRUE; @@ -611,10 +792,8 @@ int main(int argc, const char *argv[]) err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10); if (err) - return svn_cmdline_handle_exit_error( - svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, - _("Invalid port '%s'"), arg), - pool, "svnserve: "); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err, + _("Invalid port '%s'"), arg); port = (apr_uint16_t)val; } break; @@ -644,23 +823,18 @@ int main(int argc, const char *argv[]) break; case 'r': - SVN_INT_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(¶ms.root, arg, pool)); - err = svn_io_check_resolved_path(params.root, &kind, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool)); if (kind != svn_node_dir) { - svn_error_clear - (svn_cmdline_fprintf - (stderr, pool, - _("svnserve: Root path '%s' does not exist " - "or is not a directory.\n"), params.root)); - return EXIT_FAILURE; + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Root path '%s' does not exist " + "or is not a directory"), params.root); } params.root = svn_dirent_internal_style(params.root, pool); - SVN_INT_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool)); + SVN_ERR(svn_dirent_get_absolute(¶ms.root, params.root, pool)); break; case 'R': @@ -685,18 +859,19 @@ int main(int argc, const char *argv[]) break; case SVNSERVE_OPT_CACHE_TXDELTAS: - params.cache_txdeltas - = svn_tristate__from_word(arg) == svn_tristate_true; + cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true; break; case SVNSERVE_OPT_CACHE_FULLTEXTS: - params.cache_fulltexts - = svn_tristate__from_word(arg) == svn_tristate_true; + cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true; break; case SVNSERVE_OPT_CACHE_REVPROPS: - params.cache_revprops - = svn_tristate__from_word(arg) == svn_tristate_true; + cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true; + break; + + case SVNSERVE_OPT_BLOCK_READ: + use_block_read = svn_tristate__from_word(arg) == svn_tristate_true; break; case SVNSERVE_OPT_CLIENT_SPEED: @@ -716,6 +891,14 @@ int main(int argc, const char *argv[]) } break; + case SVNSERVE_OPT_MIN_THREADS: + min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0); + break; + + case SVNSERVE_OPT_MAX_THREADS: + max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0); + break; + #ifdef WIN32 case SVNSERVE_OPT_SERVICE: if (run_mode != run_mode_service) @@ -727,17 +910,16 @@ int main(int argc, const char *argv[]) #endif case SVNSERVE_OPT_CONFIG_FILE: - SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool)); config_filename = svn_dirent_internal_style(config_filename, pool); - SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename, - pool)); + SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename, + pool)); break; case SVNSERVE_OPT_PID_FILE: - SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool)); pid_filename = svn_dirent_internal_style(pid_filename, pool); - SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, - pool)); + SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool)); break; case SVNSERVE_OPT_VIRTUAL_HOST: @@ -745,10 +927,9 @@ int main(int argc, const char *argv[]) break; case SVNSERVE_OPT_LOG_FILE: - SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool)); log_filename = svn_dirent_internal_style(log_filename, pool); - SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename, - pool)); + SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool)); break; } @@ -756,12 +937,16 @@ int main(int argc, const char *argv[]) if (is_version) { - SVN_INT_ERR(version(quiet, pool)); - exit(0); + SVN_ERR(version(quiet, pool)); + return SVN_NO_ERROR; } if (os->ind != argc) - usage(argv[0], pool); + { + usage(argv[0], pool); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; + } if (mode_opt_count != 1) { @@ -774,6 +959,8 @@ int main(int argc, const char *argv[]) #endif stderr, pool)); usage(argv[0], pool); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } if (handling_opt_count > 1) @@ -782,65 +969,83 @@ int main(int argc, const char *argv[]) _("You may only specify one of -T or --single-thread\n"), stderr, pool)); usage(argv[0], pool); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } + /* construct object pools */ + is_multi_threaded = handling_mode == connection_mode_thread; + params.fs_config = apr_hash_make(pool); + svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, + cache_txdeltas ? "1" :"0"); + svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, + cache_fulltexts ? "1" :"0"); + svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, + cache_revprops ? "2" :"0"); + svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, + use_block_read ? "1" :"0"); + + SVN_ERR(svn_repos__config_pool_create(¶ms.config_pool, + is_multi_threaded, + pool)); + SVN_ERR(svn_repos__authz_pool_create(¶ms.authz_pool, + params.config_pool, + is_multi_threaded, + pool)); + /* If a configuration file is specified, load it and any referenced * password and authorization files. */ if (config_filename) { params.base = svn_dirent_dirname(config_filename, pool); - SVN_INT_ERR(svn_config_read3(¶ms.cfg, config_filename, - TRUE, /* must_exist */ - FALSE, /* section_names_case_sensitive */ - FALSE, /* option_names_case_sensitive */ - pool)); + SVN_ERR(svn_repos__config_pool_get(¶ms.cfg, NULL, + params.config_pool, + config_filename, + TRUE, /* must_exist */ + FALSE, /* names_case_sensitive */ + NULL, + pool)); } if (log_filename) - SVN_INT_ERR(svn_io_file_open(¶ms.log_file, log_filename, - APR_WRITE | APR_CREATE | APR_APPEND, - APR_OS_DEFAULT, pool)); + SVN_ERR(logger__create(¶ms.logger, log_filename, pool)); + else if (run_mode == run_mode_listen_once) + SVN_ERR(logger__create_for_stderr(¶ms.logger, pool)); if (params.tunnel_user && run_mode != run_mode_tunnel) { - svn_error_clear - (svn_cmdline_fprintf - (stderr, pool, - _("Option --tunnel-user is only valid in tunnel mode.\n"))); - exit(1); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Option --tunnel-user is only valid in tunnel mode")); } if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel) { + apr_pool_t *connection_pool; + svn_ra_svn_conn_t *conn; + svn_stream_t *stdin_stream; + svn_stream_t *stdout_stream; + params.tunnel = (run_mode == run_mode_tunnel); apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null, redirect_stdout); - status = apr_file_open_stdin(&in_file, pool); - if (status) - { - err = svn_error_wrap_apr(status, _("Can't open stdin")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); - } - status = apr_file_open_stdout(&out_file, pool); - if (status) - { - err = svn_error_wrap_apr(status, _("Can't open stdout")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); - } + SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool)); + SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); /* Use a subpool for the connection to ensure that if SASL is used * the pool cleanup handlers that call sasl_dispose() (connection_pool) * and sasl_done() (pool) are run in the right order. See issue #3664. */ connection_pool = svn_pool_create(pool); - conn = svn_ra_svn_create_conn3(NULL, in_file, out_file, + conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream, params.compression_level, params.zero_copy_limit, params.error_check_interval, connection_pool); - svn_error_clear(serve(conn, ¶ms, connection_pool)); - exit(0); + err = serve(conn, ¶ms, connection_pool); + svn_pool_destroy(connection_pool); + + return err; } #ifdef WIN32 @@ -885,7 +1090,8 @@ int main(int argc, const char *argv[]) } svn_error_clear(err); - exit(1); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* The service is now in the "starting" state. Before the SCM will @@ -930,8 +1136,7 @@ int main(int argc, const char *argv[]) sockaddr_info_flags, pool); if (status) { - err = svn_error_wrap_apr(status, _("Can't get address info")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + return svn_error_wrap_apr(status, _("Can't get address info")); } @@ -944,25 +1149,32 @@ int main(int argc, const char *argv[]) #endif if (status) { - err = svn_error_wrap_apr(status, _("Can't create server socket")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + return svn_error_wrap_apr(status, _("Can't create server socket")); } /* Prevents "socket in use" errors when server is killed and quickly * restarted. */ - apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); + status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); + if (status) + { + return svn_error_wrap_apr(status, _("Can't set options on server socket")); + } status = apr_socket_bind(sock, sa); if (status) { - err = svn_error_wrap_apr(status, _("Can't bind server socket")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + return svn_error_wrap_apr(status, _("Can't bind server socket")); } - apr_socket_listen(sock, 7); + status = apr_socket_listen(sock, ACCEPT_BACKLOG); + if (status) + { + return svn_error_wrap_apr(status, _("Can't listen on server socket")); + } #if APR_HAS_FORK if (run_mode != run_mode_listen_once && !foreground) + /* ### ignoring errors... */ apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); apr_signal(SIGCHLD, sigchld_handler); @@ -981,7 +1193,7 @@ int main(int argc, const char *argv[]) #endif if (pid_filename) - SVN_INT_ERR(write_pid_file(pid_filename, pool)); + SVN_ERR(write_pid_file(pid_filename, pool)); #ifdef WIN32 status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock); @@ -1021,107 +1233,72 @@ int main(int argc, const char *argv[]) svn_cache_config_set(&settings); } - while (1) - { -#ifdef WIN32 - if (winservice_is_stopping()) - return ERROR_SUCCESS; -#endif - - /* Non-standard pool handling. The main thread never blocks to join - the connection threads so it cannot clean up after each one. So - separate pools that can be cleared at thread exit are used. */ - - connection_pool - = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); +#if APR_HAS_THREADS + SVN_ERR(svn_root_pools__create(&connection_pools)); - status = apr_socket_accept(&usock, sock, connection_pool); - if (handling_mode == connection_mode_fork) - { - /* Collect any zombie child processes. */ - while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, - connection_pool) == APR_CHILD_DONE) - ; - } - if (APR_STATUS_IS_EINTR(status) - || APR_STATUS_IS_ECONNABORTED(status) - || APR_STATUS_IS_ECONNRESET(status)) - { - svn_pool_destroy(connection_pool); - continue; - } + if (handling_mode == connection_mode_thread) + { + /* create the thread pool with a valid range of threads */ + if (max_thread_count < 1) + max_thread_count = 1; + if (min_thread_count > max_thread_count) + min_thread_count = max_thread_count; + + status = apr_thread_pool_create(&threads, + min_thread_count, + max_thread_count, + pool); if (status) { - err = svn_error_wrap_apr - (status, _("Can't accept client connection")); - return svn_cmdline_handle_exit_error(err, pool, "svnserve: "); + return svn_error_wrap_apr(status, _("Can't create thread pool")); } - /* Enable TCP keep-alives on the socket so we time out when - * the connection breaks due to network-layer problems. - * If the peer has dropped the connection due to a network partition - * or a crash, or if the peer no longer considers the connection - * valid because we are behind a NAT and our public IP has changed, - * it will respond to the keep-alive probe with a RST instead of an - * acknowledgment segment, which will cause svn to abort the session - * even while it is currently blocked waiting for data from the peer. */ - status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1); - if (status) - { - /* It's not a fatal error if we cannot enable keep-alives. */ - } + /* let idle threads linger for a while in case more requests are + coming in */ + apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT); - conn = svn_ra_svn_create_conn3(usock, NULL, NULL, - params.compression_level, - params.zero_copy_limit, - params.error_check_interval, - connection_pool); + /* don't queue requests unless we reached the worker thread limit */ + apr_thread_pool_threshold_set(threads, 0); + } + else + { + threads = NULL; + } +#endif + while (1) + { + connection_t *connection = NULL; + SVN_ERR(accept_connection(&connection, sock, ¶ms, handling_mode, + pool)); if (run_mode == run_mode_listen_once) { - err = serve(conn, ¶ms, connection_pool); - - if (err) - svn_handle_error2(err, stdout, FALSE, "svnserve: "); - svn_error_clear(err); - - apr_socket_close(usock); - apr_socket_close(sock); - exit(0); + err = serve_socket(connection, connection->pool); + close_connection(connection); + return err; } switch (handling_mode) { case connection_mode_fork: #if APR_HAS_FORK - status = apr_proc_fork(&proc, connection_pool); + status = apr_proc_fork(&proc, connection->pool); if (status == APR_INCHILD) { + /* the child would't listen to the main server's socket */ apr_socket_close(sock); - err = serve(conn, ¶ms, connection_pool); - log_error(err, params.log_file, - svn_ra_svn_conn_remote_host(conn), - NULL, NULL, /* user, repos */ - connection_pool); - svn_error_clear(err); - apr_socket_close(usock); - exit(0); - } - else if (status == APR_INPARENT) - { - apr_socket_close(usock); + + /* serve_socket() logs any error it returns, so ignore it. */ + svn_error_clear(serve_socket(connection, connection->pool)); + close_connection(connection); + return SVN_NO_ERROR; } - else + else if (status != APR_INPARENT) { err = svn_error_wrap_apr(status, "apr_proc_fork"); - log_error(err, params.log_file, - svn_ra_svn_conn_remote_host(conn), - NULL, NULL, /* user, repos */ - connection_pool); + logger__log_error(params.logger, err, NULL, NULL); svn_error_clear(err); - apr_socket_close(usock); } - svn_pool_destroy(connection_pool); #endif break; @@ -1130,46 +1307,64 @@ int main(int argc, const char *argv[]) particularly sophisticated strategy for a threaded server, it's little different from forking one process per connection. */ #if APR_HAS_THREADS - shared_pool = attach_shared_pool(connection_pool); - status = apr_threadattr_create(&tattr, connection_pool); - if (status) - { - err = svn_error_wrap_apr(status, _("Can't create threadattr")); - svn_handle_error2(err, stderr, FALSE, "svnserve: "); - svn_error_clear(err); - exit(1); - } - status = apr_threadattr_detach_set(tattr, 1); - if (status) - { - err = svn_error_wrap_apr(status, _("Can't set detached state")); - svn_handle_error2(err, stderr, FALSE, "svnserve: "); - svn_error_clear(err); - exit(1); - } - thread_data = apr_palloc(connection_pool, sizeof(*thread_data)); - thread_data->conn = conn; - thread_data->params = ¶ms; - thread_data->shared_pool = shared_pool; - status = apr_thread_create(&tid, tattr, serve_thread, thread_data, - shared_pool->pool); + attach_connection(connection); + + status = apr_thread_pool_push(threads, serve_thread, connection, + 0, NULL); if (status) { - err = svn_error_wrap_apr(status, _("Can't create thread")); - svn_handle_error2(err, stderr, FALSE, "svnserve: "); - svn_error_clear(err); - exit(1); + return svn_error_wrap_apr(status, _("Can't push task")); } - release_shared_pool(shared_pool); #endif break; case connection_mode_single: /* Serve one connection at a time. */ - svn_error_clear(serve(conn, ¶ms, connection_pool)); - svn_pool_destroy(connection_pool); + /* serve_socket() logs any error it returns, so ignore it. */ + svn_error_clear(serve_socket(connection, connection->pool)); } + + close_connection(connection); } /* NOTREACHED */ } + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS) + return EXIT_FAILURE; + + /* Create our top-level pool. */ + pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnserve: "); + } + +#if APR_HAS_THREADS + /* Explicitly wait for all threads to exit. As we found out with similar + code in our C test framework, the memory pool cleanup below cannot be + trusted to do the right thing. */ + if (threads) + apr_thread_pool_destroy(threads); +#endif + + /* this will also close the server's socket */ + svn_pool_destroy(pool); + return exit_code; +} diff --git a/contrib/subversion/subversion/svnsync/svnsync.c b/contrib/subversion/subversion/svnsync/svnsync.c index 621b782d6..6dde1f108 100644 --- a/contrib/subversion/subversion/svnsync/svnsync.c +++ b/contrib/subversion/subversion/svnsync/svnsync.c @@ -38,7 +38,6 @@ #include "private/svn_opt_private.h" #include "private/svn_ra_private.h" #include "private/svn_cmdline_private.h" -#include "private/svn_subr_private.h" #include "sync.h" @@ -69,6 +68,8 @@ enum svnsync__opt { svnsync_opt_disable_locking, svnsync_opt_version, svnsync_opt_trust_server_cert, + svnsync_opt_trust_server_cert_failures_src, + svnsync_opt_trust_server_cert_failures_dst, svnsync_opt_allow_non_empty, svnsync_opt_steal_lock }; @@ -79,6 +80,8 @@ enum svnsync__opt { svnsync_opt_auth_username, \ svnsync_opt_auth_password, \ svnsync_opt_trust_server_cert, \ + svnsync_opt_trust_server_cert_failures_src, \ + svnsync_opt_trust_server_cert_failures_dst, \ svnsync_opt_source_username, \ svnsync_opt_source_password, \ svnsync_opt_sync_username, \ @@ -113,7 +116,7 @@ static const svn_opt_subcommand_desc2_t svnsync_cmd_table[] = "mirror of the source repository.\n"), { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q', svnsync_opt_allow_non_empty, svnsync_opt_disable_locking, - svnsync_opt_steal_lock } }, + svnsync_opt_steal_lock, 'M' } }, { "synchronize", synchronize_cmd, { "sync" }, N_("usage: svnsync synchronize DEST_URL [SOURCE_URL]\n" "\n" @@ -126,7 +129,7 @@ static const svn_opt_subcommand_desc2_t svnsync_cmd_table[] = "if untrusted users/administrators may have write access to the\n" "DEST_URL repository.\n"), { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q', - svnsync_opt_disable_locking, svnsync_opt_steal_lock } }, + svnsync_opt_disable_locking, svnsync_opt_steal_lock, 'M' } }, { "copy-revprops", copy_revprops_cmd, { 0 }, N_("usage:\n" "\n" @@ -147,7 +150,7 @@ static const svn_opt_subcommand_desc2_t svnsync_cmd_table[] = "\n" "Form 2 is deprecated syntax, equivalent to specifying \"-rREV[:REV2]\".\n"), { SVNSYNC_OPTS_DEFAULT, svnsync_opt_source_prop_encoding, 'q', 'r', - svnsync_opt_disable_locking, svnsync_opt_steal_lock } }, + svnsync_opt_disable_locking, svnsync_opt_steal_lock, 'M' } }, { "info", info_cmd, { 0 }, N_("usage: svnsync info DEST_URL\n" "\n" @@ -195,11 +198,37 @@ static const apr_getopt_option_t svnsync_options[] = " " "see --source-password and --sync-password)") }, {"trust-server-cert", svnsync_opt_trust_server_cert, 0, - N_("accept SSL server certificates from unknown\n" + N_("deprecated; same as\n" + " " + "--source-trust-server-cert-failures=unknown-ca\n" + " " + "--sync-trust-server-cert-failures=unknown-ca")}, + {"source-trust-server-cert-failures", svnsync_opt_trust_server_cert_failures_src, 1, + N_("with --non-interactive, accept SSL\n" + " " + "server certificates with failures.\n" + " " + "ARG is a comma-separated list of:\n" + " " + "- 'unknown-ca' (Unknown Authority)\n" + " " + "- 'cn-mismatch' (Hostname mismatch)\n" + " " + "- 'expired' (Expired certificate)\n" + " " + "- 'not-yet-valid' (Not yet valid certificate)\n" + " " + "- 'other' (all other not separately classified\n" + " " + " certificate errors).\n" + " " + "Applied to the source URL.")}, + {"sync-trust-server-cert-failures", svnsync_opt_trust_server_cert_failures_dst, 1, + N_("Like\n" " " - "certificate authorities without prompting (but only\n" + "--source-trust-server-cert-failures,\n" " " - "with '--non-interactive')") }, + "but applied to the destination URL.")}, {"source-username", svnsync_opt_source_username, 1, N_("connect to source repository with username ARG") }, {"source-password", svnsync_opt_source_password, 1, @@ -238,6 +267,10 @@ static const apr_getopt_option_t svnsync_options[] = "and is not being concurrently accessed by another\n" " " "svnsync instance.")}, + {"memory-cache-size", 'M', 1, + N_("size of the extra in-memory cache in MB used to\n" + " " + "minimize operations for local 'file' scheme.\n")}, {"version", svnsync_opt_version, 0, N_("show program version information")}, {"help", 'h', 0, @@ -249,7 +282,13 @@ static const apr_getopt_option_t svnsync_options[] = typedef struct opt_baton_t { svn_boolean_t non_interactive; - svn_boolean_t trust_server_cert; + struct { + svn_boolean_t trust_server_cert_unknown_ca; + svn_boolean_t trust_server_cert_cn_mismatch; + svn_boolean_t trust_server_cert_expired; + svn_boolean_t trust_server_cert_not_yet_valid; + svn_boolean_t trust_server_cert_other_failure; + } src_trust, dst_trust; svn_boolean_t no_auth_cache; svn_auth_baton_t *source_auth_baton; svn_auth_baton_t *sync_auth_baton; @@ -483,7 +522,7 @@ remove_props_not_in_source(svn_ra_session_t *session, hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_pool_clear(subpool); @@ -527,8 +566,8 @@ filter_props(int *filtered_count, apr_hash_t *props, for (hi = apr_hash_first(pool, props); hi ; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); - void *propval = svn__apr_hash_index_val(hi); + const char *propname = apr_hash_this_key(hi); + void *propval = apr_hash_this_val(hi); /* Copy all properties: - not matching the exclude pattern if provided OR @@ -569,8 +608,8 @@ write_revprops(int *filtered_count, 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); + const char *propname = apr_hash_this_key(hi); + const svn_string_t *propval = apr_hash_this_val(hi); svn_pool_clear(subpool); @@ -729,8 +768,10 @@ open_target_session(svn_ra_session_t **to_session_p, /*** `svnsync init' ***/ /* Initialize the repository associated with RA session TO_SESSION, - * using information found in BATON, while the repository is - * locked. Implements `with_locked_func_t' interface. + * using information found in BATON. + * + * Implements `with_locked_func_t' interface. The caller has + * acquired a lock on the repository if locking is needed. */ static svn_error_t * do_initialize(svn_ra_session_t *to_session, @@ -937,7 +978,8 @@ open_source_session(svn_ra_session_t **from_session, /* ### TODO: Should we validate that FROM_URL_STR->data matches any provided FROM_URL here? */ if (! from_url) - from_url = from_url_str->data; + SVN_ERR(svn_opt__arg_canonicalize_url(&from_url, from_url_str->data, + pool)); /* Open the session to copy the revision data. */ SVN_ERR(svn_ra_open4(from_session, NULL, from_url, from_uuid_str->data, @@ -967,14 +1009,18 @@ open_target_session(svn_ra_session_t **target_session_p, typedef struct replay_baton_t { svn_ra_session_t *from_session; svn_ra_session_t *to_session; - /* Extra 'backdoor' session for fetching data *from* the target repo. */ - svn_ra_session_t *extra_to_session; svn_revnum_t current_revision; subcommand_baton_t *sb; svn_boolean_t has_commit_revprops_capability; + svn_boolean_t has_atomic_revprops_capability; int normalized_rev_props_count; int normalized_node_props_count; const char *to_root; + +#ifdef ENABLE_EV2_SHIMS + /* Extra 'backdoor' session for fetching data *from* the target repo. */ + svn_ra_session_t *extra_to_session; +#endif } replay_baton_t; /* Return a replay baton allocated from POOL and populated with @@ -1051,7 +1097,7 @@ filter_include_log(const char *key) return ! filter_exclude_log(key); } - +#ifdef ENABLE_EV2_SHIMS static svn_error_t * fetch_base_func(const char **filename, void *baton, @@ -1179,6 +1225,7 @@ get_shim_callbacks(replay_baton_t *rb, return callbacks; } +#endif /* Callback function for svn_ra_replay_range, invoked when starting to parse @@ -1247,8 +1294,10 @@ replay_rev_started(svn_revnum_t revision, rb->sb->source_prop_encoding, pool)); rb->normalized_rev_props_count += normalized_count; +#ifdef ENABLE_EV2_SHIMS SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->to_session, get_shim_callbacks(rb, pool))); +#endif SVN_ERR(svn_ra_get_commit_editor3(rb->to_session, &commit_editor, &commit_baton, filtered, @@ -1291,6 +1340,7 @@ replay_rev_finished(svn_revnum_t revision, apr_hash_t *filtered, *existing_props; int filtered_count; int normalized_count; + const svn_string_t *rev_str; SVN_ERR(editor->close_edit(edit_baton, pool)); @@ -1330,21 +1380,24 @@ replay_rev_finished(svn_revnum_t revision, svn_pool_clear(subpool); + rev_str = svn_string_createf(subpool, "%ld", revision); + /* Ok, we're done, bring the last-merged-rev property up to date. */ SVN_ERR(svn_ra_change_rev_prop2( rb->to_session, 0, SVNSYNC_PROP_LAST_MERGED_REV, NULL, - svn_string_create(apr_psprintf(pool, "%ld", revision), - subpool), + rev_str, subpool)); /* And finally drop the currently copying prop, since we're done with this revision. */ SVN_ERR(svn_ra_change_rev_prop2(rb->to_session, 0, SVNSYNC_PROP_CURRENTLY_COPYING, - NULL, NULL, subpool)); + rb->has_atomic_revprops_capability + ? &rev_str : NULL, + NULL, subpool)); /* Notify the user that we copied revision properties. */ if (! rb->sb->quiet) @@ -1356,8 +1409,10 @@ replay_rev_finished(svn_revnum_t revision, } /* Synchronize the repository associated with RA session TO_SESSION, - * using information found in BATON, while the repository is - * locked. Implements `with_locked_func_t' interface. + * using information found in BATON. + * + * Implements `with_locked_func_t' interface. The caller has + * acquired a lock on the repository if locking is needed. */ static svn_error_t * do_synchronize(svn_ra_session_t *to_session, @@ -1474,6 +1529,11 @@ do_synchronize(svn_ra_session_t *to_session, SVN_RA_CAPABILITY_COMMIT_REVPROPS, pool)); + SVN_ERR(svn_ra_has_capability(rb->to_session, + &rb->has_atomic_revprops_capability, + SVN_RA_CAPABILITY_ATOMIC_REVPROPS, + pool)); + start_revision = last_merged + 1; end_revision = from_latest; @@ -1545,8 +1605,10 @@ synchronize_cmd(apr_getopt_t *os, void *b, apr_pool_t *pool) /*** `svnsync copy-revprops' ***/ /* Copy revision properties to the repository associated with RA - * session TO_SESSION, using information found in BATON, while the - * repository is locked. Implements `with_locked_func_t' interface. + * session TO_SESSION, using information found in BATON. + * + * Implements `with_locked_func_t' interface. The caller has + * acquired a lock on the repository if locking is needed. */ static svn_error_t * do_copy_revprops(svn_ra_session_t *to_session, @@ -1848,6 +1910,7 @@ help_cmd(apr_getopt_t *os, void *baton, apr_pool_t *pool) const char *header = _("general usage: svnsync SUBCOMMAND DEST_URL [ARGS & OPTIONS ...]\n" + "Subversion repository replication tool.\n" "Type 'svnsync help ' for help on a specific subcommand.\n" "Type 'svnsync --version' to see the program version and RA modules.\n" "\n" @@ -1876,8 +1939,13 @@ help_cmd(apr_getopt_t *os, void *baton, apr_pool_t *pool) /*** Main ***/ -int -main(int argc, const char *argv[]) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { const svn_opt_subcommand_desc2_t *subcommand = NULL; apr_array_header_t *received_opts; @@ -1885,7 +1953,6 @@ main(int argc, const char *argv[]) svn_config_t *config; apr_status_t apr_err; apr_getopt_t *os; - apr_pool_t *pool; svn_error_t *err; int opt_id, i; const char *username = NULL, *source_username = NULL, *sync_username = NULL; @@ -1894,23 +1961,10 @@ main(int argc, const char *argv[]) const char *source_prop_encoding = NULL; svn_boolean_t force_interactive = FALSE; - if (svn_cmdline_init("svnsync", stderr) != EXIT_SUCCESS) - { - return EXIT_FAILURE; - } + /* Check library versions */ + SVN_ERR(check_lib_versions()); - err = check_lib_versions(); - if (err) - return svn_cmdline_handle_exit_error(err, NULL, "svnsync: "); - - /* 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)); - - err = svn_ra_initialize(pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + SVN_ERR(svn_ra_initialize(pool)); /* Initialize the option baton. */ memset(&opt_baton, 0, sizeof(opt_baton)); @@ -1921,14 +1975,12 @@ main(int argc, const char *argv[]) if (argc <= 1) { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } - err = svn_cmdline__getopt_init(&os, argc, argv, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; @@ -1942,9 +1994,9 @@ main(int argc, const char *argv[]) break; else if (apr_err) { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } APR_ARRAY_PUSH(received_opts, int) = opt_id; @@ -1959,8 +2011,31 @@ main(int argc, const char *argv[]) force_interactive = TRUE; break; - case svnsync_opt_trust_server_cert: - opt_baton.trust_server_cert = TRUE; + case svnsync_opt_trust_server_cert: /* backwards compat */ + opt_baton.src_trust.trust_server_cert_unknown_ca = TRUE; + opt_baton.dst_trust.trust_server_cert_unknown_ca = TRUE; + break; + + case svnsync_opt_trust_server_cert_failures_src: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &opt_baton.src_trust.trust_server_cert_unknown_ca, + &opt_baton.src_trust.trust_server_cert_cn_mismatch, + &opt_baton.src_trust.trust_server_cert_expired, + &opt_baton.src_trust.trust_server_cert_not_yet_valid, + &opt_baton.src_trust.trust_server_cert_other_failure, + opt_arg, pool)); + break; + + case svnsync_opt_trust_server_cert_failures_dst: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &opt_baton.dst_trust.trust_server_cert_unknown_ca, + &opt_baton.dst_trust.trust_server_cert_cn_mismatch, + &opt_baton.dst_trust.trust_server_cert_expired, + &opt_baton.dst_trust.trust_server_cert_not_yet_valid, + &opt_baton.dst_trust.trust_server_cert_other_failure, + opt_arg, pool)); break; case svnsync_opt_no_auth_cache: @@ -2006,12 +2081,10 @@ main(int argc, const char *argv[]) apr_array_make(pool, 1, sizeof(svn_cmdline__config_argument_t*)); - err = svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool); - if (!err) - err = svn_cmdline__parse_config_option(config_options, - opt_arg, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(config_options, + opt_arg, "svnsync: ", + pool)); break; case svnsync_opt_source_prop_encoding: @@ -2045,13 +2118,11 @@ main(int argc, const char *argv[]) opt_arg, pool) != 0) { const char *utf8_opt_arg; - err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool); - if (! err) - err = svn_error_createf( + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + return svn_error_createf( SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error in revision argument '%s'"), utf8_opt_arg); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); } /* We only allow numbers and 'HEAD'. */ @@ -2061,13 +2132,28 @@ main(int argc, const char *argv[]) (opt_baton.end_rev.kind != svn_opt_revision_head) && (opt_baton.end_rev.kind != svn_opt_revision_unspecified))) { - err = svn_error_createf( + return svn_error_createf( SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Invalid revision range '%s' provided"), opt_arg); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); } break; + case 'M': + if (!config_options) + config_options = + apr_array_make(pool, 1, + sizeof(svn_cmdline__config_argument_t*)); + + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option( + config_options, + apr_psprintf(pool, + "config:miscellany:memory-cache-size=%s", + opt_arg), + NULL /* won't be used */, + pool)); + break; + case '?': case 'h': opt_baton.help = TRUE; @@ -2075,14 +2161,14 @@ main(int argc, const char *argv[]) default: { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } - if(opt_err) - return svn_cmdline_handle_exit_error(opt_err, pool, "svnsync: "); + if (opt_err) + return opt_err; } if (opt_baton.help) @@ -2092,10 +2178,9 @@ main(int argc, const char *argv[]) * exclusive. */ if (opt_baton.non_interactive && force_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--non-interactive and --force-interactive " - "are mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); } else opt_baton.non_interactive = !svn_cmdline__be_interactive( @@ -2109,12 +2194,11 @@ main(int argc, const char *argv[]) && (source_username || sync_username || source_password || sync_password)) { - err = svn_error_create + return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Cannot use --username or --password with any of " "--source-username, --source-password, --sync-username, " "or --sync-password.\n")); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); } if (username) { @@ -2134,24 +2218,32 @@ main(int argc, const char *argv[]) /* Disallow mixing of --steal-lock and --disable-locking. */ if (opt_baton.steal_lock && opt_baton.disable_locking) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--disable-locking and --steal-lock are " - "mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--disable-locking and --steal-lock are " + "mutually exclusive")); } - /* --trust-server-cert can only be used with --non-interactive */ - if (opt_baton.trust_server_cert && !opt_baton.non_interactive) + /* --trust-* can only be used with --non-interactive */ + if (!opt_baton.non_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--trust-server-cert requires " - "--non-interactive")); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + if (opt_baton.src_trust.trust_server_cert_unknown_ca + || opt_baton.src_trust.trust_server_cert_cn_mismatch + || opt_baton.src_trust.trust_server_cert_expired + || opt_baton.src_trust.trust_server_cert_not_yet_valid + || opt_baton.src_trust.trust_server_cert_other_failure + || opt_baton.dst_trust.trust_server_cert_unknown_ca + || opt_baton.dst_trust.trust_server_cert_cn_mismatch + || opt_baton.dst_trust.trust_server_cert_expired + || opt_baton.dst_trust.trust_server_cert_not_yet_valid + || opt_baton.dst_trust.trust_server_cert_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--source-trust-server-cert-failures " + "and " + "--sync-trust-server-cert-failures require " + "--non-interactive")); } - err = svn_config_ensure(opt_baton.config_dir, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "synsync: "); + SVN_ERR(svn_config_ensure(opt_baton.config_dir, pool)); if (subcommand == NULL) { @@ -2170,9 +2262,9 @@ main(int argc, const char *argv[]) } else { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -2182,9 +2274,9 @@ main(int argc, const char *argv[]) first_arg); if (subcommand == NULL) { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -2205,23 +2297,20 @@ main(int argc, const char *argv[]) svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') { - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); + SVN_ERR(help_cmd(NULL, NULL, pool)); } else { - err = svn_error_createf + return svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Subcommand '%s' doesn't accept option '%s'\n" "Type 'svnsync help %s' for usage.\n"), subcommand->name, optstr, subcommand->name); - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); } } } - err = svn_config_get_config(&opt_baton.config, opt_baton.config_dir, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + SVN_ERR(svn_config_get_config(&opt_baton.config, opt_baton.config_dir, pool)); /* Update the options in the config */ if (config_options) @@ -2263,27 +2352,37 @@ main(int argc, const char *argv[]) apr_signal(SIGXFSZ, SIG_IGN); #endif - err = svn_cmdline_create_auth_baton(&opt_baton.source_auth_baton, - opt_baton.non_interactive, - opt_baton.source_username, - opt_baton.source_password, - opt_baton.config_dir, - opt_baton.no_auth_cache, - opt_baton.trust_server_cert, - config, - check_cancel, NULL, - pool); + err = svn_cmdline_create_auth_baton2( + &opt_baton.source_auth_baton, + opt_baton.non_interactive, + opt_baton.source_username, + opt_baton.source_password, + opt_baton.config_dir, + opt_baton.no_auth_cache, + opt_baton.src_trust.trust_server_cert_unknown_ca, + opt_baton.src_trust.trust_server_cert_cn_mismatch, + opt_baton.src_trust.trust_server_cert_expired, + opt_baton.src_trust.trust_server_cert_not_yet_valid, + opt_baton.src_trust.trust_server_cert_other_failure, + config, + check_cancel, NULL, + pool); if (! err) - err = svn_cmdline_create_auth_baton(&opt_baton.sync_auth_baton, - opt_baton.non_interactive, - opt_baton.sync_username, - opt_baton.sync_password, - opt_baton.config_dir, - opt_baton.no_auth_cache, - opt_baton.trust_server_cert, - config, - check_cancel, NULL, - pool); + err = svn_cmdline_create_auth_baton2( + &opt_baton.sync_auth_baton, + opt_baton.non_interactive, + opt_baton.sync_username, + opt_baton.sync_password, + opt_baton.config_dir, + opt_baton.no_auth_cache, + opt_baton.dst_trust.trust_server_cert_unknown_ca, + opt_baton.dst_trust.trust_server_cert_cn_mismatch, + opt_baton.dst_trust.trust_server_cert_expired, + opt_baton.dst_trust.trust_server_cert_not_yet_valid, + opt_baton.dst_trust.trust_server_cert_other_failure, + config, + check_cancel, NULL, + pool); if (! err) err = (*subcommand->cmd_func)(os, &opt_baton, pool); if (err) @@ -2297,10 +2396,40 @@ main(int argc, const char *argv[]) _("Try 'svnsync help' for more info")); } - return svn_cmdline_handle_exit_error(err, pool, "svnsync: "); + return err; } - svn_pool_destroy(pool); + return SVN_NO_ERROR; +} - return EXIT_SUCCESS; +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnsync", 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); + + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnsync: "); + } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/contrib/subversion/subversion/svnsync/sync.c b/contrib/subversion/subversion/svnsync/sync.c index 4d54ee82c..87730421e 100644 --- a/contrib/subversion/subversion/svnsync/sync.c +++ b/contrib/subversion/subversion/svnsync/sync.c @@ -195,8 +195,8 @@ svnsync_normalize_revprops(apr_hash_t *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); + const char *propname = apr_hash_this_key(hi); + const svn_string_t *propval = apr_hash_this_val(hi); if (svn_prop_needs_translation(propname)) { diff --git a/contrib/subversion/subversion/svnversion/svnversion.c b/contrib/subversion/subversion/svnversion/svnversion.c index 94ac588c4..8e4cefffe 100644 --- a/contrib/subversion/subversion/svnversion/svnversion.c +++ b/contrib/subversion/subversion/svnversion/svnversion.c @@ -29,7 +29,6 @@ #include "private/svn_opt_private.h" #include "private/svn_cmdline_private.h" -#include "private/svn_subr_private.h" #include "svn_private_config.h" @@ -48,7 +47,6 @@ usage(apr_pool_t *pool) { svn_error_clear(svn_cmdline_fprintf (stderr, pool, _("Type 'svnversion --help' for usage.\n"))); - exit(1); } @@ -58,7 +56,10 @@ help(const apr_getopt_option_t *options, apr_pool_t *pool) svn_error_clear (svn_cmdline_fprintf (stdout, pool, - _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n\n" + _("usage: svnversion [OPTIONS] [WC_PATH [TRAIL_URL]]\n" + "Subversion working copy identification tool.\n" + "Type 'svnversion --version' to see the program version.\n" + "\n" " Produce a compact version identifier for the working copy path\n" " WC_PATH. TRAIL_URL is the trailing portion of the URL used to\n" " determine if WC_PATH itself is switched (detection of switches\n" @@ -95,7 +96,6 @@ help(const apr_getopt_option_t *options, apr_pool_t *pool) ++options; } svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n")); - exit(0); } @@ -115,16 +115,19 @@ check_lib_versions(void) } /* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + * * Why is this not an svn subcommand? I have this vague idea that it could * be run as part of the build process, with the output embedded in the svn * program. Obviously we don't want to have to run svn when building svn. */ -int -main(int argc, const char *argv[]) +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { const char *wc_path, *trail_url; const char *local_abspath; - apr_pool_t *pool; svn_wc_revision_status_t *res; svn_boolean_t no_newline = FALSE, committed = FALSE; svn_error_t *err; @@ -144,33 +147,18 @@ main(int argc, const char *argv[]) {0, 0, 0, 0} }; - /* Initialize the app. */ - if (svn_cmdline_init("svnversion", 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, "svnversion: "); + SVN_ERR(check_lib_versions()); #if defined(WIN32) || defined(__CYGWIN__) /* Set the working copy administrative directory name. */ if (getenv("SVN_ASP_DOT_NET_HACK")) { - err = svn_wc_set_adm_dir("_svn", pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnversion: "); + SVN_ERR(svn_wc_set_adm_dir("_svn", pool)); } #endif - err = svn_cmdline__getopt_init(&os, argc, argv, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnversion: "); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = 1; while (1) @@ -181,7 +169,11 @@ main(int argc, const char *argv[]) if (APR_STATUS_IS_EOF(status)) break; if (status != APR_SUCCESS) - usage(pool); /* this will exit() */ + { + *exit_code = EXIT_FAILURE; + usage(pool); + return SVN_NO_ERROR; + } switch (opt) { @@ -196,35 +188,39 @@ main(int argc, const char *argv[]) break; case 'h': help(options, pool); - break; + return SVN_NO_ERROR; case SVNVERSION_OPT_VERSION: is_version = TRUE; break; default: - usage(pool); /* this will exit() */ + *exit_code = EXIT_FAILURE; + usage(pool); + return SVN_NO_ERROR; } } if (is_version) { - SVN_INT_ERR(version(quiet, pool)); - exit(0); + SVN_ERR(version(quiet, pool)); + return SVN_NO_ERROR; } if (os->ind > argc || os->ind < argc - 2) - usage(pool); /* this will exit() */ + { + *exit_code = EXIT_FAILURE; + usage(pool); + return SVN_NO_ERROR; + } - SVN_INT_ERR(svn_utf_cstring_to_utf8(&wc_path, - (os->ind < argc) ? os->argv[os->ind] - : ".", - pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&wc_path, + (os->ind < argc) ? os->argv[os->ind] : ".", + pool)); - SVN_INT_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool)); - SVN_INT_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool)); - SVN_INT_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool)); + SVN_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool)); + SVN_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool)); + SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool)); if (os->ind+1 < argc) - SVN_INT_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1], - pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1], pool)); else trail_url = NULL; @@ -239,63 +235,87 @@ main(int argc, const char *argv[]) svn_error_clear(err); - SVN_INT_ERR(svn_io_check_special_path(local_abspath, &kind, &special, - pool)); + SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &special, pool)); if (special) - SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"), - no_newline ? "" : "\n")); + SVN_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"), + no_newline ? "" : "\n")); else if (kind == svn_node_dir) - SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"), - no_newline ? "" : "\n")); + SVN_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"), + no_newline ? "" : "\n")); else if (kind == svn_node_file) - SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"), - no_newline ? "" : "\n")); + SVN_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"), + no_newline ? "" : "\n")); else { - SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool, - kind == svn_node_none - ? _("'%s' doesn't exist\n") - : _("'%s' is of unknown type\n"), - svn_dirent_local_style(local_abspath, - pool))); - svn_pool_destroy(pool); - return EXIT_FAILURE; + SVN_ERR(svn_cmdline_fprintf(stderr, pool, + kind == svn_node_none + ? _("'%s' doesn't exist\n") + : _("'%s' is of unknown type\n"), + svn_dirent_local_style(local_abspath, + pool))); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } - svn_pool_destroy(pool); - return EXIT_SUCCESS; + return SVN_NO_ERROR; } - SVN_INT_ERR(err); + SVN_ERR(err); if (! SVN_IS_VALID_REVNUM(res->min_rev)) { /* Local uncommitted modifications, no revision info was found. */ - SVN_INT_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, " - "copy or move%s"), - no_newline ? "" : "\n")); - svn_pool_destroy(pool); - return EXIT_SUCCESS; + SVN_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, " + "copy or move%s"), + no_newline ? "" : "\n")); + return SVN_NO_ERROR; } /* Build compact '123[:456]M?S?' string. */ - SVN_INT_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev)); + SVN_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev)); if (res->min_rev != res->max_rev) - SVN_INT_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev)); + SVN_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev)); if (res->modified) - SVN_INT_ERR(svn_cmdline_fputs("M", stdout, pool)); + SVN_ERR(svn_cmdline_fputs("M", stdout, pool)); if (res->switched) - SVN_INT_ERR(svn_cmdline_fputs("S", stdout, pool)); + SVN_ERR(svn_cmdline_fputs("S", stdout, pool)); if (res->sparse_checkout) - SVN_INT_ERR(svn_cmdline_fputs("P", stdout, pool)); + SVN_ERR(svn_cmdline_fputs("P", stdout, pool)); if (! no_newline) - SVN_INT_ERR(svn_cmdline_fputs("\n", stdout, pool)); + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); - svn_pool_destroy(pool); + return SVN_NO_ERROR; +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; + + /* Initialize the app. */ + if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS) + return EXIT_FAILURE; - /* Flush stdout to make sure that the user will see any printing errors. */ - SVN_INT_ERR(svn_cmdline_fflush(stdout)); + /* 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)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); - return EXIT_SUCCESS; + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnversion: "); + } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/contrib/subversion/win-tests.py b/contrib/subversion/win-tests.py index 8a3d423f5..9c9daa307 100644 --- a/contrib/subversion/win-tests.py +++ b/contrib/subversion/win-tests.py @@ -24,13 +24,14 @@ 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: 1692801 $ +# $HeadURL: http://svn.apache.org/repos/asf/subversion/branches/1.9.x/win-tests.py $ +# $LastChangedRevision: 1718291 $ import os, sys, subprocess import filecmp import shutil import traceback +import logging try: # Python >=3.0 import configparser @@ -59,7 +60,6 @@ def _usage_exit(): 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") @@ -84,6 +84,8 @@ def _usage_exit(): 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(" --swig=language : Run the swig perl/python/ruby tests instead of") + print(" 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") @@ -99,6 +101,8 @@ def _usage_exit(): print(" --config-file : Configuration file for tests") print(" --fsfs-sharding : Specify shard size (for fsfs)") print(" --fsfs-packing : Run 'svnadmin pack' automatically") + print(" -q, --quiet : Deprecated; this is the default.") + print(" Use --set-log-level instead.") sys.exit(0) @@ -108,29 +112,20 @@ def _usage_exit(): sys.path.insert(0, os.path.join('build', 'generator')) sys.path.insert(1, 'build') -import gen_win +import gen_win_dependencies +import gen_base 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)) - +gen_obj = gen_win_dependencies.GenDependenciesBase('build.conf', version_header, + cp.items('options')) 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', + 'fsfs-packing', 'fsfs-sharding=', 'javahl', 'swig=', 'list', 'enable-sasl', 'bin=', 'parallel', 'config-file=', 'server-minor-version=', 'log-level=', 'log-to-stdout', 'mode-filter=', 'milestone-filter=', @@ -139,7 +134,7 @@ def _usage_exit(): 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 +base_url, fs_type, verbose, cleanup = None, None, None, None repo_loc = 'local repository.' objdir = 'Debug' log = 'tests.log' @@ -156,6 +151,7 @@ def _usage_exit(): list_tests = None milestone_filter = None test_javahl = None +test_swig = None enable_sasl = None svn_bin = None parallel = None @@ -178,8 +174,7 @@ def _usage_exit(): fs_type = val elif opt in ('-v', '--verbose'): verbose = 1 - elif opt in ('-q', '--quiet'): - quiet = 1 + log_level = logging.DEBUG elif opt in ('-c', '--cleanup'): cleanup = 1 elif opt in ('-t', '--test'): @@ -216,6 +211,11 @@ def _usage_exit(): fsfs_packing = 1 elif opt == '--javahl': test_javahl = 1 + elif opt == '--swig': + if val not in ['perl', 'python', 'ruby']: + sys.stderr.write('Running \'%s\' swig tests not supported (yet).\n' + % (val,)) + test_swig = val elif opt == '--list': list_tests = 1 elif opt == '--milestone-filter': @@ -226,7 +226,7 @@ def _usage_exit(): enable_sasl = 1 base_url = "svn://localhost/" elif opt == '--server-minor-version': - server_minor_version = val + server_minor_version = int(val) elif opt == '--bin': svn_bin = val elif opt in ('-p', '--parallel'): @@ -236,7 +236,7 @@ def _usage_exit(): elif opt == '--log-to-stdout': log_to_stdout = 1 elif opt == '--log-level': - log_level = val + log_level = getattr(logging, val, None) or int(val) elif opt == '--ssl-cert': ssl_cert = val @@ -254,10 +254,14 @@ def _usage_exit(): if not fs_type: fs_type = 'fsfs' -# Don't run bdb tests if they want to test fsfs -if fs_type == 'fsfs': +if fs_type == 'bdb': + all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \ + + gen_obj.scripts + gen_obj.bdb_scripts +else: all_tests = gen_obj.test_progs + gen_obj.scripts +client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)] + if run_httpd: if not httpd_port: httpd_port = random.randrange(1024, 30000) @@ -289,12 +293,18 @@ def create_target_dir(dirname): print("mkdir: %s" % tgt_dir) os.makedirs(tgt_dir) -def copy_changed_file(src, tgt): +def copy_changed_file(src, tgt=None, to_dir=None, cleanup=True): 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 to_dir and not tgt: + tgt = os.path.join(to_dir, os.path.basename(src)) + elif not tgt or (tgt and to_dir): + raise RuntimeError("Using 'tgt' *or* 'to_dir' is required" % (tgt,)) + elif tgt and os.path.isdir(tgt): + raise RuntimeError("'%s' is a directory. Use to_dir=" % (tgt,)) + if os.path.exists(tgt): assert os.path.isfile(tgt) if filecmp.cmp(src, tgt): @@ -306,57 +316,39 @@ def copy_changed_file(src, tgt): 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) + + if cleanup: + copied_execs.append(tgt) def locate_libs(): "Move DLLs to a known location and set env vars" - dlls = [] + debug = (objdir == 'Debug') - # 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 = "" + for lib in gen_obj._libraries.values(): - 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') + if debug: + name, dir = lib.debug_dll_name, lib.debug_dll_dir else: - dlls.append(partial_path + '.dll') + name, dir = lib.dll_name, lib.dll_dir + + if name and dir: + src = os.path.join(dir, name) + if os.path.exists(src): + copy_changed_file(src, to_dir=abs_builddir, cleanup=False) - if gen_obj.sasl_path is not None: - dlls.append(os.path.join(gen_obj.sasl_path, 'lib', 'libsasl.dll')) + for name in lib.extra_bin: + src = os.path.join(dir, name) + copy_changed_file(src, to_dir=abs_builddir) - 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) + for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL): + if isinstance(i, gen_base.TargetLib) and i.msvc_export: + src = os.path.join(abs_objdir, i.filename) + if os.path.isfile(src): + copy_changed_file(src, to_dir=abs_builddir, + cleanup=False) # Copy the Apache modules if run_httpd and cp.has_option('options', '--with-httpd'): @@ -367,11 +359,11 @@ def locate_libs(): 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) + copy_changed_file(mod_dav_svn_path, to_dir=abs_builddir, cleanup=False) + copy_changed_file(mod_authz_svn_path, to_dir=abs_builddir, cleanup=False) + copy_changed_file(mod_dontdothat_path, to_dir=abs_builddir, cleanup=False) - os.environ['PATH'] = abs_objdir + os.pathsep + os.environ['PATH'] + os.environ['PATH'] = abs_builddir + os.pathsep + os.environ['PATH'] def fix_case(path): path = os.path.normpath(path) @@ -396,7 +388,7 @@ def __init__(self, svnserve_args, objdir, abs_objdir, abs_builddir): 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 + self.proc = None def __del__(self): "Stop svnserve when the object is deleted" @@ -414,26 +406,18 @@ def start(self): 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) + + self.proc = subprocess.Popen([self.path] + args[1:]) def stop(self): - if self.proc_handle is not None: + if self.proc is not None: try: - import win32process print('Stopping %s' % self.name) - win32process.TerminateProcess(self.proc_handle, 0) + self.proc.poll(); + if self.proc.returncode is None: + self.proc.kill(); return - except ImportError: + except AttributeError: pass print('Svnserve.stop not implemented') @@ -456,7 +440,7 @@ def __init__(self, abs_httpd_dir, abs_objdir, abs_builddir, httpd_port, self.bulkupdates_option = 'off' self.service = service - self.proc_handle = None + self.proc = None self.path = os.path.join(self.httpd_dir, 'bin', self.name) if short_circuit: @@ -499,15 +483,9 @@ def __init__(self, abs_httpd_dir, abs_objdir, abs_builddir, httpd_port, 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 + # Obtain version. + version_vals = gen_obj._libraries['httpd'].version.split('.') + self.httpd_ver = float('%s.%s' % (version_vals[0], version_vals[1])) # Create httpd config file fp = open(self.httpd_config, 'w') @@ -632,7 +610,7 @@ def _sys_module(self, name, path): return 'LoadModule ' + name + " " + self._quote(full_path) + '\n' def _svn_module(self, name, path): - full_path = os.path.join(self.abs_objdir, path) + full_path = os.path.join(self.abs_builddir, path) return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n' def _svn_repo(self, name): @@ -808,9 +786,9 @@ def _svn_authz_repo(self): ' AuthzSendForbiddenOnFailure On' + '\n' \ ' Satisfy All' + '\n' \ ' ' + '\n' \ - ' Require valid-user' + '\n' \ - ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ - ' ' + '\n' \ + ' Require valid-user' + '\n' \ + ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ + '' + '\n' \ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ '' + '\n' \ '' + '\n' \ @@ -843,46 +821,33 @@ 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) + self.proc = subprocess.Popen([self.path] + self.httpd_args[1:]) def _stop_daemon(self): "Stop the HTTPD daemon" - if self.proc_handle is not None: + if self.proc is not None: try: - import win32process print('Stopping %s' % self.name) - win32process.TerminateProcess(self.proc_handle, 0) + self.proc.poll(); + if self.proc.returncode is None: + self.proc.kill(); return - except ImportError: + except AttributeError: pass print('Httpd.stop_daemon not implemented') # Move the binaries to the test directory +create_target_dir(abs_builddir) 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) + for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL): + if isinstance(i, gen_base.TargetExe): + src = os.path.join(abs_objdir, i.filename) + + if os.path.isfile(src): + dst = os.path.join(abs_builddir, i.filename) + create_target_dir(os.path.dirname(dst)) + copy_changed_file(src, dst) # Create the base directory for Python tests create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH) @@ -940,7 +905,7 @@ def _stop_daemon(self): print('Testing %s configuration on %s' % (objdir, repo_loc)) sys.path.insert(0, os.path.join(abs_srcdir, 'build')) -if not test_javahl: +if not test_javahl and not test_swig: import run_tests if log_to_stdout: log_file = None @@ -950,20 +915,30 @@ def _stop_daemon(self): fail_log_file = os.path.join(abs_builddir, faillog) if run_httpd: - httpd_version = "%.1f" % daemon.httpd_ver + httpd_version = gen_obj._libraries['httpd'].version else: httpd_version = None + + opts, args = run_tests.create_parser().parse_args([]) + opts.url = base_url + opts.fs_type = fs_type + opts.http_library = 'serf' + opts.server_minor_version = server_minor_version + opts.cleanup = cleanup + opts.enable_sasl = enable_sasl + opts.parallel = parallel + opts.config_file = config_file + opts.fsfs_sharding = fsfs_sharding + opts.fsfs_packing = fsfs_packing + opts.list_tests = list_tests + opts.svn_bin = svn_bin + opts.mode_filter = mode_filter + opts.milestone_filter = milestone_filter + opts.httpd_version = httpd_version + opts.set_log_level = log_level + opts.ssl_cert = ssl_cert 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) + log_file, fail_log_file, opts) old_cwd = os.getcwd() try: os.chdir(abs_builddir) @@ -973,44 +948,208 @@ def _stop_daemon(self): raise else: os.chdir(old_cwd) -else: +elif test_javahl: 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'])) + java_exe = None + + for path in os.environ["PATH"].split(os.pathsep): + if os.path.isfile(os.path.join(path, 'java.exe')): + java_exe = os.path.join(path, 'java.exe') + break + + if not java_exe and 'java_sdk' in gen_obj._libraries: + jdk = gen_obj._libraries['java_sdk'] + + if os.path.isfile(os.path.join(jdk.lib_dir, '../bin/java.exe')): + java_exe = os.path.join(jdk.lib_dir, '../bin/java.exe') + + if not java_exe: + print('Java not found. Skipping Java tests') + else: + args = (os.path.abspath(java_exe),) + if (objdir == 'Debug'): + args = args + ('-Xcheck:jni',) + + args = args + ( + '-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 +elif test_swig == 'perl': + failed = False + swig_dir = os.path.join(abs_builddir, 'swig') + swig_pl_dir = os.path.join(swig_dir, 'p5lib') + swig_pl_svn = os.path.join(swig_pl_dir, 'SVN') + swig_pl_auto_svn = os.path.join(swig_pl_dir, 'auto', 'SVN') + + create_target_dir(swig_pl_svn) + + for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL): + if isinstance(i, gen_base.TargetSWIG) and i.lang == 'perl': + mod_dir = os.path.join(swig_pl_auto_svn, '_' + i.name[5:].capitalize()) + create_target_dir(mod_dir) + copy_changed_file(os.path.join(abs_objdir, i.filename), to_dir=mod_dir) + + elif isinstance(i, gen_base.TargetSWIGLib) and i.lang == 'perl': + copy_changed_file(os.path.join(abs_objdir, i.filename), + to_dir=abs_builddir) + + pm_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'perl', + 'native') + + tests = [] + + for root, dirs, files in os.walk(pm_src): + for name in files: + if name.endswith('.pm'): + fn = os.path.join(root, name) + copy_changed_file(fn, to_dir=swig_pl_svn) + elif name.endswith('.t'): + tests.append(os.path.relpath(os.path.join(root, name), pm_src)) + + perl5lib = swig_pl_dir + if 'PERL5LIB' in os.environ: + perl5lib += os.pathsep + os.environ['PERL5LIB'] + + perl_exe = 'perl.exe' + + print('-- Running Swig Perl tests --') sys.stdout.flush() - sys.stderr.flush() + old_cwd = os.getcwd() + try: + os.chdir(pm_src) + + os.environ['PERL5LIB'] = perl5lib + os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES' + + r = subprocess.call([ + perl_exe, + '-MExtUtils::Command::MM', + '-e', 'test_harness()' + ] + tests) + finally: + os.chdir(old_cwd) + if (r != 0): print('[Test runner reported failure]') failed = True +elif test_swig == 'python': + failed = False + swig_dir = os.path.join(abs_builddir, 'swig') + swig_py_dir = os.path.join(swig_dir, 'pylib') + swig_py_libsvn = os.path.join(swig_py_dir, 'libsvn') + swig_py_svn = os.path.join(swig_py_dir, 'svn') - print('Running org.tigris.subversion tests:') - sys.stdout.flush() - r = subprocess.call(args + tuple(['org.tigris.subversion.javahl.RunTests'])) + create_target_dir(swig_py_libsvn) + create_target_dir(swig_py_svn) + + for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL): + if (isinstance(i, gen_base.TargetSWIG) + or isinstance(i, gen_base.TargetSWIGLib)) and i.lang == 'python': + + src = os.path.join(abs_objdir, i.filename) + copy_changed_file(src, to_dir=swig_py_libsvn) + + py_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'python') + + for py_file in os.listdir(py_src): + if py_file.endswith('.py'): + copy_changed_file(os.path.join(py_src, py_file), + to_dir=swig_py_libsvn) + + py_src_svn = os.path.join(py_src, 'svn') + for py_file in os.listdir(py_src_svn): + if py_file.endswith('.py'): + copy_changed_file(os.path.join(py_src_svn, py_file), + to_dir=swig_py_svn) + + print('-- Running Swig Python tests --') sys.stdout.flush() - sys.stderr.flush() - if (r != 0): - print('[Test runner reported failure]') - failed = True + + pythonpath = swig_py_dir + if 'PYTHONPATH' in os.environ: + pythonpath += os.pathsep + os.environ['PYTHONPATH'] + + python_exe = 'python.exe' + old_cwd = os.getcwd() + try: + os.environ['PYTHONPATH'] = pythonpath + + r = subprocess.call([ + python_exe, + os.path.join(py_src, 'tests', 'run_all.py') + ]) + finally: + os.chdir(old_cwd) + + if (r != 0): + print('[Test runner reported failure]') + failed = True + +elif test_swig == 'ruby': + failed = False + + if 'ruby' not in gen_obj._libraries: + print('Ruby not found. Skipping Ruby tests') + else: + ruby_lib = gen_obj._libraries['ruby'] + + ruby_exe = 'ruby.exe' + ruby_subdir = os.path.join('subversion', 'bindings', 'swig', 'ruby') + ruby_args = [ + '-I', os.path.join(abs_srcdir, ruby_subdir), + os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'), + '--verbose' + ] + + print('-- Running Swig Ruby tests --') + sys.stdout.flush() + old_cwd = os.getcwd() + try: + os.chdir(ruby_subdir) + + os.environ["BUILD_TYPE"] = objdir + os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES' + r = subprocess.call([ruby_exe] + ruby_args) + finally: + os.chdir(old_cwd) + + sys.stdout.flush() + sys.stderr.flush() + if (r != 0): + print('[Test runner reported failure]') + failed = True # Stop service daemon, if any if daemon: diff --git a/usr.bin/svn/Makefile b/usr.bin/svn/Makefile index 1d2ae21d5..cafa47d6a 100644 --- a/usr.bin/svn/Makefile +++ b/usr.bin/svn/Makefile @@ -1,8 +1,8 @@ # $FreeBSD$ -SUBDIR = lib .WAIT \ - svn svnadmin svndumpfilter svnlook svnserve svnsync svnversion \ - svnmucc svnrdump +SUBDIR= lib .WAIT \ + svn svnadmin svnbench svndumpfilter svnfsfs svnlook svnserve \ + svnsync svnversion svnmucc svnrdump SUBDIR_PARALLEL= .include diff --git a/usr.bin/svn/Makefile.inc b/usr.bin/svn/Makefile.inc index fb50b2275..353bb0fcc 100644 --- a/usr.bin/svn/Makefile.inc +++ b/usr.bin/svn/Makefile.inc @@ -14,7 +14,7 @@ SVNDIR= ${.CURDIR}/../../../contrib/subversion/subversion APRU= ${.CURDIR}/../../../contrib/apr-util APR= ${.CURDIR}/../../../contrib/apr SQLITE= ${.CURDIR}/../../../contrib/sqlite3 -WARNS?= 0 # defintely not warns friendly +WARNS?= 0 # definitely not warns friendly .if exists(${.CURDIR}/../../Makefile.inc) .include "${.CURDIR}/../../Makefile.inc" @@ -31,6 +31,7 @@ LIBSVN_DIFFDIR= ${.OBJDIR}/../lib/libsvn_diff LIBSVN_FSDIR= ${.OBJDIR}/../lib/libsvn_fs LIBSVN_FS_FSDIR= ${.OBJDIR}/../lib/libsvn_fs_fs LIBSVN_FS_UTILDIR= ${.OBJDIR}/../lib/libsvn_fs_util +LIBSVN_FS_XDIR= ${.OBJDIR}/../lib/libsvn_fs_x LIBSVN_RADIR= ${.OBJDIR}/../lib/libsvn_ra LIBSVN_RA_LOCALDIR= ${.OBJDIR}/../lib/libsvn_ra_local LIBSVN_RA_SVNDIR= ${.OBJDIR}/../lib/libsvn_ra_svn @@ -50,6 +51,7 @@ LIBSVN_DIFF= ${LIBSVN_DIFFDIR}/libsvn_diff.a LIBSVN_FS= ${LIBSVN_FSDIR}/libsvn_fs.a LIBSVN_FS_FS= ${LIBSVN_FS_FSDIR}/libsvn_fs_fs.a LIBSVN_FS_UTIL= ${LIBSVN_FS_UTILDIR}/libsvn_fs_util.a +LIBSVN_FS_X= ${LIBSVN_FS_XDIR}/libsvn_fs_x.a LIBSVN_RA= ${LIBSVN_RADIR}/libsvn_ra.a LIBSVN_RA_LOCAL= ${LIBSVN_RA_LOCALDIR}/libsvn_ra_local.a LIBSVN_RA_SVN= ${LIBSVN_RA_SVNDIR}/libsvn_ra_svn.a diff --git a/usr.bin/svn/lib/Makefile b/usr.bin/svn/lib/Makefile index befbe4a6e..26395e180 100644 --- a/usr.bin/svn/lib/Makefile +++ b/usr.bin/svn/lib/Makefile @@ -2,7 +2,7 @@ SUBDIR= libapr libapr_util libserf libsqlite3 \ libsvn_client libsvn_delta libsvn_diff libsvn_fs libsvn_fs_fs \ - libsvn_fs_util libsvn_ra libsvn_ra_local libsvn_ra_serf \ + libsvn_fs_util libsvn_fs_x libsvn_ra libsvn_ra_local libsvn_ra_serf \ libsvn_ra_svn libsvn_repos libsvn_subr libsvn_wc SUBDIR_PARALLEL= diff --git a/usr.bin/svn/lib/libsvn_client/Makefile b/usr.bin/svn/lib/libsvn_client/Makefile index 2e1949c75..2eb7a2341 100644 --- a/usr.bin/svn/lib/libsvn_client/Makefile +++ b/usr.bin/svn/lib/libsvn_client/Makefile @@ -12,7 +12,7 @@ SRCS= add.c blame.c cat.c changelist.c checkout.c cleanup.c \ copy_foreign.c ctx.c delete.c deprecated.c diff.c \ diff_local.c diff_summarize.c export.c externals.c import.c \ info.c iprops.c list.c locking_commands.c log.c merge.c \ - mergeinfo.c patch.c log.c prop_commands.c \ + mergeinfo.c mtcc.c patch.c log.c prop_commands.c \ ra.c relocate.c repos_diff.c resolved.c revert.c revisions.c \ status.c switch.c update.c upgrade.c url.c util.c version.c diff --git a/usr.bin/svn/lib/libsvn_diff/Makefile b/usr.bin/svn/lib/libsvn_diff/Makefile index 54ff3f297..f9d342c61 100644 --- a/usr.bin/svn/lib/libsvn_diff/Makefile +++ b/usr.bin/svn/lib/libsvn_diff/Makefile @@ -7,8 +7,9 @@ INTERNALLIB= yes LIB= svn_diff -SRCS= deprecated.c diff.c diff3.c diff4.c diff_file.c diff_memory.c \ - diff_tree.c lcs.c parse-diff.c token.c util.c +SRCS= binary_diff.c deprecated.c diff.c diff3.c diff4.c \ + diff_file.c diff_memory.c diff_tree.c lcs.c parse-diff.c \ + token.c util.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/lib/libsvn_fs/Makefile b/usr.bin/svn/lib/libsvn_fs/Makefile index c6c502072..467ac9396 100644 --- a/usr.bin/svn/lib/libsvn_fs/Makefile +++ b/usr.bin/svn/lib/libsvn_fs/Makefile @@ -7,7 +7,7 @@ INTERNALLIB= yes LIB= svn_fs -SRCS= access.c editor.c fs-loader.c +SRCS= access.c deprecated.c editor.c fs-loader.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/lib/libsvn_fs_fs/Makefile b/usr.bin/svn/lib/libsvn_fs_fs/Makefile index b747877e2..eabb4f29d 100644 --- a/usr.bin/svn/lib/libsvn_fs_fs/Makefile +++ b/usr.bin/svn/lib/libsvn_fs_fs/Makefile @@ -7,8 +7,11 @@ INTERNALLIB= yes LIB= svn_fs_fs -SRCS= caching.c dag.c fs.c fs_fs.c id.c key-gen.c lock.c \ - rep-cache.c temp_serializer.c tree.c +SRCS= cached_data.c caching.c dag.c dump-index.c fs.c fs_fs.c \ + hotcopy.c id.c index.c load-index.c lock.c low_level.c \ + pack.c recovery.c rep-cache.c rev_file.c revprops.c \ + stats.c temp_serializer.c transaction.c tree.c util.c \ + verify.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/lib/libsvn_fs_x/Makefile b/usr.bin/svn/lib/libsvn_fs_x/Makefile new file mode 100644 index 000000000..d4b06d2b5 --- /dev/null +++ b/usr.bin/svn/lib/libsvn_fs_x/Makefile @@ -0,0 +1,24 @@ +# $FreeBSD$ + +.include "${.CURDIR}/../Makefile.inc" + +.PATH: ${SVNDIR}/libsvn_fs_x + +INTERNALLIB= yes +LIB= svn_fs_x + +SRCS= cached_data.c caching.c changes.c dag.c fs.c fs_id.c fs_x.c \ + hotcopy.c id.c index.c lock.c low_level.c noderevs.c pack.c \ + recovery.c rep-cache.c reps.c rev_file.c revprops.c \ + string_table.c temp_serializer.c transaction.c tree.c \ + util.c verify.c + +CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ + -I${.CURDIR}/../libapr \ + -I${APR}/include/arch/unix \ + -I${APR}/include \ + -I${.CURDIR}/../libapr_util \ + -I${APRU}/include/private \ + -I${APRU}/include + +.include diff --git a/usr.bin/svn/lib/libsvn_ra_serf/Makefile b/usr.bin/svn/lib/libsvn_ra_serf/Makefile index f9ff6ffbe..74d95574d 100644 --- a/usr.bin/svn/lib/libsvn_ra_serf/Makefile +++ b/usr.bin/svn/lib/libsvn_ra_serf/Makefile @@ -7,11 +7,12 @@ INTERNALLIB= yes LIB= svn_ra_serf -SRCS= blame.c blncache.c commit.c get_deleted_rev.c getdate.c \ +SRCS= blame.c blncache.c commit.c eagain_bucket.c \ + get_deleted_rev.c get_file.c get_lock.c getdate.c \ getlocations.c getlocationsegments.c getlocks.c \ - inherited_props.c locks.c log.c merge.c mergeinfo.c options.c \ - property.c replay.c sb_bucket.c serf.c update.c util.c \ - util_error.c xml.c + inherited_props.c lock.c log.c merge.c mergeinfo.c \ + multistatus.c options.c property.c replay.c sb_bucket.c \ + serf.c stat.c update.c util.c util_error.c xml.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/lib/libsvn_repos/Makefile b/usr.bin/svn/lib/libsvn_repos/Makefile index 92c2b861b..474a29333 100644 --- a/usr.bin/svn/lib/libsvn_repos/Makefile +++ b/usr.bin/svn/lib/libsvn_repos/Makefile @@ -7,9 +7,10 @@ INTERNALLIB= yes LIB= svn_repos -SRCS= authz.c commit.c delta.c deprecated.c dump.c fs-wrap.c \ - hooks.c load-fs-vtable.c load.c log.c node_tree.c notify.c \ - replay.c reporter.c repos.c rev_hunt.c \ +SRCS= authz.c authz_pool.c config_pool.c commit.c delta.c \ + deprecated.c dump.c fs-wrap.c hooks.c load-fs-vtable.c \ + load.c log.c node_tree.c notify.c replay.c reporter.c \ + repos.c rev_hunt.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/lib/libsvn_subr/Makefile b/usr.bin/svn/lib/libsvn_subr/Makefile index 45231973f..0ec200c9b 100644 --- a/usr.bin/svn/lib/libsvn_subr/Makefile +++ b/usr.bin/svn/lib/libsvn_subr/Makefile @@ -7,21 +7,25 @@ INTERNALLIB= yes LIB= svn_subr -SRCS= adler32.c atomic.c auth.c base64.c cache-inprocess.c \ - cache-membuffer.c cache-memcache.c cache.c cache_config.c \ - checksum.c cmdline.c compat.c config.c config_auth.c \ - config_file.c config_win.c crypto.c ctype.c date.c debug.c \ - deprecated.c dirent_uri.c dso.c eol.c error.c gpg_agent.c \ - hash.c io.c iter.c lock.c log.c macos_keychain.c magic.c \ - md5.c mergeinfo.c mutex.c named_atomic.c nls.c opt.c path.c \ - pool.c prompt.c properties.c pseudo_md5.c quoprint.c sha1.c \ - simple_providers.c skel.c sorts.c spillbuf.c sqlite.c \ - sqlite3wrapper.c ssl_client_cert_providers.c \ +SRCS= adler32.c atomic.c auth.c base64.c bit_array.c \ + cache-inprocess.c cache-membuffer.c cache-memcache.c \ + cache.c cache_config.c checksum.c cmdline.c compat.c \ + compress.c config.c config_auth.c config_file.c \ + config_win.c crypto.c ctype.c date.c debug.c \ + deprecated.c dirent_uri.c dso.c eol.c error.c fnv1a.c \ + gpg_agent.c hash.c io.c iter.c lock.c log.c \ + macos_keychain.c magic.c md5.c mergeinfo.c mutex.c nls.c \ + object_pool.c opt.c packed_data.c path.c \ + pool.c prefix_string.c prompt.c properties.c quoprint.c \ + root_pools.c simple_providers.c skel.c sorts.c spillbuf.c \ + sqlite.c sqlite3wrapper.c ssl_client_cert_providers.c \ ssl_client_cert_pw_providers.c ssl_server_trust_providers.c \ - stream.c string.c subst.c sysinfo.c target.c temp_serializer.c \ - time.c token.c types.c user.c username_providers.c utf.c \ + stream.c string.c subst.c sysinfo.c target.c \ + temp_serializer.c time.c token.c types.c user.c \ + username_providers.c utf.c utf8proc.c \ utf_validate.c utf_width.c validate.c version.c \ - win32_crashrpt.c win32_crypto.c win32_xlate.c xml.c \ + win32_crashrpt.c win32_crypto.c win32_xlate.c \ + x509info.c x509parse.c xml.c CFLAGS+= -I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/../.. \ -I${.CURDIR}/../libapr \ diff --git a/usr.bin/svn/svn/Makefile b/usr.bin/svn/svn/Makefile index dcd449ce1..3c6ac0c8c 100644 --- a/usr.bin/svn/svn/Makefile +++ b/usr.bin/svn/svn/Makefile @@ -8,15 +8,16 @@ MAN= PROG= svn${SVNLITE} -SRCS= add-cmd.c blame-cmd.c cat-cmd.c changelist-cmd.c checkout-cmd.c \ - cl-conflicts.c cleanup-cmd.c commit-cmd.c conflict-callbacks.c \ - copy-cmd.c delete-cmd.c deprecated.c diff-cmd.c export-cmd.c \ - file-merge.c help-cmd.c import-cmd.c info-cmd.c list-cmd.c \ - lock-cmd.c log-cmd.c merge-cmd.c mergeinfo-cmd.c mkdir-cmd.c \ - move-cmd.c notify.c patch-cmd.c propdel-cmd.c propedit-cmd.c \ - propget-cmd.c proplist-cmd.c props.c propset-cmd.c relocate-cmd.c \ - resolve-cmd.c resolved-cmd.c revert-cmd.c status-cmd.c status.c \ - svn.c switch-cmd.c unlock-cmd.c update-cmd.c upgrade-cmd.c util.c +SRCS= add-cmd.c auth-cmd.c blame-cmd.c cat-cmd.c changelist-cmd.c \ + checkout-cmd.c cl-conflicts.c cleanup-cmd.c commit-cmd.c \ + conflict-callbacks.c copy-cmd.c delete-cmd.c deprecated.c \ + diff-cmd.c export-cmd.c file-merge.c help-cmd.c import-cmd.c \ + info-cmd.c list-cmd.c lock-cmd.c log-cmd.c merge-cmd.c \ + mergeinfo-cmd.c mkdir-cmd.c move-cmd.c notify.c patch-cmd.c \ + propdel-cmd.c propedit-cmd.c propget-cmd.c proplist-cmd.c \ + props.c propset-cmd.c relocate-cmd.c resolve-cmd.c \ + resolved-cmd.c revert-cmd.c status-cmd.c similarity.c status.c \ + svn.c switch-cmd.c unlock-cmd.c update-cmd.c upgrade-cmd.c util.c CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ -I${.CURDIR}/../lib/libapr \ @@ -35,6 +36,7 @@ LDADD= -L${LIBSVN_CLIENTDIR} -lsvn_client \ -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_DIFFDIR} -lsvn_diff \ diff --git a/usr.bin/svn/svn_private_config.h b/usr.bin/svn/svn_private_config.h index 40da1cb31..4907e98e9 100644 --- a/usr.bin/svn/svn_private_config.h +++ b/usr.bin/svn/svn_private_config.h @@ -17,7 +17,7 @@ /* #undef ENABLE_NLS */ /* Define to 1 if you have the `bind_textdomain_codeset' function. */ -#define HAVE_BIND_TEXTDOMAIN_CODESET 1 +/* #undef HAVE_BIND_TEXTDOMAIN_CODESET */ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 @@ -25,6 +25,9 @@ /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ /* #undef HAVE_DOPRNT */ +/* Define to 1 if you have the `getpid' function. */ +#define HAVE_GETPID 1 + /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -49,6 +52,9 @@ /* Define to 1 if you have the header file. */ #define HAVE_SERF_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 @@ -94,8 +100,7 @@ /* Define to 1 if you have the 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. */ #define LT_OBJDIR ".libs/" /* Define to the address where bug reports for this package should be sent. */ @@ -105,7 +110,7 @@ #define PACKAGE_NAME "subversion" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "subversion 1.8.14" +#define PACKAGE_STRING "subversion 1.9.4" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "subversion" @@ -114,20 +119,17 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.8.14" +#define PACKAGE_VERSION "1.9.4" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 -/* Defined to allow building against httpd 2.4 with broken auth */ +/* Defined to build 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 */ #define SVN_APR_INT64_T_PYCFMT "l" -/* 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 */ #define SVN_BINDIR "/usr/bin" @@ -206,6 +208,9 @@ /* Defined if libsvn_fs should link against libsvn_fs_fs */ #define SVN_LIBSVN_FS_LINKS_FS_FS 1 +/* Defined if libsvn_fs should link against libsvn_fs_x */ +#define SVN_LIBSVN_FS_LINKS_FS_X 1 + /* Defined to be the path to the installed locale dirs */ #define SVN_LOCALE_DIR "NONE/share/locale" @@ -224,6 +229,9 @@ /* Defined if svn should try to load DSOs */ /* #undef SVN_USE_DSO */ +/* Defined to build with patched httpd 2.4 and working auth */ +/* #undef SVN_USE_FORCE_AUTHN */ + /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ @@ -261,3 +269,27 @@ #define dgettext(domain, x) (x) #endif +/* compiler hints */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +# define SVN__PREDICT_FALSE(x) (__builtin_expect(x, 0)) +# define SVN__PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +# define SVN__PREDICT_FALSE(x) (x) +# define SVN__PREDICT_TRUE(x) (x) +#endif + +#if defined(SVN_DEBUG) +# define SVN__FORCE_INLINE +# define SVN__PREVENT_INLINE +#elif defined(__GNUC__) +# define SVN__FORCE_INLINE APR_INLINE __attribute__ ((always_inline)) +# define SVN__PREVENT_INLINE __attribute__ ((noinline)) +#else +# define SVN__FORCE_INLINE APR_INLINE +# define SVN__PREVENT_INLINE +#endif + +/* Macro used to specify that a variable is intentionally left unused. + Supresses compiler warnings about the variable being unused. */ +#define SVN_UNUSED(v) ( (void)(v) ) + diff --git a/usr.bin/svn/svnadmin/Makefile b/usr.bin/svn/svnadmin/Makefile index 1dd585305..3b1f1422a 100644 --- a/usr.bin/svn/svnadmin/Makefile +++ b/usr.bin/svn/svnadmin/Makefile @@ -21,6 +21,7 @@ CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ LDADD= -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_SUBRDIR} -lsvn_subr \ diff --git a/usr.bin/svn/svnbench/Makefile b/usr.bin/svn/svnbench/Makefile new file mode 100644 index 000000000..8fb6ef8cd --- /dev/null +++ b/usr.bin/svn/svnbench/Makefile @@ -0,0 +1,50 @@ +# $FreeBSD$ + +MAN= + +.include "${.CURDIR}/../Makefile.inc" + +.PATH: ${SVNDIR}/svnbench + +PROG= svn${SVNLITE}bench + +SRCS= help-cmd.c notify.c null-blame-cmd.c null-export-cmd.c \ + null-info-cmd.c null-list-cmd.c null-log-cmd.c svnbench.c util.c + +CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ + -I${.CURDIR}/../lib/libapr \ + -I${APR}/include/arch/unix \ + -I${APR}/include \ + -I${.CURDIR}/../lib/libapr_util \ + -I${APRU}/include/private \ + -I${APRU}/include + +LDADD= -L${LIBSVN_CLIENTDIR} -lsvn_client \ + -L${LIBSVN_WCDIR} -lsvn_wc \ + -L${LIBSVN_RADIR} -lsvn_ra \ + -L${LIBSVN_RA_LOCALDIR} -lsvn_ra_local \ + -L${LIBSVN_RA_SVNDIR} -lsvn_ra_svn \ + -L${LIBSVN_RA_SERFDIR} -lsvn_ra_serf \ + -L${LIBSVN_REPOSDIR} -lsvn_repos \ + -L${LIBSVN_FSDIR} -lsvn_fs \ + -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ + -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ + -L${LIBSVN_DELTADIR} -lsvn_delta \ + -L${LIBSVN_DIFFDIR} -lsvn_diff \ + -L${LIBSVN_SUBRDIR} -lsvn_subr \ + -L${LIBSERFDIR} -lserf \ + -L${LIBAPR_UTILDIR} -lapr-util \ + -lbsdxml \ + -L${LIBAPRDIR} -lapr \ + -L${LIBSQLITEDIR} -lsqlite3 \ + -lz -lmagic -lcrypto -lssl -lpthread + +DPADD= ${LIBSVN_CLIENT} ${LIBSVN_WC} ${LIBSVN_RA} ${LIBSVN_RA_LOCAL} \ + ${LIBSVN_RA_SVN} ${LIBSVN_RA_SERF} ${LIBSVN_REPOS} \ + ${LIBSVN_FS} ${LIBSVN_FS_FS} ${LIBSVN_FS_UTIL} ${LIBSVN_DELTA} \ + ${LIBSVN_DIFF} ${LIBSVN_SUBR} ${LIBSERF} ${LIBAPR_UTIL} \ + ${LIBBSDXML} ${LIBAPR} ${LIBSQLITE} ${LIBZ} ${LIBMAGIC} \ + ${LIBCRYPTO} ${LIBSSL} ${LIBPTHREAD} + +.include diff --git a/usr.bin/svn/svndumpfilter/Makefile b/usr.bin/svn/svndumpfilter/Makefile index 80d258429..17968a155 100644 --- a/usr.bin/svn/svndumpfilter/Makefile +++ b/usr.bin/svn/svndumpfilter/Makefile @@ -21,6 +21,7 @@ CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ LDADD= -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_SUBRDIR} -lsvn_subr \ diff --git a/usr.bin/svn/svnfsfs/Makefile b/usr.bin/svn/svnfsfs/Makefile new file mode 100644 index 000000000..3f056dce7 --- /dev/null +++ b/usr.bin/svn/svnfsfs/Makefile @@ -0,0 +1,38 @@ +# $FreeBSD$ + +MAN= + +.include "${.CURDIR}/../Makefile.inc" + +.PATH: ${SVNDIR}/svnfsfs + +PROG= svn${SVNLITE}fsfs + +SRCS= dump-index-cmd.c load-index-cmd.c stats-cmd.c svnfsfs.c + +CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ + -I${.CURDIR}/../lib/libapr \ + -I${APR}/include/arch/unix \ + -I${APR}/include \ + -I${.CURDIR}/../lib/libapr_util \ + -I${APRU}/include/private \ + -I${APRU}/include + +LDADD= -L${LIBSVN_REPOSDIR} -lsvn_repos \ + -L${LIBSVN_FSDIR} -lsvn_fs \ + -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ + -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ + -L${LIBSVN_DELTADIR} -lsvn_delta \ + -L${LIBSVN_SUBRDIR} -lsvn_subr \ + -L${LIBAPR_UTILDIR} -lapr-util \ + -lbsdxml \ + -L${LIBAPRDIR} -lapr \ + -L${LIBSQLITEDIR} -lsqlite3 \ + -lz -lpthread + +DPADD= ${LIBSVN_REPOS} ${LIBSVN_FS} ${LIBSVN_FS_FS} ${LIBSVN_FS_UTIL} \ + ${LIBSVN_DELTA} ${LIBSVN_SUBR} ${LIBAPR_UTIL} \ + ${LIBBSDXML} ${LIBAPR} ${LIBSQLITE} ${LIBZ} ${LIBPTHREAD} + +.include diff --git a/usr.bin/svn/svnlook/Makefile b/usr.bin/svn/svnlook/Makefile index 5307e3743..60d4a2e2f 100644 --- a/usr.bin/svn/svnlook/Makefile +++ b/usr.bin/svn/svnlook/Makefile @@ -21,6 +21,7 @@ CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ LDADD= -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_DIFFDIR} -lsvn_diff \ diff --git a/usr.bin/svn/svnmucc/Makefile b/usr.bin/svn/svnmucc/Makefile index 23a98e30f..5beb30882 100644 --- a/usr.bin/svn/svnmucc/Makefile +++ b/usr.bin/svn/svnmucc/Makefile @@ -19,6 +19,7 @@ CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ -I${APRU}/include LDADD= -L${LIBSVN_CLIENTDIR} -lsvn_client \ + -L${LIBSVN_WCDIR} -lsvn_wc \ -L${LIBSVN_RADIR} -lsvn_ra \ -L${LIBSVN_RA_LOCALDIR} -lsvn_ra_local \ -L${LIBSVN_RA_SVNDIR} -lsvn_ra_svn \ @@ -26,8 +27,10 @@ LDADD= -L${LIBSVN_CLIENTDIR} -lsvn_client \ -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ + -L${LIBSVN_DIFFDIR} -lsvn_diff \ -L${LIBSVN_SUBRDIR} -lsvn_subr \ -L${LIBSERFDIR} -lserf \ -L${LIBAPR_UTILDIR} -lapr-util \ diff --git a/usr.bin/svn/svnrdump/Makefile b/usr.bin/svn/svnrdump/Makefile index afeae003c..b6e91ce94 100644 --- a/usr.bin/svn/svnrdump/Makefile +++ b/usr.bin/svn/svnrdump/Makefile @@ -27,6 +27,7 @@ LDADD= -L${LIBSVN_CLIENTDIR} -lsvn_client \ -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_DIFFDIR} -lsvn_diff \ diff --git a/usr.bin/svn/svnserve/Makefile b/usr.bin/svn/svnserve/Makefile index f1fa314e1..4133c271a 100644 --- a/usr.bin/svn/svnserve/Makefile +++ b/usr.bin/svn/svnserve/Makefile @@ -8,7 +8,7 @@ MAN= PROG= svn${SVNLITE}serve -SRCS= cyrus_auth.c log-escape.c serve.c svnserve.c winservice.c +SRCS= cyrus_auth.c log-escape.c logger.c serve.c svnserve.c winservice.c CFLAGS+=-I${SVNDIR}/include -I${SVNDIR} -I${.CURDIR}/.. \ -I${.CURDIR}/../lib/libapr \ @@ -25,6 +25,7 @@ LDADD= -L${LIBSVN_RADIR} -lsvn_ra \ -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_SUBRDIR} -lsvn_subr \ diff --git a/usr.bin/svn/svnsync/Makefile b/usr.bin/svn/svnsync/Makefile index a8cd3802a..f6be87632 100644 --- a/usr.bin/svn/svnsync/Makefile +++ b/usr.bin/svn/svnsync/Makefile @@ -25,6 +25,7 @@ LDADD= -L${LIBSVN_RADIR} -lsvn_ra \ -L${LIBSVN_REPOSDIR} -lsvn_repos \ -L${LIBSVN_FSDIR} -lsvn_fs \ -L${LIBSVN_FS_FSDIR} -lsvn_fs_fs \ + -L${LIBSVN_FS_XDIR} -lsvn_fs_x \ -L${LIBSVN_FS_UTILDIR} -lsvn_fs_util \ -L${LIBSVN_DELTADIR} -lsvn_delta \ -L${LIBSVN_SUBRDIR} -lsvn_subr \ -- 2.45.0